chore: optimize startup logic

This commit is contained in:
ACh Sulfate
2024-07-23 13:45:50 +08:00
parent 9c92d1ef50
commit 8b5c6414c2
7 changed files with 78 additions and 34 deletions

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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());

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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());

View File

@@ -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);