chore: optimize startup logic
This commit is contained in:
@@ -66,9 +66,11 @@ public class StartupAgent {
|
||||
// I don't know... What happened?
|
||||
return;
|
||||
}
|
||||
System.setProperty(StartupAgent.class.getName(), "true");
|
||||
StartupInfo.setModulePath(modulePath);
|
||||
StartupInfo.setLoaderInfo(loaderInfo);
|
||||
StartupInfo.setHookBridge(hookBridge);
|
||||
StartupInfo.setInHostProcess(true);
|
||||
// bypass hidden api
|
||||
ensureHiddenApiAccess();
|
||||
// we want context
|
||||
|
||||
@@ -40,6 +40,8 @@ public class StartupInfo {
|
||||
|
||||
private static IHookBridge hookBridge;
|
||||
|
||||
private static Boolean inHostProcess = null;
|
||||
|
||||
@NonNull
|
||||
public static String getModulePath() {
|
||||
return modulePath;
|
||||
@@ -77,4 +79,18 @@ public class StartupInfo {
|
||||
StartupInfo.modulePath = modulePath;
|
||||
}
|
||||
|
||||
public static boolean isInHostProcess() {
|
||||
if (inHostProcess == null) {
|
||||
throw new IllegalStateException("Host process status is not initialized");
|
||||
}
|
||||
return inHostProcess;
|
||||
}
|
||||
|
||||
public static void setInHostProcess(boolean inHostProcess) {
|
||||
if (StartupInfo.inHostProcess != null) {
|
||||
throw new IllegalStateException("Host process status is already initialized");
|
||||
}
|
||||
StartupInfo.inHostProcess = inHostProcess;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class StartupRoutine {
|
||||
InitFields.ezXClassLoader = ctx.getClassLoader();
|
||||
// resource injection is done somewhere else, do not init it here
|
||||
Log.INSTANCE.getCurrentLogger().setLogTag("QAuxv");
|
||||
Natives.load(ctx);
|
||||
Natives.initialize(ctx);
|
||||
overrideLSPatchModifiedVersionCodeIfNecessary(ctx);
|
||||
NativeCoreBridge.initNativeCore(ctx.getPackageName(), Build.VERSION.SDK_INT,
|
||||
HostInfo.getHostInfo().getVersionName(), HostInfo.getHostInfo().getVersionCode());
|
||||
|
||||
@@ -22,36 +22,34 @@
|
||||
package io.github.qauxv.util;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.qauxv.loader.hookapi.IHookBridge;
|
||||
import io.github.qauxv.loader.hookapi.ILoaderInfo;
|
||||
import io.github.qauxv.poststartup.StartupInfo;
|
||||
|
||||
public class LspObfuscationHelper {
|
||||
public class LoaderExtensionHelper {
|
||||
|
||||
public static final String CMD_GET_XPOSED_BRIDGE_CLASS = "GetXposedBridgeClass";
|
||||
private static String sProbeLsposedNativeApiClassName = "Lorg/lsposed/lspd/nativebridge/NativeAPI;";
|
||||
|
||||
private LspObfuscationHelper() {
|
||||
private LoaderExtensionHelper() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Class<?> getXposedBridgeClass() {
|
||||
IHookBridge hookBridge = StartupInfo.getHookBridge();
|
||||
if (hookBridge == null) {
|
||||
return null;
|
||||
}
|
||||
return (Class<?>) hookBridge.queryExtension(CMD_GET_XPOSED_BRIDGE_CLASS);
|
||||
ILoaderInfo loaderInfo = StartupInfo.getLoaderInfo();
|
||||
return (Class<?>) loaderInfo.queryExtension(CMD_GET_XPOSED_BRIDGE_CLASS);
|
||||
}
|
||||
|
||||
public static String getObfuscatedLsposedNativeApiClassName() {
|
||||
return sProbeLsposedNativeApiClassName.replace('.', '/').substring(1, sProbeLsposedNativeApiClassName.length() - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getXposedBridgeClassName() {
|
||||
Class<?> xposedBridgeClass = getXposedBridgeClass();
|
||||
if (xposedBridgeClass != null) {
|
||||
return xposedBridgeClass.getName();
|
||||
} else {
|
||||
return "de.robv.android.xposed.XposedBridge";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@ import android.os.Build.VERSION;
|
||||
import android.os.Process;
|
||||
import android.system.Os;
|
||||
import android.system.StructUtsname;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import io.github.qauxv.BuildConfig;
|
||||
import io.github.qauxv.loader.hookapi.IHookBridge;
|
||||
import io.github.qauxv.poststartup.StartupInfo;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -95,6 +97,8 @@ public class Natives {
|
||||
public static final int O_SYNC = (0x100000 | O_DSYNC);
|
||||
public static final int O_PATH = 0x200000;
|
||||
|
||||
private static volatile boolean sInitialized = false;
|
||||
|
||||
private Natives() {
|
||||
throw new AssertionError("No instance for you!");
|
||||
}
|
||||
@@ -197,41 +201,56 @@ public class Natives {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class<?> xp = Class.forName(LspObfuscationHelper.getXposedBridgeClassName());
|
||||
try {
|
||||
xp.getClassLoader()
|
||||
.loadClass(LspObfuscationHelper.getObfuscatedLsposedNativeApiClassName())
|
||||
ClassLoader apiClassLoader = IHookBridge.class.getClassLoader().getParent();
|
||||
if (apiClassLoader != null) {
|
||||
apiClassLoader.loadClass(LoaderExtensionHelper.getObfuscatedLsposedNativeApiClassName())
|
||||
.getMethod("recordNativeEntrypoint", String.class)
|
||||
.invoke(null, soTailingName);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// not LSPosed, ignore
|
||||
} catch (NoSuchMethodException | IllegalArgumentException
|
||||
| InvocationTargetException | IllegalAccessException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// not in host process, ignore
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// not LSPosed, ignore
|
||||
} catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(Context ctx) throws LinkageError {
|
||||
/**
|
||||
* Load native library without initializing. This method is useful when you need to call native methods before Application.attachBaseContext() is called.
|
||||
* <p>
|
||||
* Note that typically you should call {@link #initialize(Context)} instead.
|
||||
*
|
||||
* @param filesDir Files directory, can be obtained from {@link Context#getFilesDir()}
|
||||
*/
|
||||
public static void loadNativeWithoutInitialization(@NonNull File filesDir) {
|
||||
try {
|
||||
getpagesize();
|
||||
return;
|
||||
} catch (UnsatisfiedLinkError ignored) {
|
||||
}
|
||||
try {
|
||||
Class.forName(LspObfuscationHelper.getXposedBridgeClassName());
|
||||
if (StartupInfo.isInHostProcess()) {
|
||||
// in host process
|
||||
List<String> abis = getAbiForLibrary();
|
||||
String modulePath = StartupInfo.getModulePath();
|
||||
loadNativeLibraryInHost(ctx, modulePath, abis);
|
||||
} catch (ClassNotFoundException e) {
|
||||
loadNativeLibraryInHost(filesDir, modulePath, abis);
|
||||
} else {
|
||||
// not in host process, ignore
|
||||
System.loadLibrary("qauxv");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load native library and initialize MMKV
|
||||
* @param ctx Application context
|
||||
* @throws LinkageError if failed to load native library
|
||||
*/
|
||||
public static void initialize(@NonNull Context ctx) throws LinkageError {
|
||||
if(sInitialized){
|
||||
return;
|
||||
}
|
||||
File filesDir = ctx.getFilesDir();
|
||||
loadNativeWithoutInitialization(filesDir);
|
||||
getpagesize();
|
||||
File mmkvDir = new File(ctx.getFilesDir(), "qa_mmkv");
|
||||
File mmkvDir = new File(filesDir, "qa_mmkv");
|
||||
if (!mmkvDir.exists()) {
|
||||
mmkvDir.mkdirs();
|
||||
}
|
||||
@@ -245,10 +264,11 @@ public class Natives {
|
||||
});
|
||||
MMKV.mmkvWithID("global_config", MMKV.MULTI_PROCESS_MODE);
|
||||
MMKV.mmkvWithID("global_cache", MMKV.MULTI_PROCESS_MODE);
|
||||
sInitialized = true;
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||
private static void loadNativeLibraryInHost(Context ctx, String modulePath, List<String> abis) throws UnsatisfiedLinkError {
|
||||
private static void loadNativeLibraryInHost(@NonNull File filesDir, String modulePath, List<String> abis) throws UnsatisfiedLinkError {
|
||||
Iterator<String> it = abis.iterator();
|
||||
if (modulePath != null && modulePath.length() > 0 && new File(modulePath).exists()) {
|
||||
// try direct memory map
|
||||
@@ -264,7 +284,7 @@ public class Natives {
|
||||
}
|
||||
}
|
||||
// direct memory map load failed, extract and dlopen
|
||||
File libname = extractNativeLibrary(ctx, "qauxv", abis.get(0));
|
||||
File libname = extractNativeLibrary(filesDir, "qauxv", abis.get(0));
|
||||
registerNativeLibEntry(libname.getName());
|
||||
try {
|
||||
System.load(libname.getAbsolutePath());
|
||||
@@ -301,9 +321,9 @@ public class Natives {
|
||||
*
|
||||
* @param libraryName library name without "lib" or ".so", eg. "qauxv", "mmkv"
|
||||
*/
|
||||
static File extractNativeLibrary(Context ctx, String libraryName, String abi) throws IOError {
|
||||
static File extractNativeLibrary(@NonNull File filesDir, String libraryName, String abi) throws IOError {
|
||||
String soName = "lib" + libraryName + ".so." + BuildConfig.VERSION_CODE + "." + abi;
|
||||
File dir = new File(ctx.getFilesDir(), "qa_dyn_lib");
|
||||
File dir = new File(filesDir, "qa_dyn_lib");
|
||||
if (!dir.isDirectory()) {
|
||||
if (dir.isFile()) {
|
||||
dir.delete();
|
||||
|
||||
@@ -34,7 +34,7 @@ import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import io.github.qauxv.BuildConfig;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.util.LspObfuscationHelper;
|
||||
import io.github.qauxv.util.LoaderExtensionHelper;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
import io.github.qauxv.util.NonUiThread;
|
||||
import java.io.File;
|
||||
@@ -145,7 +145,7 @@ public class HookStatus {
|
||||
}
|
||||
|
||||
private static void initHookStatusImplInHostProcess() throws LinkageError {
|
||||
Class<?> xposedClass = LspObfuscationHelper.getXposedBridgeClass();
|
||||
Class<?> xposedClass = LoaderExtensionHelper.getXposedBridgeClass();
|
||||
boolean dexObfsEnabled = false;
|
||||
if (xposedClass != null) {
|
||||
dexObfsEnabled = !"de.robv.android.xposed.XposedBridge".equals(xposedClass.getName());
|
||||
|
||||
@@ -25,6 +25,7 @@ package io.github.qauxv.util.hookstatus;
|
||||
import android.app.Application;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.qauxv.BuildConfig;
|
||||
import io.github.qauxv.core.NativeCoreBridge;
|
||||
import io.github.qauxv.loader.hookapi.ILoaderInfo;
|
||||
@@ -38,10 +39,11 @@ public class ModuleAppImpl extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
StartupInfo.setInHostProcess(false);
|
||||
// init host info, even if we are not in the host app
|
||||
HostInfo.init(this);
|
||||
// load native library
|
||||
Natives.load(this);
|
||||
Natives.initialize(this);
|
||||
// bypass hidden api check for current process
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
HiddenApiBypass.setHiddenApiExemptions("L");
|
||||
@@ -88,6 +90,12 @@ public class ModuleAppImpl extends Application {
|
||||
public void log(@NonNull Throwable tr) {
|
||||
android.util.Log.e("QAuxv", tr.toString(), tr);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object queryExtension(@NonNull String key, @Nullable Object... args) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
StartupInfo.setModulePath(apkPath);
|
||||
StartupInfo.setLoaderInfo(loaderInfo);
|
||||
|
||||
Reference in New Issue
Block a user