Refactor Java code
This commit is contained in:
parent
c362449e33
commit
c38affbf3a
|
|
@ -23,6 +23,7 @@ dependencies {
|
||||||
compileOnly project(':hiddenapi-stubs')
|
compileOnly project(':hiddenapi-stubs')
|
||||||
implementation project(':xposed-bridge')
|
implementation project(':xposed-bridge')
|
||||||
compileOnly project(':dexmaker')
|
compileOnly project(':dexmaker')
|
||||||
|
api "androidx.annotation:annotation:1.1.0-rc01"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.elderdrivers.riru.edxp.whale._hooker;
|
package com.elderdrivers.riru.edxp._hooker.impl;
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
import android.app.LoadedApk;
|
import android.app.LoadedApk;
|
||||||
|
|
@ -6,14 +6,14 @@ import android.content.ComponentName;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.CompatibilityInfo;
|
import android.content.res.CompatibilityInfo;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.SliceProviderFix;
|
import com.elderdrivers.riru.edxp.hooker.SliceProviderFix;
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedInstallerHooker;
|
import com.elderdrivers.riru.edxp.hooker.XposedInstallerHooker;
|
||||||
|
import com.elderdrivers.riru.edxp.util.Hookers;
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
@ -25,26 +25,23 @@ import static com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker.BLACK_LIST
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
||||||
|
|
||||||
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
||||||
public class HandleBindAppHooker implements KeepMembers {
|
public class HandleBindApp extends XC_MethodHook {
|
||||||
|
|
||||||
public static String className = "android.app.ActivityThread";
|
@Override
|
||||||
public static String methodName = "handleBindApplication";
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
public static String methodSig = "(Landroid/app/ActivityThread$AppBindData;)V";
|
|
||||||
|
|
||||||
public static void hook(Object thiz, Object bindData) {
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
||||||
backup(thiz, bindData);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Router.logD("ActivityThread#handleBindApplication() starts");
|
Hookers.logD("ActivityThread#handleBindApplication() starts");
|
||||||
ActivityThread activityThread = (ActivityThread) thiz;
|
ActivityThread activityThread = (ActivityThread) param.thisObject;
|
||||||
|
Object bindData = param.args[0];
|
||||||
ApplicationInfo appInfo = (ApplicationInfo) XposedHelpers.getObjectField(bindData, "appInfo");
|
ApplicationInfo appInfo = (ApplicationInfo) XposedHelpers.getObjectField(bindData, "appInfo");
|
||||||
// save app process name here for later use
|
// save app process name here for later use
|
||||||
Main.appProcessName = (String) XposedHelpers.getObjectField(bindData, "processName");
|
ConfigManager.appProcessName = (String) XposedHelpers.getObjectField(bindData, "processName");
|
||||||
String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
|
String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
|
||||||
Utils.logD("processName=" + Main.appProcessName +
|
Utils.logD("processName=" + ConfigManager.appProcessName +
|
||||||
", packageName=" + reportedPackageName + ", appDataDir=" + Main.appDataDir);
|
", packageName=" + reportedPackageName + ", appDataDir=" + ConfigManager.appDataDir);
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks(reportedPackageName)) {
|
if (XposedBlackListHooker.shouldDisableHooks(reportedPackageName)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -52,7 +49,7 @@ public class HandleBindAppHooker implements KeepMembers {
|
||||||
|
|
||||||
ComponentName instrumentationName = (ComponentName) XposedHelpers.getObjectField(bindData, "instrumentationName");
|
ComponentName instrumentationName = (ComponentName) XposedHelpers.getObjectField(bindData, "instrumentationName");
|
||||||
if (instrumentationName != null) {
|
if (instrumentationName != null) {
|
||||||
Router.logD("Instrumentation detected, disabling framework for");
|
Hookers.logD("Instrumentation detected, disabling framework for");
|
||||||
XposedBridge.disableHooks = true;
|
XposedBridge.disableHooks = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -85,12 +82,7 @@ public class HandleBindAppHooker implements KeepMembers {
|
||||||
SliceProviderFix.hook();
|
SliceProviderFix.hook();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Router.logE("error when hooking bindApp", t);
|
Hookers.logE("error when hooking bindApp", t);
|
||||||
} finally {
|
|
||||||
backup(thiz, bindData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static void backup(Object thiz, Object bindData) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.impl;
|
||||||
|
|
||||||
|
import android.app.AndroidAppHelper;
|
||||||
|
import android.app.LoadedApk;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
||||||
|
import com.elderdrivers.riru.edxp.util.Hookers;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
|
|
||||||
|
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
||||||
|
|
||||||
|
// when a package is loaded for an existing process, trigger the callbacks as well
|
||||||
|
// ed: remove resources related hooking
|
||||||
|
public class LoadedApkCstr extends XC_MethodHook {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hookers.logD("LoadedApk#<init> starts");
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoadedApk loadedApk = (LoadedApk) param.thisObject;
|
||||||
|
String packageName = loadedApk.getPackageName();
|
||||||
|
Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir");
|
||||||
|
Hookers.logD("LoadedApk#<init> ends: " + mAppDir);
|
||||||
|
|
||||||
|
if (XposedBlackListHooker.shouldDisableHooks(packageName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageName.equals("android")) {
|
||||||
|
Hookers.logD("LoadedApk#<init> is android, skip: " + mAppDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mIncludeCode checking should go ahead of loadedPackagesInProcess added checking
|
||||||
|
if (!XposedHelpers.getBooleanField(loadedApk, "mIncludeCode")) {
|
||||||
|
Hookers.logD("LoadedApk#<init> mIncludeCode == false: " + mAppDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!XposedInit.loadedPackagesInProcess.add(packageName)) {
|
||||||
|
Hookers.logD("LoadedApk#<init> has been loaded before, skip: " + mAppDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnePlus magic...
|
||||||
|
if (Log.getStackTraceString(new Throwable()).
|
||||||
|
contains("android.app.ActivityThread$ApplicationThread.schedulePreload")) {
|
||||||
|
Hookers.logD("LoadedApk#<init> maybe oneplus's custom opt, skip");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceParentClassLoader(loadedApk.getClassLoader());
|
||||||
|
|
||||||
|
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
||||||
|
lpparam.packageName = packageName;
|
||||||
|
lpparam.processName = AndroidAppHelper.currentProcessName();
|
||||||
|
lpparam.classLoader = loadedApk.getClassLoader();
|
||||||
|
lpparam.appInfo = loadedApk.getApplicationInfo();
|
||||||
|
lpparam.isFirstApplication = false;
|
||||||
|
XC_LoadPackage.callAll(lpparam);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Hookers.logE("error when hooking LoadedApk.<init>", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa._hooker;
|
package com.elderdrivers.riru.edxp._hooker.impl;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.edxp.core.Main;
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
import com.elderdrivers.riru.edxp.util.Hookers;
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
|
import dalvik.system.BaseDexClassLoader;
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On OnePlus stock roms (Android Pie), {@link dalvik.system.BaseDexClassLoader#findClass(String)}
|
* On OnePlus stock roms (Android Pie), {@link BaseDexClassLoader#findClass(String)}
|
||||||
* will open /dev/binder to communicate with PackageManagerService to check whether
|
* will open /dev/binder to communicate with PackageManagerService to check whether
|
||||||
* current package name inCompatConfigList, which is an OnePlus OEM feature enabled only when
|
* current package name inCompatConfigList, which is an OnePlus OEM feature enabled only when
|
||||||
* system prop "persist.sys.oem.region" set to "CN".(detail of related source code:
|
* system prop "persist.sys.oem.region" set to "CN".(detail of related source code:
|
||||||
|
|
@ -21,21 +22,15 @@ import de.robv.android.xposed.XposedBridge;
|
||||||
* open of /dev/binder and we haven't found side effects yet.
|
* open of /dev/binder and we haven't found side effects yet.
|
||||||
* Other roms might share the same problems but not reported too.
|
* Other roms might share the same problems but not reported too.
|
||||||
*/
|
*/
|
||||||
public class OnePlusWorkAroundHooker implements KeepMembers {
|
public class OneplusWorkaround extends XC_MethodHook {
|
||||||
|
|
||||||
public static String className = "dalvik.system.BaseDexClassLoader";
|
@Override
|
||||||
public static String methodName = "inCompatConfigList";
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
public static String methodSig = "(ILjava/lang/String;)Z";
|
if (XposedBridge.disableHooks || Main.getEdxpImpl().getRouter().isForkCompleted()) {
|
||||||
|
return;
|
||||||
public static boolean hook(int type, String packageName) {
|
|
||||||
if (XposedBridge.disableHooks || Router.forkCompleted) {
|
|
||||||
return backup(type, packageName);
|
|
||||||
}
|
}
|
||||||
Router.logD("BaseDexClassLoader#inCompatConfigList() starts");
|
Hookers.logD("BaseDexClassLoader#inCompatConfigList() starts");
|
||||||
return false;
|
param.setResult(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean backup(int type, String packageName) {
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa._hooker;
|
package com.elderdrivers.riru.edxp._hooker.impl;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.edxp.util.Hookers;
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XC_MethodReplacement;
|
import de.robv.android.xposed.XC_MethodReplacement;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
|
@ -15,15 +15,11 @@ import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClas
|
||||||
import static com.elderdrivers.riru.edxp.util.Utils.logD;
|
import static com.elderdrivers.riru.edxp.util.Utils.logD;
|
||||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
||||||
|
|
||||||
public class StartBootstrapServicesHooker implements KeepMembers {
|
public class StartBootstrapServices extends XC_MethodHook {
|
||||||
public static String className = "com.android.server.SystemServer";
|
|
||||||
public static String methodName = "startBootstrapServices";
|
|
||||||
public static String methodSig = "()V";
|
|
||||||
|
|
||||||
public static void hook(Object systemServer) {
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
if (XposedBridge.disableHooks) {
|
if (XposedBridge.disableHooks) {
|
||||||
backup(systemServer);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,35 +28,33 @@ public class StartBootstrapServicesHooker implements KeepMembers {
|
||||||
try {
|
try {
|
||||||
XposedInit.loadedPackagesInProcess.add("android");
|
XposedInit.loadedPackagesInProcess.add("android");
|
||||||
|
|
||||||
replaceParentClassLoader(SystemMainHooker.systemServerCL);
|
replaceParentClassLoader(SystemMain.systemServerCL);
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
||||||
lpparam.packageName = "android";
|
lpparam.packageName = "android";
|
||||||
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
|
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
|
||||||
lpparam.classLoader = SystemMainHooker.systemServerCL;
|
lpparam.classLoader = SystemMain.systemServerCL;
|
||||||
lpparam.appInfo = null;
|
lpparam.appInfo = null;
|
||||||
lpparam.isFirstApplication = true;
|
lpparam.isFirstApplication = true;
|
||||||
XC_LoadPackage.callAll(lpparam);
|
XC_LoadPackage.callAll(lpparam);
|
||||||
|
|
||||||
// Huawei
|
// Huawei
|
||||||
try {
|
try {
|
||||||
findAndHookMethod("com.android.server.pm.HwPackageManagerService", SystemMainHooker.systemServerCL, "isOdexMode", XC_MethodReplacement.returnConstant(false));
|
findAndHookMethod("com.android.server.pm.HwPackageManagerService",
|
||||||
|
SystemMain.systemServerCL, "isOdexMode",
|
||||||
|
XC_MethodReplacement.returnConstant(false));
|
||||||
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
|
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
|
||||||
findAndHookMethod(className, SystemMainHooker.systemServerCL, "dexEntryExists", String.class, XC_MethodReplacement.returnConstant(true));
|
findAndHookMethod(className, SystemMain.systemServerCL,
|
||||||
|
"dexEntryExists", String.class,
|
||||||
|
XC_MethodReplacement.returnConstant(true));
|
||||||
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Router.logE("error when hooking startBootstrapServices", t);
|
Hookers.logE("error when hooking startBootstrapServices", t);
|
||||||
} finally {
|
|
||||||
backup(systemServer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void backup(Object systemServer) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.impl;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.core.Main;
|
||||||
|
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
||||||
|
import com.elderdrivers.riru.edxp.util.Hookers;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
// system_server initialization
|
||||||
|
// ed: only support sdk >= 21 for now
|
||||||
|
public class SystemMain extends XC_MethodHook {
|
||||||
|
|
||||||
|
public static volatile ClassLoader systemServerCL;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
if (XposedBridge.disableHooks) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Hookers.logD("ActivityThread#systemMain() starts");
|
||||||
|
try {
|
||||||
|
// get system_server classLoader
|
||||||
|
systemServerCL = Thread.currentThread().getContextClassLoader();
|
||||||
|
// deopt methods in SYSTEMSERVERCLASSPATH
|
||||||
|
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
||||||
|
Main.getEdxpImpl().getRouter().startSystemServerHook();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Hookers.logE("error when hooking systemMain", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.HandleBindApp;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
public class HandleBindAppHooker implements KeepMembers {
|
||||||
|
|
||||||
|
public static String className = "android.app.ActivityThread";
|
||||||
|
public static String methodName = "handleBindApplication";
|
||||||
|
public static String methodSig = "(Landroid/app/ActivityThread$AppBindData;)V";
|
||||||
|
|
||||||
|
public static void hook(final Object thiz, final Object bindData) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new HandleBindApp();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = thiz;
|
||||||
|
param.args = new Object[]{bindData};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
backup(thiz, bindData);
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backup(Object thiz, Object bindData) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.res.CompatibilityInfo;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.LoadedApkCstr;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
public class LoadedApkConstructorHooker implements KeepMembers {
|
||||||
|
public static String className = "android.app.LoadedApk";
|
||||||
|
public static String methodName = "<init>";
|
||||||
|
public static String methodSig = "(Landroid/app/ActivityThread;" +
|
||||||
|
"Landroid/content/pm/ApplicationInfo;" +
|
||||||
|
"Landroid/content/res/CompatibilityInfo;" +
|
||||||
|
"Ljava/lang/ClassLoader;ZZZ)V";
|
||||||
|
|
||||||
|
public static void hook(Object thiz, ActivityThread activityThread,
|
||||||
|
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
||||||
|
ClassLoader baseLoader, boolean securityViolation,
|
||||||
|
boolean includeCode, boolean registerPackage) throws Throwable {
|
||||||
|
|
||||||
|
final XC_MethodHook methodHook = new LoadedApkCstr();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = thiz;
|
||||||
|
param.args = new Object[]{activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
||||||
|
includeCode, registerPackage};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
||||||
|
includeCode, registerPackage);
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backup(Object thiz, ActivityThread activityThread,
|
||||||
|
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
||||||
|
ClassLoader baseLoader, boolean securityViolation,
|
||||||
|
boolean includeCode, boolean registerPackage) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
|
||||||
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
public class OnePlusWorkAroundHooker implements KeepMembers {
|
||||||
|
|
||||||
|
static {
|
||||||
|
HookMain.addHookItemWhiteList(OnePlusWorkAroundHooker.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String className = "dalvik.system.BaseDexClassLoader";
|
||||||
|
public static String methodName = "inCompatConfigList";
|
||||||
|
public static String methodSig = "(ILjava/lang/String;)Z";
|
||||||
|
|
||||||
|
public static boolean hook(int type, String packageName) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new OneplusWorkaround();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = null;
|
||||||
|
param.args = new Object[]{type, packageName};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
param.setResult(backup(type, packageName));
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
return (boolean) param.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean backup(int type, String packageName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.StartBootstrapServices;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
public class StartBootstrapServicesHooker implements KeepMembers {
|
||||||
|
public static String className = "com.android.server.SystemServer";
|
||||||
|
public static String methodName = "startBootstrapServices";
|
||||||
|
public static String methodSig = "()V";
|
||||||
|
|
||||||
|
public static void hook(Object systemServer) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new StartBootstrapServices();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = systemServer;
|
||||||
|
param.args = new Object[]{};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
backup(systemServer);
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backup(Object systemServer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
public class SystemMainHooker implements KeepMembers {
|
||||||
|
|
||||||
|
public static String className = "android.app.ActivityThread";
|
||||||
|
public static String methodName = "systemMain";
|
||||||
|
public static String methodSig = "()Landroid/app/ActivityThread;";
|
||||||
|
|
||||||
|
public static ClassLoader systemServerCL;
|
||||||
|
|
||||||
|
public static ActivityThread hook() throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new OneplusWorkaround();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = null;
|
||||||
|
param.args = new Object[]{};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
param.setResult(backup());
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
return (ActivityThread) param.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ActivityThread backup() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,27 +1,19 @@
|
||||||
package com.elderdrivers.riru.edxp.config;
|
package com.elderdrivers.riru.edxp.config;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.SELinuxHelper;
|
import de.robv.android.xposed.SELinuxHelper;
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_DATA_BASE_DIR;
|
import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_DATA_BASE_DIR;
|
||||||
import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_PACKAGE_NAME;
|
|
||||||
|
|
||||||
public class ConfigManager {
|
public class ConfigManager {
|
||||||
|
|
||||||
private static final String BLACK_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/blacklist/";
|
public static String appDataDir = "";
|
||||||
private static final String WHITE_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/whitelist/";
|
public static String niceName = "";
|
||||||
private static final String COMPAT_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/compatlist/";
|
public static String appProcessName = "";
|
||||||
private static final String USE_WHITE_LIST = INSTALLER_DATA_BASE_DIR + "conf/usewhitelist";
|
|
||||||
private static final String DYNAMIC_MODULES = INSTALLER_DATA_BASE_DIR + "conf/dynamicmodules";
|
|
||||||
private static final Set<String> WHITE_LIST = Collections.singleton(INSTALLER_PACKAGE_NAME);
|
|
||||||
private static final HashMap<String, Boolean> compatModeCache = new HashMap<>();
|
|
||||||
|
|
||||||
public static boolean shouldUseWhitelist() {
|
private static final String COMPAT_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/compatlist/";
|
||||||
return isFileExists(USE_WHITE_LIST);
|
private static final HashMap<String, Boolean> compatModeCache = new HashMap<>();
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean shouldUseCompatMode(String packageName) {
|
public static boolean shouldUseCompatMode(String packageName) {
|
||||||
Boolean result;
|
Boolean result;
|
||||||
|
|
@ -34,17 +26,6 @@ public class ConfigManager {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean shouldHook(String packageName) {
|
|
||||||
if (WHITE_LIST.contains(packageName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (shouldUseWhitelist()) {
|
|
||||||
return isFileExists(WHITE_LIST_PATH + packageName);
|
|
||||||
} else {
|
|
||||||
return !isFileExists(BLACK_LIST_PATH + packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isFileExists(String path) {
|
private static boolean isFileExists(String path) {
|
||||||
return SELinuxHelper.getAppDataFileService().checkFileExists(path);
|
return SELinuxHelper.getAppDataFileService().checkFileExists(path);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.elderdrivers.riru.edxp.core;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.BlackWhiteListProxy;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.NormalProxy;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.Router;
|
||||||
|
|
||||||
|
public abstract class BaseEdxpImpl implements EdxpImpl {
|
||||||
|
|
||||||
|
protected Proxy mBlackWhiteListProxy;
|
||||||
|
protected Proxy mNormalProxy;
|
||||||
|
protected Router mRouter;
|
||||||
|
|
||||||
|
protected boolean mInitialized = false;
|
||||||
|
|
||||||
|
protected void setInitialized() {
|
||||||
|
mInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return mInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Proxy getBlackWhiteListProxy() {
|
||||||
|
if (mBlackWhiteListProxy == null) {
|
||||||
|
mBlackWhiteListProxy = createBlackWhiteListProxy();
|
||||||
|
}
|
||||||
|
return mBlackWhiteListProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Proxy getNormalProxy() {
|
||||||
|
if (mNormalProxy == null) {
|
||||||
|
mNormalProxy = createNormalProxy();
|
||||||
|
}
|
||||||
|
return mNormalProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Router getRouter() {
|
||||||
|
if (mRouter == null) {
|
||||||
|
mRouter = createRouter();
|
||||||
|
}
|
||||||
|
return mRouter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Proxy createBlackWhiteListProxy() {
|
||||||
|
return new BlackWhiteListProxy(getRouter());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Proxy createNormalProxy() {
|
||||||
|
return new NormalProxy(getRouter());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Router createRouter();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.elderdrivers.riru.edxp.core;
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepAll;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.Router;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||||
|
|
||||||
|
public interface EdxpImpl extends KeepAll {
|
||||||
|
|
||||||
|
int NONE = 0;
|
||||||
|
int YAHFA = 1;
|
||||||
|
int SANDHOOK = 2;
|
||||||
|
int WHALE = 3;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Proxy getNormalProxy();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Proxy getBlackWhiteListProxy();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Router getRouter();
|
||||||
|
|
||||||
|
@Variant
|
||||||
|
int getVariant();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
boolean isInitialized();
|
||||||
|
|
||||||
|
@Retention(SOURCE)
|
||||||
|
@IntDef({NONE, YAHFA, SANDHOOK, WHALE})
|
||||||
|
@interface Variant {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,44 +1,30 @@
|
||||||
package com.elderdrivers.riru.edxp;
|
package com.elderdrivers.riru.edxp.core;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepAll;
|
import com.elderdrivers.riru.common.KeepAll;
|
||||||
|
import com.elderdrivers.riru.edxp.BuildConfig;
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.BuildConfig;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.core.HookMethodResolver;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.proxy.BlackWhiteListProxy;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.proxy.NormalProxy;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
import com.swift.sandhook.xposedcompat.XposedCompat;
|
|
||||||
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
|
|
||||||
|
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
public class Main implements KeepAll {
|
public class Main implements KeepAll {
|
||||||
|
|
||||||
public static String appDataDir = "";
|
|
||||||
public static String niceName = "";
|
|
||||||
public static String appProcessName = "";
|
|
||||||
private static String forkAndSpecializePramsStr = "";
|
private static String forkAndSpecializePramsStr = "";
|
||||||
private static String forkSystemServerPramsStr = "";
|
private static String forkSystemServerPramsStr = "";
|
||||||
|
|
||||||
static {
|
private static final AtomicReference<EdxpImpl> edxpImplRef = new AtomicReference<>(null);
|
||||||
Yahfa.init(Build.VERSION.SDK_INT);
|
|
||||||
HookMethodResolver.init();
|
|
||||||
Router.injectConfig();
|
|
||||||
InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName());
|
|
||||||
SandHookXposedBridge.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setAppDataDir(String appDataDir) {
|
static {
|
||||||
Main.appDataDir = appDataDir;
|
loadEdxpImpls();
|
||||||
XposedCompat.appDataDir = appDataDir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -50,6 +36,10 @@ public class Main implements KeepAll {
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||||
boolean startChildZygote, String instructionSet,
|
boolean startChildZygote, String instructionSet,
|
||||||
String appDataDir) {
|
String appDataDir) {
|
||||||
|
final EdxpImpl edxp = getEdxpImpl();
|
||||||
|
if (edxp == null || !edxp.isInitialized()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
forkAndSpecializePramsStr = String.format(
|
forkAndSpecializePramsStr = String.format(
|
||||||
"Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)",
|
"Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)",
|
||||||
|
|
@ -57,24 +47,29 @@ public class Main implements KeepAll {
|
||||||
mountExternal, seInfo, niceName, Arrays.toString(fdsToClose),
|
mountExternal, seInfo, niceName, Arrays.toString(fdsToClose),
|
||||||
Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir);
|
Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||||
BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
|
edxp.getBlackWhiteListProxy().forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
|
||||||
mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote,
|
mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote,
|
||||||
instructionSet, appDataDir);
|
instructionSet, appDataDir);
|
||||||
} else {
|
} else {
|
||||||
NormalProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
|
edxp.getNormalProxy().forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
|
||||||
seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet,
|
seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet,
|
||||||
appDataDir);
|
appDataDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||||
|
final EdxpImpl edxp = getEdxpImpl();
|
||||||
|
if (edxp == null || !edxp.isInitialized()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid());
|
Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid());
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||||
BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName);
|
edxp.getBlackWhiteListProxy().forkAndSpecializePost(pid, appDataDir, niceName);
|
||||||
} else {
|
} else {
|
||||||
NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName);
|
edxp.getNormalProxy().forkAndSpecializePost(pid, appDataDir, niceName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// in zygote process, res is child zygote pid
|
// in zygote process, res is child zygote pid
|
||||||
|
|
@ -84,27 +79,35 @@ public class Main implements KeepAll {
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
||||||
long permittedCapabilities, long effectiveCapabilities) {
|
long permittedCapabilities, long effectiveCapabilities) {
|
||||||
|
final EdxpImpl edxp = getEdxpImpl();
|
||||||
|
if (edxp == null || !edxp.isInitialized()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)",
|
forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)",
|
||||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
||||||
permittedCapabilities, effectiveCapabilities);
|
permittedCapabilities, effectiveCapabilities);
|
||||||
}
|
}
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||||
BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
edxp.getBlackWhiteListProxy().forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
||||||
permittedCapabilities, effectiveCapabilities);
|
permittedCapabilities, effectiveCapabilities);
|
||||||
} else {
|
} else {
|
||||||
NormalProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
edxp.getNormalProxy().forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
||||||
permittedCapabilities, effectiveCapabilities);
|
permittedCapabilities, effectiveCapabilities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
public static void forkSystemServerPost(int pid) {
|
||||||
|
final EdxpImpl edxp = getEdxpImpl();
|
||||||
|
if (edxp == null || !edxp.isInitialized()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid());
|
Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid());
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||||
BlackWhiteListProxy.forkSystemServerPost(pid);
|
edxp.getBlackWhiteListProxy().forkSystemServerPost(pid);
|
||||||
} else {
|
} else {
|
||||||
NormalProxy.forkSystemServerPost(pid);
|
edxp.getNormalProxy().forkSystemServerPost(pid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// in zygote process, res is child zygote pid
|
// in zygote process, res is child zygote pid
|
||||||
|
|
@ -112,4 +115,29 @@ public class Main implements KeepAll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static synchronized boolean setEdxpImpl(EdxpImpl edxp) {
|
||||||
|
return edxpImplRef.compareAndSet(null, edxp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized EdxpImpl getEdxpImpl() {
|
||||||
|
return edxpImplRef.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadEdxpImpls() {
|
||||||
|
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||||
|
public Void run() {
|
||||||
|
Iterator<EdxpImpl> iterator = ServiceLoader.load(
|
||||||
|
EdxpImpl.class, Main.class.getClassLoader()).iterator();
|
||||||
|
try {
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.elderdrivers.riru.edxp.core;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepAll;
|
||||||
|
|
||||||
|
public interface Proxy extends KeepAll {
|
||||||
|
|
||||||
|
boolean init();
|
||||||
|
|
||||||
|
void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
||||||
|
int[][] rlimits, int mountExternal, String seInfo,
|
||||||
|
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||||
|
boolean startChildZygote, String instructionSet,
|
||||||
|
String appDataDir);
|
||||||
|
|
||||||
|
void forkAndSpecializePost(int pid, String appDataDir, String niceName);
|
||||||
|
|
||||||
|
void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
||||||
|
long permittedCapabilities, long effectiveCapabilities);
|
||||||
|
|
||||||
|
void forkSystemServerPost(int pid);
|
||||||
|
}
|
||||||
|
|
@ -1,23 +1,26 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.core;
|
package com.elderdrivers.riru.edxp.core.yahfa;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.art.Heap;
|
import com.elderdrivers.riru.edxp.art.Heap;
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
|
||||||
public class HookMain {
|
public class HookMain {
|
||||||
|
|
||||||
private static Set<String> hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName());
|
private static final Set<String> hookItemWhiteList = new HashSet<String>();
|
||||||
|
|
||||||
|
public static void addHookItemWhiteList(String className) {
|
||||||
|
hookItemWhiteList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) {
|
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.core;
|
package com.elderdrivers.riru.edxp.core.yahfa;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
|
|
||||||
|
|
@ -113,12 +112,12 @@ public class HookMethodResolver {
|
||||||
Object artMethod = artMethodField.get(backup);
|
Object artMethod = artMethodField.get(backup);
|
||||||
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
|
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
||||||
((Object[])resolvedMethods)[dexMethodIndex] = artMethod;
|
((Object[]) resolvedMethods)[dexMethodIndex] = artMethod;
|
||||||
} else {
|
} else {
|
||||||
int dexMethodIndex = (int) dexMethodIndexField.get(backup);
|
int dexMethodIndex = (int) dexMethodIndexField.get(backup);
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
||||||
long artMethod = (long) artMethodField.get(backup);
|
long artMethod = (long) artMethodField.get(backup);
|
||||||
((long[])resolvedMethods)[dexMethodIndex] = artMethod;
|
((long[]) resolvedMethods)[dexMethodIndex] = artMethod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package com.elderdrivers.riru.edxp.entry;
|
||||||
|
|
||||||
|
public interface Hook {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.elderdrivers.riru.edxp.entry;
|
||||||
|
|
||||||
|
public class HookImpl<T> implements Hook {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.entry.yahfa;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.HandleBindAppHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.LoadedApkConstructorHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.OnePlusWorkAroundHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||||
|
|
||||||
public class AppBootstrapHookInfo implements KeepMembers {
|
public class AppBootstrapHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.entry.yahfa;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.HandleBindAppHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.LoadedApkConstructorHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.SystemMainHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.SystemMainHooker;
|
||||||
|
|
||||||
public class SysBootstrapHookInfo implements KeepMembers {
|
public class SysBootstrapHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.entry.yahfa;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.StartBootstrapServicesHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.StartBootstrapServicesHooker;
|
||||||
|
|
||||||
public class SysInnerHookInfo implements KeepMembers {
|
public class SysInnerHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.entry.yahfa;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.OnePlusWorkAroundHooker;
|
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||||
|
|
||||||
public class WorkAroundHookInfo implements KeepMembers {
|
public class WorkAroundHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.elderdrivers.riru.edxp.proxy;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.core.Proxy;
|
||||||
|
|
||||||
|
public abstract class BaseProxy implements Proxy {
|
||||||
|
|
||||||
|
protected Router mRouter;
|
||||||
|
|
||||||
|
public BaseProxy(Router router) {
|
||||||
|
mRouter = router;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean init() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
package com.elderdrivers.riru.edxp.proxy;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.yahfa.SystemMainHooker;
|
||||||
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
|
import com.elderdrivers.riru.edxp.entry.yahfa.AppBootstrapHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.entry.yahfa.SysBootstrapHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.entry.yahfa.SysInnerHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.entry.yahfa.WorkAroundHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
||||||
|
public abstract class BaseRouter implements Router {
|
||||||
|
|
||||||
|
protected volatile boolean forkCompleted = false;
|
||||||
|
|
||||||
|
protected volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public void initResourcesHook() {
|
||||||
|
startWorkAroundHook(); // for OnePlus devices
|
||||||
|
XposedBridge.initXResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prepare(boolean isSystem) {
|
||||||
|
// this flag is needed when loadModules
|
||||||
|
XposedInit.startsSystemServer = isSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onForkStart() {
|
||||||
|
forkCompleted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onForkFinish() {
|
||||||
|
forkCompleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForkCompleted() {
|
||||||
|
return forkCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void installBootstrapHooks(boolean isSystem) {
|
||||||
|
// Initialize the Xposed framework
|
||||||
|
try {
|
||||||
|
if (!bootstrapHooked.compareAndSet(false, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startBootstrapHook(isSystem);
|
||||||
|
XposedInit.initForZygote(isSystem);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Utils.logE("error during Xposed initialization", t);
|
||||||
|
XposedBridge.disableHooks = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadModulesSafely(boolean isInZygote) {
|
||||||
|
try {
|
||||||
|
// FIXME some coredomain app can't reading modules.list
|
||||||
|
XposedInit.loadModules(isInZygote);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Utils.logE("error loading module list", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parsePackageName(String appDataDir) {
|
||||||
|
if (TextUtils.isEmpty(appDataDir)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int lastIndex = appDataDir.lastIndexOf("/");
|
||||||
|
if (lastIndex < 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return appDataDir.substring(lastIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void startBootstrapHook(boolean isSystem) {
|
||||||
|
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||||
|
ClassLoader classLoader = XposedBridge.BOOTCLASSLOADER;
|
||||||
|
if (isSystem) {
|
||||||
|
HookMain.doHookDefault(
|
||||||
|
BaseRouter.class.getClassLoader(),
|
||||||
|
classLoader,
|
||||||
|
SysBootstrapHookInfo.class.getName());
|
||||||
|
} else {
|
||||||
|
HookMain.doHookDefault(
|
||||||
|
BaseRouter.class.getClassLoader(),
|
||||||
|
classLoader,
|
||||||
|
AppBootstrapHookInfo.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startSystemServerHook() {
|
||||||
|
HookMain.doHookDefault(
|
||||||
|
BaseRouter.class.getClassLoader(),
|
||||||
|
SystemMainHooker.systemServerCL,
|
||||||
|
SysInnerHookInfo.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startWorkAroundHook() {
|
||||||
|
HookMain.doHookDefault(
|
||||||
|
BaseRouter.class.getClassLoader(),
|
||||||
|
XposedBridge.BOOTCLASSLOADER,
|
||||||
|
WorkAroundHookInfo.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.proxy;
|
package com.elderdrivers.riru.edxp.proxy;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
import com.elderdrivers.riru.edxp.framework.Zygote;
|
||||||
import com.elderdrivers.riru.edxp.util.ProcessUtils;
|
import com.elderdrivers.riru.edxp.util.ProcessUtils;
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
|
@ -33,13 +31,17 @@ import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
||||||
* 2. Dynamic mode:
|
* 2. Dynamic mode:
|
||||||
* to be continued
|
* to be continued
|
||||||
*/
|
*/
|
||||||
public class BlackWhiteListProxy {
|
public class BlackWhiteListProxy extends BaseProxy {
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
public BlackWhiteListProxy(Router router) {
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
super(router);
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
}
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
public void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
||||||
|
int[][] rlimits, int mountExternal, String seInfo,
|
||||||
|
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||||
|
boolean startChildZygote, String instructionSet,
|
||||||
|
String appDataDir) {
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
||||||
if (isDynamicModulesMode) {
|
if (isDynamicModulesMode) {
|
||||||
// should never happen
|
// should never happen
|
||||||
|
|
@ -49,13 +51,13 @@ public class BlackWhiteListProxy {
|
||||||
onForkPreForNonDynamicMode(false);
|
onForkPreForNonDynamicMode(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||||
onForkPostCommon(false, appDataDir, niceName);
|
onForkPostCommon(false, appDataDir, niceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
||||||
int[][] rlimits, long permittedCapabilities,
|
int[][] rlimits, long permittedCapabilities,
|
||||||
long effectiveCapabilities) {
|
long effectiveCapabilities) {
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
||||||
if (isDynamicModulesMode) {
|
if (isDynamicModulesMode) {
|
||||||
// should never happen
|
// should never happen
|
||||||
|
|
@ -65,59 +67,59 @@ public class BlackWhiteListProxy {
|
||||||
onForkPreForNonDynamicMode(true);
|
onForkPreForNonDynamicMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
public void forkSystemServerPost(int pid) {
|
||||||
onForkPostCommon(true, getDataPathPrefix() + "android", "system_server");
|
onForkPostCommon(true, getDataPathPrefix() + "android", "system_server");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some details are different between main zygote and secondary zygote.
|
* Some details are different between main zygote and secondary zygote.
|
||||||
*/
|
*/
|
||||||
private static void onForkPreForNonDynamicMode(boolean isSystemServer) {
|
private void onForkPreForNonDynamicMode(boolean isSystemServer) {
|
||||||
Router.onForkStart();
|
mRouter.onForkStart();
|
||||||
Router.initResourcesHook();
|
mRouter.initResourcesHook();
|
||||||
// set startsSystemServer flag used when loadModules
|
// set startsSystemServer flag used when loadModules
|
||||||
Router.prepare(isSystemServer);
|
mRouter.prepare(isSystemServer);
|
||||||
// deoptBootMethods once for all child processes of zygote
|
// deoptBootMethods once for all child processes of zygote
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
PrebuiltMethodsDeopter.deoptBootMethods();
|
||||||
// we never install bootstrap hooks here in black/white list mode except workaround hooks
|
// we never install bootstrap hooks here in black/white list mode except workaround hooks
|
||||||
// because installed hooks would be propagated to all child processes of zygote
|
// because installed hooks would be propagated to all child processes of zygote
|
||||||
Router.startWorkAroundHook();
|
mRouter.startWorkAroundHook();
|
||||||
// loadModules once for all child processes of zygote
|
// loadModules once for all child processes of zygote
|
||||||
// TODO maybe just save initZygote callbacks and call them when whitelisted process forked?
|
// TODO maybe just save initZygote callbacks and call them when whitelisted process forked?
|
||||||
Router.loadModulesSafely(true);
|
mRouter.loadModulesSafely(true);
|
||||||
Zygote.closeFilesBeforeFork();
|
Zygote.closeFilesBeforeFork();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
private void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
||||||
Main.appDataDir = appDataDir;
|
ConfigManager.appDataDir = appDataDir;
|
||||||
Main.niceName = niceName;
|
ConfigManager.niceName = niceName;
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
||||||
if (!isDynamicModulesMode) {
|
if (!isDynamicModulesMode) {
|
||||||
Zygote.reopenFilesAfterFork();
|
Zygote.reopenFilesAfterFork();
|
||||||
}
|
}
|
||||||
Router.onEnterChildProcess();
|
mRouter.onEnterChildProcess();
|
||||||
if (!checkNeedHook(appDataDir, niceName)) {
|
if (!checkNeedHook(appDataDir, niceName)) {
|
||||||
// if is blacklisted, just stop here
|
// if is blacklisted, just stop here
|
||||||
Router.onForkFinish();
|
mRouter.onForkFinish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDynamicModulesMode) {
|
if (isDynamicModulesMode) {
|
||||||
Router.initResourcesHook();
|
mRouter.initResourcesHook();
|
||||||
}
|
}
|
||||||
Router.prepare(isSystemServer);
|
mRouter.prepare(isSystemServer);
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
PrebuiltMethodsDeopter.deoptBootMethods();
|
||||||
Router.installBootstrapHooks(isSystemServer);
|
mRouter.installBootstrapHooks(isSystemServer);
|
||||||
if (isDynamicModulesMode) {
|
if (isDynamicModulesMode) {
|
||||||
Router.loadModulesSafely(false);
|
mRouter.loadModulesSafely(false);
|
||||||
}
|
}
|
||||||
Router.onForkFinish();
|
mRouter.onForkFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkNeedHook(String appDataDir, String niceName) {
|
private boolean checkNeedHook(String appDataDir, String niceName) {
|
||||||
boolean needHook;
|
boolean needHook;
|
||||||
if (TextUtils.isEmpty(appDataDir)) {
|
if (TextUtils.isEmpty(appDataDir)) {
|
||||||
Utils.logE("niceName:" + niceName + ", procName:"
|
Utils.logE("niceName:" + niceName + ", procName:"
|
||||||
+ ProcessUtils.getCurrentProcessName(Main.appProcessName) + ", appDataDir is null, blacklisted!");
|
+ ProcessUtils.getCurrentProcessName(ConfigManager.appProcessName) + ", appDataDir is null, blacklisted!");
|
||||||
needHook = false;
|
needHook = false;
|
||||||
} else {
|
} else {
|
||||||
// FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth
|
// FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.elderdrivers.riru.edxp.proxy;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
|
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
||||||
|
import com.elderdrivers.riru.edxp.framework.Zygote;
|
||||||
|
|
||||||
|
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
||||||
|
|
||||||
|
public class NormalProxy extends BaseProxy {
|
||||||
|
|
||||||
|
public NormalProxy(Router router) {
|
||||||
|
super(router);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
||||||
|
int[][] rlimits, int mountExternal, String seInfo,
|
||||||
|
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||||
|
boolean startChildZygote, String instructionSet,
|
||||||
|
String appDataDir) {
|
||||||
|
// mainly for secondary zygote
|
||||||
|
mRouter.onForkStart();
|
||||||
|
mRouter.initResourcesHook();
|
||||||
|
// call this to ensure the flag is set to false ASAP
|
||||||
|
mRouter.prepare(false);
|
||||||
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||||
|
// install bootstrap hooks for secondary zygote
|
||||||
|
mRouter.installBootstrapHooks(false);
|
||||||
|
// only load modules for secondary zygote
|
||||||
|
mRouter.loadModulesSafely(true);
|
||||||
|
Zygote.closeFilesBeforeFork();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||||
|
// TODO consider processes without forkAndSpecializePost called
|
||||||
|
ConfigManager.appDataDir = appDataDir;
|
||||||
|
ConfigManager.niceName = niceName;
|
||||||
|
mRouter.prepare(false);
|
||||||
|
Zygote.reopenFilesAfterFork();
|
||||||
|
mRouter.onEnterChildProcess();
|
||||||
|
// load modules for each app process on its forked if dynamic modules mode is on
|
||||||
|
mRouter.loadModulesSafely(false);
|
||||||
|
mRouter.onForkFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
||||||
|
long permittedCapabilities, long effectiveCapabilities) {
|
||||||
|
mRouter.onForkStart();
|
||||||
|
mRouter.initResourcesHook();
|
||||||
|
// set startsSystemServer flag used when loadModules
|
||||||
|
mRouter.prepare(true);
|
||||||
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote
|
||||||
|
// install bootstrap hooks for main zygote as early as possible
|
||||||
|
// in case we miss some processes not forked via forkAndSpecialize
|
||||||
|
// for instance com.android.phone
|
||||||
|
mRouter.installBootstrapHooks(true);
|
||||||
|
// loadModules have to be executed in zygote even isDynamicModules is false
|
||||||
|
// because if not global hooks installed in initZygote might not be
|
||||||
|
// propagated to processes not forked via forkAndSpecialize
|
||||||
|
mRouter.loadModulesSafely(true);
|
||||||
|
Zygote.closeFilesBeforeFork();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forkSystemServerPost(int pid) {
|
||||||
|
// in system_server process
|
||||||
|
ConfigManager.appDataDir = getDataPathPrefix() + "android";
|
||||||
|
ConfigManager.niceName = "system_server";
|
||||||
|
mRouter.prepare(true);
|
||||||
|
Zygote.reopenFilesAfterFork();
|
||||||
|
mRouter.onEnterChildProcess();
|
||||||
|
// reload module list if dynamic mode is on
|
||||||
|
mRouter.loadModulesSafely(false);
|
||||||
|
mRouter.onForkFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.elderdrivers.riru.edxp.proxy;
|
||||||
|
|
||||||
|
public interface Router {
|
||||||
|
|
||||||
|
void initResourcesHook();
|
||||||
|
|
||||||
|
void prepare(boolean isSystem);
|
||||||
|
|
||||||
|
String parsePackageName(String appDataDir);
|
||||||
|
|
||||||
|
void installBootstrapHooks(boolean isSystem);
|
||||||
|
|
||||||
|
void loadModulesSafely(boolean isInZygote);
|
||||||
|
|
||||||
|
void startBootstrapHook(boolean isSystem);
|
||||||
|
|
||||||
|
void startSystemServerHook();
|
||||||
|
|
||||||
|
void startWorkAroundHook();
|
||||||
|
|
||||||
|
void onForkStart();
|
||||||
|
|
||||||
|
void onForkFinish();
|
||||||
|
|
||||||
|
void onEnterChildProcess();
|
||||||
|
|
||||||
|
void injectConfig();
|
||||||
|
|
||||||
|
boolean isForkCompleted();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.elderdrivers.riru.edxp.util;
|
||||||
|
|
||||||
|
import android.app.AndroidAppHelper;
|
||||||
|
|
||||||
|
public class Hookers {
|
||||||
|
|
||||||
|
public static void logD(String prefix) {
|
||||||
|
Utils.logD(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
||||||
|
AndroidAppHelper.currentProcessName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logE(String prefix, Throwable throwable) {
|
||||||
|
Utils.logE(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
||||||
|
AndroidAppHelper.currentProcessName()), throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,10 @@ import org.gradle.internal.os.OperatingSystem
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
version "v0.4.3.0_alpha"
|
version "v0.4.3.2_alpha"
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
versionCode = "4300"
|
versionCode = "4320"
|
||||||
module_name = "EdXposed"
|
module_name = "EdXposed"
|
||||||
jar_dest_dir = "${projectDir}/template_override/system/framework/"
|
jar_dest_dir = "${projectDir}/template_override/system/framework/"
|
||||||
is_windows = OperatingSystem.current().isWindows()
|
is_windows = OperatingSystem.current().isWindows()
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,15 @@
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion);
|
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion);
|
||||||
|
|
||||||
jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
|
jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
|
||||||
jclass targetClass, jstring methodName, jstring methodSig);
|
jclass targetClass, jstring methodName,
|
||||||
|
jstring methodSig);
|
||||||
|
|
||||||
jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
|
jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
|
||||||
jobject target, jobject hook,
|
jobject target, jobject hook,
|
||||||
|
|
@ -18,6 +23,10 @@ void Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(JNIEnv *env, jclass clazz
|
||||||
|
|
||||||
void setNonCompilable(void *method);
|
void setNonCompilable(void *method);
|
||||||
|
|
||||||
static void* getResolvedMethodsAddr(JNIEnv*, jobject);
|
static void *getResolvedMethodsAddr(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // HOOK_MAIN_H
|
#endif // HOOK_MAIN_H
|
||||||
|
|
@ -20,7 +20,7 @@ namespace edxp {
|
||||||
static constexpr const char *kInjectDexPath = "/system/framework/edxp.jar:"
|
static constexpr const char *kInjectDexPath = "/system/framework/edxp.jar:"
|
||||||
"/system/framework/eddalvikdx.jar:"
|
"/system/framework/eddalvikdx.jar:"
|
||||||
"/system/framework/eddexmaker.jar";
|
"/system/framework/eddexmaker.jar";
|
||||||
static constexpr const char *kEntryClassName = "com.elderdrivers.riru.edxp.Main";
|
static constexpr const char *kEntryClassName = "com.elderdrivers.riru.edxp.core.Main";
|
||||||
static constexpr const char *kSandHookClassName = "com.swift.sandhook.SandHook";
|
static constexpr const char *kSandHookClassName = "com.swift.sandhook.SandHook";
|
||||||
static constexpr const char *kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall";
|
static constexpr const char *kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#include "HookMain.h"
|
#include "HookMain.h"
|
||||||
}
|
|
||||||
|
|
||||||
#include <nativehelper/jni_macros.h>
|
#include <nativehelper/jni_macros.h>
|
||||||
#include "jni.h"
|
#include "jni.h"
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/system/bin/sh
|
#!/system/bin/sh
|
||||||
|
|
||||||
EDXP_VERSION="0.4.3.0_alpha (4300)"
|
EDXP_VERSION="0.4.3.2_alpha (4320)"
|
||||||
ANDROID_SDK=`getprop ro.build.version.sdk`
|
ANDROID_SDK=`getprop ro.build.version.sdk`
|
||||||
BUILD_DESC=`getprop ro.build.description`
|
BUILD_DESC=`getprop ro.build.description`
|
||||||
PRODUCT=`getprop ro.build.product`
|
PRODUCT=`getprop ro.build.product`
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ android {
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
multiDexEnabled true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
@ -61,7 +62,8 @@ afterEvaluate {
|
||||||
|
|
||||||
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
||||||
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
||||||
from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/"
|
from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/",
|
||||||
|
"${projectDir}/src/main/resources/"
|
||||||
destinationDir file(myTemplatePath + "system/framework/")
|
destinationDir file(myTemplatePath + "system/framework/")
|
||||||
baseName "edxp"
|
baseName "edxp"
|
||||||
doLast {
|
doLast {
|
||||||
|
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.LoadedApk;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.SliceProviderFix;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedInstallerHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.swift.sandhook.SandHook;
|
|
||||||
import com.swift.sandhook.annotation.HookClass;
|
|
||||||
import com.swift.sandhook.annotation.HookMethod;
|
|
||||||
import com.swift.sandhook.annotation.HookMethodBackup;
|
|
||||||
import com.swift.sandhook.annotation.Param;
|
|
||||||
import com.swift.sandhook.annotation.SkipParamCheck;
|
|
||||||
import com.swift.sandhook.annotation.ThisObject;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_PACKAGE_NAME;
|
|
||||||
import static com.elderdrivers.riru.edxp.hooker.SliceProviderFix.SYSTEMUI_PACKAGE_NAME;
|
|
||||||
import static com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker.BLACK_LIST_PACKAGE_NAME;
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
|
|
||||||
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
|
||||||
@HookClass(ActivityThread.class)
|
|
||||||
public class HandleBindAppHooker implements KeepMembers {
|
|
||||||
|
|
||||||
public static String className = "android.app.ActivityThread";
|
|
||||||
public static String methodName = "handleBindApplication";
|
|
||||||
public static String methodSig = "(Landroid/app/ActivityThread$AppBindData;)V";
|
|
||||||
|
|
||||||
@HookMethodBackup("handleBindApplication")
|
|
||||||
@SkipParamCheck
|
|
||||||
static Method backup;
|
|
||||||
|
|
||||||
@HookMethod("handleBindApplication")
|
|
||||||
public static void hook(@ThisObject ActivityThread thiz, @Param("android.app.ActivityThread$AppBindData") Object bindData) throws Throwable {
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
|
||||||
backup(thiz, bindData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Router.logD("ActivityThread#handleBindApplication() starts");
|
|
||||||
ActivityThread activityThread = (ActivityThread) thiz;
|
|
||||||
ApplicationInfo appInfo = (ApplicationInfo) XposedHelpers.getObjectField(bindData, "appInfo");
|
|
||||||
// save app process name here for later use
|
|
||||||
Main.appProcessName = (String) XposedHelpers.getObjectField(bindData, "processName");
|
|
||||||
String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
|
|
||||||
Utils.logD("processName=" + Main.appProcessName +
|
|
||||||
", packageName=" + reportedPackageName + ", appDataDir=" + Main.appDataDir);
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks(reportedPackageName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentName instrumentationName = (ComponentName) XposedHelpers.getObjectField(bindData, "instrumentationName");
|
|
||||||
if (instrumentationName != null) {
|
|
||||||
Router.logD("Instrumentation detected, disabling framework for");
|
|
||||||
XposedBridge.disableHooks = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CompatibilityInfo compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(bindData, "compatInfo");
|
|
||||||
if (appInfo.sourceDir == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
XposedHelpers.setObjectField(activityThread, "mBoundApplication", bindData);
|
|
||||||
XposedInit.loadedPackagesInProcess.add(reportedPackageName);
|
|
||||||
LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
|
|
||||||
|
|
||||||
replaceParentClassLoader(loadedApk.getClassLoader());
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = reportedPackageName;
|
|
||||||
lpparam.processName = (String) XposedHelpers.getObjectField(bindData, "processName");
|
|
||||||
lpparam.classLoader = loadedApk.getClassLoader();
|
|
||||||
lpparam.appInfo = appInfo;
|
|
||||||
lpparam.isFirstApplication = true;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
|
|
||||||
if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME)) {
|
|
||||||
XposedInstallerHooker.hookXposedInstaller(lpparam.classLoader);
|
|
||||||
}
|
|
||||||
if (reportedPackageName.equals(BLACK_LIST_PACKAGE_NAME)) {
|
|
||||||
XposedBlackListHooker.hook(lpparam.classLoader);
|
|
||||||
}
|
|
||||||
if (reportedPackageName.equals(SYSTEMUI_PACKAGE_NAME)) {
|
|
||||||
SliceProviderFix.hook();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking bindApp", t);
|
|
||||||
} finally {
|
|
||||||
backup(thiz, bindData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object thiz, Object bindData) throws Throwable {
|
|
||||||
SandHook.callOriginByBackup(backup, thiz, bindData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.AndroidAppHelper;
|
|
||||||
import android.app.LoadedApk;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.swift.sandhook.SandHook;
|
|
||||||
import com.swift.sandhook.annotation.HookClass;
|
|
||||||
import com.swift.sandhook.annotation.HookMethod;
|
|
||||||
import com.swift.sandhook.annotation.HookMethodBackup;
|
|
||||||
import com.swift.sandhook.annotation.SkipParamCheck;
|
|
||||||
import com.swift.sandhook.annotation.ThisObject;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
|
|
||||||
// when a package is loaded for an existing process, trigger the callbacks as well
|
|
||||||
// ed: remove resources related hooking
|
|
||||||
@HookClass(LoadedApk.class)
|
|
||||||
public class LoadedApkConstructorHooker implements KeepMembers {
|
|
||||||
public static String className = "android.app.LoadedApk";
|
|
||||||
public static String methodName = "<init>";
|
|
||||||
public static String methodSig = "(Landroid/app/ActivityThread;" +
|
|
||||||
"Landroid/content/pm/ApplicationInfo;" +
|
|
||||||
"Landroid/content/res/CompatibilityInfo;" +
|
|
||||||
"Ljava/lang/ClassLoader;ZZZ)V";
|
|
||||||
|
|
||||||
@HookMethodBackup
|
|
||||||
@SkipParamCheck
|
|
||||||
static Method backup;
|
|
||||||
|
|
||||||
@HookMethod
|
|
||||||
public static void hook(@ThisObject Object thiz, ActivityThread activityThread,
|
|
||||||
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
|
||||||
ClassLoader baseLoader, boolean securityViolation,
|
|
||||||
boolean includeCode, boolean registerPackage) throws Throwable {
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
|
||||||
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation, includeCode, registerPackage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Router.logD("LoadedApk#<init> starts");
|
|
||||||
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation, includeCode, registerPackage);
|
|
||||||
|
|
||||||
try {
|
|
||||||
LoadedApk loadedApk = (LoadedApk) thiz;
|
|
||||||
String packageName = loadedApk.getPackageName();
|
|
||||||
Object mAppDir = XposedHelpers.getObjectField(thiz, "mAppDir");
|
|
||||||
Router.logD("LoadedApk#<init> ends: " + mAppDir);
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks(packageName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageName.equals("android")) {
|
|
||||||
Router.logD("LoadedApk#<init> is android, skip: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mIncludeCode checking should go ahead of loadedPackagesInProcess added checking
|
|
||||||
if (!XposedHelpers.getBooleanField(loadedApk, "mIncludeCode")) {
|
|
||||||
Router.logD("LoadedApk#<init> mIncludeCode == false: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!XposedInit.loadedPackagesInProcess.add(packageName)) {
|
|
||||||
Router.logD("LoadedApk#<init> has been loaded before, skip: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnePlus magic...
|
|
||||||
if (Log.getStackTraceString(new Throwable()).
|
|
||||||
contains("android.app.ActivityThread$ApplicationThread.schedulePreload")) {
|
|
||||||
Router.logD("LoadedApk#<init> maybe oneplus's custom opt, skip");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceParentClassLoader(loadedApk.getClassLoader());
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = packageName;
|
|
||||||
lpparam.processName = AndroidAppHelper.currentProcessName();
|
|
||||||
lpparam.classLoader = loadedApk.getClassLoader();
|
|
||||||
lpparam.appInfo = loadedApk.getApplicationInfo();
|
|
||||||
lpparam.isFirstApplication = false;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking LoadedApk.<init>", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object thiz, ActivityThread activityThread,
|
|
||||||
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
|
||||||
ClassLoader baseLoader, boolean securityViolation,
|
|
||||||
boolean includeCode, boolean registerPackage) throws Throwable {
|
|
||||||
SandHook.callOriginByBackup(backup, thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation, includeCode, registerPackage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook._hooker;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.swift.sandhook.SandHook;
|
|
||||||
import com.swift.sandhook.annotation.HookClass;
|
|
||||||
import com.swift.sandhook.annotation.HookMethod;
|
|
||||||
import com.swift.sandhook.annotation.HookMethodBackup;
|
|
||||||
import com.swift.sandhook.annotation.SkipParamCheck;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import dalvik.system.BaseDexClassLoader;
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On OnePlus stock roms (Android Pie), {@link dalvik.system.BaseDexClassLoader#findClass(String)}
|
|
||||||
* will open /dev/binder to communicate with PackageManagerService to check whether
|
|
||||||
* current package name inCompatConfigList, which is an OnePlus OEM feature enabled only when
|
|
||||||
* system prop "persist.sys.oem.region" set to "CN".(detail of related source code:
|
|
||||||
* https://gist.github.com/solohsu/ecc07141759958fc096ba0781fac0a5f)
|
|
||||||
* If we invoke intZygoteCallbacks in
|
|
||||||
* {@link Main#forkAndSpecializePre}, where in zygote process,
|
|
||||||
* we would get a chance to invoke findclass, leaving fd of /dev/binder open in zygote process,
|
|
||||||
* which is not allowed because /dev/binder is not in predefined whitelist here:
|
|
||||||
* http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/jni/fd_utils.cpp#35
|
|
||||||
* So we just hook BaseDexClassLoader#inCompatConfigList to return false to prevent
|
|
||||||
* open of /dev/binder and we haven't found side effects yet.
|
|
||||||
* Other roms might share the same problems but not reported too.
|
|
||||||
*/
|
|
||||||
@HookClass(BaseDexClassLoader.class)
|
|
||||||
public class OnePlusWorkAroundHooker implements KeepMembers {
|
|
||||||
|
|
||||||
public static String className = "dalvik.system.BaseDexClassLoader";
|
|
||||||
public static String methodName = "inCompatConfigList";
|
|
||||||
public static String methodSig = "(ILjava/lang/String;)Z";
|
|
||||||
|
|
||||||
@HookMethodBackup("inCompatConfigList")
|
|
||||||
@SkipParamCheck
|
|
||||||
static Method backup;
|
|
||||||
|
|
||||||
@HookMethod("inCompatConfigList")
|
|
||||||
public static boolean hook(int type, String packageName) throws Throwable {
|
|
||||||
if (XposedBridge.disableHooks || Router.forkCompleted) {
|
|
||||||
return backup(type, packageName);
|
|
||||||
}
|
|
||||||
Router.logD("BaseDexClassLoader#inCompatConfigList() starts");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean backup(int type, String packageName) throws Throwable {
|
|
||||||
return (boolean) SandHook.callOriginByBackup(backup, null, type, packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook._hooker;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.swift.sandhook.SandHook;
|
|
||||||
import com.swift.sandhook.annotation.HookMethod;
|
|
||||||
import com.swift.sandhook.annotation.HookMethodBackup;
|
|
||||||
import com.swift.sandhook.annotation.HookReflectClass;
|
|
||||||
import com.swift.sandhook.annotation.SkipParamCheck;
|
|
||||||
import com.swift.sandhook.annotation.ThisObject;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XC_MethodReplacement;
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
import static com.elderdrivers.riru.edxp.util.Utils.logD;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
|
||||||
|
|
||||||
@HookReflectClass("com.android.server.SystemServer")
|
|
||||||
public class StartBootstrapServicesHooker implements KeepMembers {
|
|
||||||
public static String className = "com.android.server.SystemServer";
|
|
||||||
public static String methodName = "startBootstrapServices";
|
|
||||||
public static String methodSig = "()V";
|
|
||||||
|
|
||||||
@HookMethodBackup("startBootstrapServices")
|
|
||||||
@SkipParamCheck
|
|
||||||
static Method backup;
|
|
||||||
|
|
||||||
@HookMethod("startBootstrapServices")
|
|
||||||
public static void hook(@ThisObject Object systemServer) throws Throwable {
|
|
||||||
|
|
||||||
if (XposedBridge.disableHooks) {
|
|
||||||
backup(systemServer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logD("SystemServer#startBootstrapServices() starts");
|
|
||||||
|
|
||||||
try {
|
|
||||||
XposedInit.loadedPackagesInProcess.add("android");
|
|
||||||
|
|
||||||
replaceParentClassLoader(SystemMainHooker.systemServerCL);
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = "android";
|
|
||||||
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
|
|
||||||
lpparam.classLoader = SystemMainHooker.systemServerCL;
|
|
||||||
lpparam.appInfo = null;
|
|
||||||
lpparam.isFirstApplication = true;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
|
|
||||||
// Huawei
|
|
||||||
try {
|
|
||||||
findAndHookMethod("com.android.server.pm.HwPackageManagerService", SystemMainHooker.systemServerCL, "isOdexMode", XC_MethodReplacement.returnConstant(false));
|
|
||||||
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
|
|
||||||
findAndHookMethod(className, SystemMainHooker.systemServerCL, "dexEntryExists", String.class, XC_MethodReplacement.returnConstant(true));
|
|
||||||
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking startBootstrapServices", t);
|
|
||||||
} finally {
|
|
||||||
backup(systemServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object systemServer) throws Throwable {
|
|
||||||
SandHook.callOriginByBackup(backup, systemServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.config;
|
package com.elderdrivers.riru.edxp.sandhook.config;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfig;
|
import com.elderdrivers.riru.edxp.config.EdXpConfig;
|
||||||
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.elderdrivers.riru.edxp.sandhook.config;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.art.ClassLinker;
|
import com.elderdrivers.riru.edxp.art.ClassLinker;
|
||||||
import com.elderdrivers.riru.edxp.config.BaseHookProvider;
|
import com.elderdrivers.riru.edxp.config.BaseHookProvider;
|
||||||
import com.elderdrivers.riru.edxp.core.ResourcesHook;
|
import com.elderdrivers.riru.edxp.core.ResourcesHook;
|
||||||
|
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.core;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.art.Heap;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
|
|
||||||
public class HookMain {
|
|
||||||
|
|
||||||
private static Set<String> hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName());
|
|
||||||
|
|
||||||
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) {
|
|
||||||
try {
|
|
||||||
Class<?> hookInfoClass = Class.forName(hookInfoClassName, true, patchClassLoader);
|
|
||||||
String[] hookItemNames = (String[]) hookInfoClass.getField("hookItemNames").get(null);
|
|
||||||
for (String hookItemName : hookItemNames) {
|
|
||||||
doHookItemDefault(patchClassLoader, hookItemName, originClassLoader);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Utils.logE("error when hooking all in: " + hookInfoClassName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doHookItemDefault(ClassLoader patchClassLoader, String hookItemName, ClassLoader originClassLoader) {
|
|
||||||
try {
|
|
||||||
Utils.logD("Start hooking with item " + hookItemName);
|
|
||||||
Class<?> hookItem = Class.forName(hookItemName, true, patchClassLoader);
|
|
||||||
|
|
||||||
String className = (String) hookItem.getField("className").get(null);
|
|
||||||
String methodName = (String) hookItem.getField("methodName").get(null);
|
|
||||||
String methodSig = (String) hookItem.getField("methodSig").get(null);
|
|
||||||
|
|
||||||
if (className == null || className.equals("")) {
|
|
||||||
Utils.logW("No target class. Skipping...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Class<?> clazz = null;
|
|
||||||
try {
|
|
||||||
clazz = Class.forName(className, true, originClassLoader);
|
|
||||||
} catch (ClassNotFoundException cnfe) {
|
|
||||||
Utils.logE(className + " not found in " + originClassLoader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Modifier.isAbstract(clazz.getModifiers())) {
|
|
||||||
Utils.logW("Hook may fail for abstract class: " + className);
|
|
||||||
}
|
|
||||||
|
|
||||||
Method hook = null;
|
|
||||||
Method backup = null;
|
|
||||||
for (Method method : hookItem.getDeclaredMethods()) {
|
|
||||||
if (method.getName().equals("hook") && Modifier.isStatic(method.getModifiers())) {
|
|
||||||
hook = method;
|
|
||||||
} else if (method.getName().equals("backup") && Modifier.isStatic(method.getModifiers())) {
|
|
||||||
backup = method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hook == null) {
|
|
||||||
Utils.logE("Cannot find hook for " + methodName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
findAndBackupAndHook(clazz, methodName, methodSig, hook, backup);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!hookItemWhiteList.contains(hookItemName)) {
|
|
||||||
Utils.logE("error when hooking " + hookItemName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void findAndHook(Class targetClass, String methodName, String methodSig, Method hook) {
|
|
||||||
hook(findMethod(targetClass, methodName, methodSig), hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void findAndBackupAndHook(Class targetClass, String methodName, String methodSig,
|
|
||||||
Method hook, Method backup) {
|
|
||||||
backupAndHook(findMethod(targetClass, methodName, methodSig), hook, backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void hook(Object target, Method hook) {
|
|
||||||
backupAndHook(target, hook, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backupAndHook(Object target, Method hook, Method backup) {
|
|
||||||
Utils.logD(String.format("target=%s, hook=%s, backup=%s", target, hook, backup));
|
|
||||||
if (target == null) {
|
|
||||||
throw new IllegalArgumentException("null target method");
|
|
||||||
}
|
|
||||||
if (hook == null) {
|
|
||||||
throw new IllegalArgumentException("null hook method");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Modifier.isStatic(hook.getModifiers())) {
|
|
||||||
throw new IllegalArgumentException("Hook must be a static method: " + hook);
|
|
||||||
}
|
|
||||||
checkCompatibleMethods(target, hook, "Original", "Hook");
|
|
||||||
if (backup != null) {
|
|
||||||
if (!Modifier.isStatic(backup.getModifiers())) {
|
|
||||||
throw new IllegalArgumentException("Backup must be a static method: " + backup);
|
|
||||||
}
|
|
||||||
// backup is just a placeholder and the constraint could be less strict
|
|
||||||
checkCompatibleMethods(target, backup, "Original", "Backup");
|
|
||||||
}
|
|
||||||
if (backup != null) {
|
|
||||||
HookMethodResolver.resolveMethod(hook, backup);
|
|
||||||
}
|
|
||||||
// make sure GC completed before hook
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
int lastGcType = Heap.waitForGcToComplete(
|
|
||||||
XposedHelpers.getLongField(currentThread, "nativePeer"));
|
|
||||||
if (lastGcType < 0) {
|
|
||||||
Utils.logW("waitForGcToComplete failed, using fallback");
|
|
||||||
Runtime.getRuntime().gc();
|
|
||||||
}
|
|
||||||
if (!Yahfa.backupAndHookNative(target, hook, backup)) {
|
|
||||||
throw new RuntimeException("Failed to hook " + target + " with " + hook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object findMethod(Class cls, String methodName, String methodSig) {
|
|
||||||
if (cls == null) {
|
|
||||||
throw new IllegalArgumentException("null class");
|
|
||||||
}
|
|
||||||
if (methodName == null) {
|
|
||||||
throw new IllegalArgumentException("null method name");
|
|
||||||
}
|
|
||||||
if (methodSig == null) {
|
|
||||||
throw new IllegalArgumentException("null method signature");
|
|
||||||
}
|
|
||||||
return Yahfa.findMethodNative(cls, methodName, methodSig);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) {
|
|
||||||
ArrayList<Class<?>> originalParams;
|
|
||||||
if (original instanceof Method) {
|
|
||||||
originalParams = new ArrayList<>(Arrays.asList(((Method) original).getParameterTypes()));
|
|
||||||
} else if (original instanceof Constructor) {
|
|
||||||
originalParams = new ArrayList<>(Arrays.asList(((Constructor<?>) original).getParameterTypes()));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Type of target method is wrong");
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Class<?>> replacementParams = new ArrayList<>(Arrays.asList(replacement.getParameterTypes()));
|
|
||||||
|
|
||||||
if (original instanceof Method
|
|
||||||
&& !Modifier.isStatic(((Method) original).getModifiers())) {
|
|
||||||
originalParams.add(0, ((Method) original).getDeclaringClass());
|
|
||||||
} else if (original instanceof Constructor) {
|
|
||||||
originalParams.add(0, ((Constructor<?>) original).getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!Modifier.isStatic(replacement.getModifiers())) {
|
|
||||||
replacementParams.add(0, replacement.getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (original instanceof Method
|
|
||||||
&& !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) {
|
|
||||||
throw new IllegalArgumentException("Incompatible return types. " + originalName + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType());
|
|
||||||
} else if (original instanceof Constructor) {
|
|
||||||
if (replacement.getReturnType().equals(Void.class)) {
|
|
||||||
throw new IllegalArgumentException("Incompatible return types. " + "<init>" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originalParams.size() != replacementParams.size()) {
|
|
||||||
throw new IllegalArgumentException("Number of arguments don't match. " + originalName + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < originalParams.size(); i++) {
|
|
||||||
if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) {
|
|
||||||
throw new IllegalArgumentException("Incompatible argument #" + i + ": " + originalName + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.core;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create by Swift Gan on 14/01/2019
|
|
||||||
* To ensure method in resolved cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class HookMethodResolver {
|
|
||||||
|
|
||||||
public static Class artMethodClass;
|
|
||||||
|
|
||||||
public static Field resolvedMethodsField;
|
|
||||||
public static Field dexCacheField;
|
|
||||||
public static Field dexMethodIndexField;
|
|
||||||
public static Field artMethodField;
|
|
||||||
|
|
||||||
public static boolean canResolvedInJava = false;
|
|
||||||
public static boolean isArtMethod = false;
|
|
||||||
|
|
||||||
public static long resolvedMethodsAddress = 0;
|
|
||||||
public static int dexMethodIndex = 0;
|
|
||||||
|
|
||||||
public static Method testMethod;
|
|
||||||
public static Object testArtMethod;
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
checkSupport();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkSupport() {
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
isArtMethod = false;
|
|
||||||
canResolvedInJava = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testMethod = HookMethodResolver.class.getDeclaredMethod("init");
|
|
||||||
artMethodField = getField(Method.class, "artMethod");
|
|
||||||
|
|
||||||
testArtMethod = artMethodField.get(testMethod);
|
|
||||||
|
|
||||||
if (hasJavaArtMethod() && testArtMethod.getClass() == artMethodClass) {
|
|
||||||
checkSupportForArtMethod();
|
|
||||||
isArtMethod = true;
|
|
||||||
} else if (testArtMethod instanceof Long) {
|
|
||||||
checkSupportForArtMethodId();
|
|
||||||
isArtMethod = false;
|
|
||||||
} else {
|
|
||||||
canResolvedInJava = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
Utils.logE("error when checkSupport", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// may 5.0
|
|
||||||
private static void checkSupportForArtMethod() throws Exception {
|
|
||||||
dexMethodIndexField = getField(artMethodClass, "dexMethodIndex");
|
|
||||||
dexCacheField = getField(Class.class, "dexCache");
|
|
||||||
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
|
|
||||||
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
|
|
||||||
if (resolvedMethodsField.get(dexCache) instanceof Object[]) {
|
|
||||||
canResolvedInJava = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// may 6.0
|
|
||||||
private static void checkSupportForArtMethodId() throws Exception {
|
|
||||||
dexMethodIndexField = getField(Method.class, "dexMethodIndex");
|
|
||||||
dexMethodIndex = (int) dexMethodIndexField.get(testMethod);
|
|
||||||
dexCacheField = getField(Class.class, "dexCache");
|
|
||||||
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
|
|
||||||
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
|
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
|
||||||
if (resolvedMethods instanceof Long) {
|
|
||||||
canResolvedInJava = false;
|
|
||||||
resolvedMethodsAddress = (long) resolvedMethods;
|
|
||||||
} else if (resolvedMethods instanceof long[]) {
|
|
||||||
canResolvedInJava = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void resolveMethod(Method hook, Method backup) {
|
|
||||||
if (canResolvedInJava && artMethodField != null) {
|
|
||||||
// in java
|
|
||||||
try {
|
|
||||||
resolveInJava(hook, backup);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// in native
|
|
||||||
resolveInNative(hook, backup);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in native
|
|
||||||
resolveInNative(hook, backup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resolveInJava(Method hook, Method backup) throws Exception {
|
|
||||||
Object dexCache = dexCacheField.get(hook.getDeclaringClass());
|
|
||||||
if (isArtMethod) {
|
|
||||||
Object artMethod = artMethodField.get(backup);
|
|
||||||
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
|
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
|
||||||
((Object[])resolvedMethods)[dexMethodIndex] = artMethod;
|
|
||||||
} else {
|
|
||||||
int dexMethodIndex = (int) dexMethodIndexField.get(backup);
|
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
|
||||||
long artMethod = (long) artMethodField.get(backup);
|
|
||||||
((long[])resolvedMethods)[dexMethodIndex] = artMethod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resolveInNative(Method hook, Method backup) {
|
|
||||||
Yahfa.ensureMethodCached(hook, backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Field getField(Class topClass, String fieldName) throws NoSuchFieldException {
|
|
||||||
while (topClass != null && topClass != Object.class) {
|
|
||||||
try {
|
|
||||||
Field field = topClass.getDeclaredField(fieldName);
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field;
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
topClass = topClass.getSuperclass();
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasJavaArtMethod() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (artMethodClass != null)
|
|
||||||
return true;
|
|
||||||
try {
|
|
||||||
artMethodClass = Class.forName("java.lang.reflect.ArtMethod");
|
|
||||||
return true;
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.elderdrivers.riru.edxp.sandhook.core;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
|
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
||||||
|
import com.elderdrivers.riru.edxp.core.BaseEdxpImpl;
|
||||||
|
import com.elderdrivers.riru.edxp.core.EdxpImpl;
|
||||||
|
import com.elderdrivers.riru.edxp.core.Main;
|
||||||
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMethodResolver;
|
||||||
|
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
|
||||||
|
|
||||||
|
public class SandHookEdxpImpl extends BaseEdxpImpl {
|
||||||
|
|
||||||
|
static {
|
||||||
|
final EdxpImpl edxpImpl = new SandHookEdxpImpl();
|
||||||
|
if (Main.setEdxpImpl(edxpImpl)) {
|
||||||
|
edxpImpl.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected com.elderdrivers.riru.edxp.proxy.Router createRouter() {
|
||||||
|
return new SandHookRouter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Variant
|
||||||
|
@Override
|
||||||
|
public int getVariant() {
|
||||||
|
return SANDHOOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
Yahfa.init(Build.VERSION.SDK_INT);
|
||||||
|
HookMethodResolver.init();
|
||||||
|
getRouter().injectConfig();
|
||||||
|
InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName());
|
||||||
|
SandHookXposedBridge.init();
|
||||||
|
|
||||||
|
setInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.elderdrivers.riru.edxp.sandhook.core;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.hooker.SystemMainHooker;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.config.SandHookEdxpConfig;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.config.SandHookProvider;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.dexmaker.DynamicBridge;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.entry.AppBootstrapHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.entry.SysBootstrapHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.entry.SysInnerHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.sandhook.entry.WorkAroundHookInfo;
|
||||||
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
|
import com.swift.sandhook.xposedcompat.XposedCompat;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
public class SandHookRouter extends BaseRouter {
|
||||||
|
|
||||||
|
private static boolean useSandHook = false;
|
||||||
|
|
||||||
|
public void startBootstrapHook(boolean isSystem) {
|
||||||
|
if (useSandHook) {
|
||||||
|
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||||
|
ClassLoader classLoader = XposedBridge.BOOTCLASSLOADER;
|
||||||
|
if (isSystem) {
|
||||||
|
XposedCompat.addHookers(classLoader, SysBootstrapHookInfo.hookItems);
|
||||||
|
} else {
|
||||||
|
XposedCompat.addHookers(classLoader, AppBootstrapHookInfo.hookItems);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.startBootstrapHook(isSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startSystemServerHook() {
|
||||||
|
if (useSandHook) {
|
||||||
|
XposedCompat.addHookers(SystemMainHooker.systemServerCL, SysInnerHookInfo.hookItems);
|
||||||
|
} else {
|
||||||
|
super.startSystemServerHook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startWorkAroundHook() {
|
||||||
|
if (useSandHook) {
|
||||||
|
XposedCompat.addHookers(XposedBridge.BOOTCLASSLOADER, WorkAroundHookInfo.hookItems);
|
||||||
|
} else {
|
||||||
|
super.startWorkAroundHook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onEnterChildProcess() {
|
||||||
|
DynamicBridge.onForkPost();
|
||||||
|
//enable compile in child process
|
||||||
|
//SandHook.enableCompiler(!XposedInit.startsSystemServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectConfig() {
|
||||||
|
EdXpConfigGlobal.sConfig = new SandHookEdxpConfig();
|
||||||
|
EdXpConfigGlobal.sHookProvider = new SandHookProvider();
|
||||||
|
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,7 @@ import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
import com.elderdrivers.riru.edxp.sandhook.core.HookMain;
|
|
||||||
|
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|
@ -27,8 +26,8 @@ public class DexMakerUtils {
|
||||||
}
|
}
|
||||||
String packageName = AndroidAppHelper.currentPackageName();
|
String packageName = AndroidAppHelper.currentPackageName();
|
||||||
if (TextUtils.isEmpty(packageName)) { //default to true
|
if (TextUtils.isEmpty(packageName)) { //default to true
|
||||||
DexLog.w("packageName is empty, processName=" + Main.appProcessName
|
DexLog.w("packageName is empty, processName=" + ConfigManager.appProcessName
|
||||||
+ ", appDataDir=" + Main.appDataDir);
|
+ ", appDataDir=" + ConfigManager.appDataDir);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return !ConfigManager.shouldUseCompatMode(packageName);
|
return !ConfigManager.shouldUseCompatMode(packageName);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.dexmaker;
|
package com.elderdrivers.riru.edxp.sandhook.dexmaker;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
@ -8,16 +8,15 @@ import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getPackageName;
|
import static com.elderdrivers.riru.edxp.util.FileUtils.getPackageName;
|
||||||
import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName;
|
import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName;
|
||||||
import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
|
|
||||||
|
|
||||||
public final class DynamicBridge {
|
public final class DynamicBridge {
|
||||||
|
|
||||||
|
|
@ -78,9 +77,9 @@ public final class DynamicBridge {
|
||||||
try {
|
try {
|
||||||
// we always choose to use device encrypted storage data on android N and later
|
// we always choose to use device encrypted storage data on android N and later
|
||||||
// in case some app is installing hooks before phone is unlocked
|
// in case some app is installing hooks before phone is unlocked
|
||||||
String fixedAppDataDir = getDataPathPrefix() + getPackageName(Main.appDataDir) + "/";
|
String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/";
|
||||||
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
|
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
|
||||||
+ getCurrentProcessName(Main.appProcessName).replace(":", "_") + "/");
|
+ getCurrentProcessName(ConfigManager.appProcessName).replace(":", "_") + "/");
|
||||||
dexDir.mkdirs();
|
dexDir.mkdirs();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
DexLog.e("error when init dex path", throwable);
|
DexLog.e("error when init dex path", throwable);
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@ import android.annotation.TargetApi;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
import com.elderdrivers.riru.edxp.sandhook.core.HookMain;
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.sandhook.entry;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.HandleBindAppHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.HandleBindAppHooker;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.LoadedApkConstructorHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.LoadedApkConstructorHooker;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.OnePlusWorkAroundHooker;
|
||||||
|
|
||||||
public class AppBootstrapHookInfo implements KeepMembers {
|
public class AppBootstrapHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.entry;
|
|
||||||
|
|
||||||
import android.app.AndroidAppHelper;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.config.SandHookEdxpConfig;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.config.SandHookProvider;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.core.HookMain;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.dexmaker.DynamicBridge;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.bootstrap.AppBootstrapHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.bootstrap.SysBootstrapHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.bootstrap.SysInnerHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.bootstrap.WorkAroundHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.SystemMainHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.swift.sandhook.xposedcompat.XposedCompat;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
|
|
||||||
import static de.robv.android.xposed.XposedInit.startsSystemServer;
|
|
||||||
|
|
||||||
public class Router {
|
|
||||||
|
|
||||||
public volatile static boolean forkCompleted = false;
|
|
||||||
|
|
||||||
private static volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
static boolean useSandHook = false;
|
|
||||||
|
|
||||||
public static void initResourcesHook() {
|
|
||||||
startWorkAroundHook(); // for OnePlus devices
|
|
||||||
XposedBridge.initXResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void prepare(boolean isSystem) {
|
|
||||||
// this flag is needed when loadModules
|
|
||||||
startsSystemServer = isSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkHookState(String appDataDir) {
|
|
||||||
// determine whether allow xposed or not
|
|
||||||
// XposedBridge.disableHooks = ConfigManager.shouldHook(parsePackageName(appDataDir));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String parsePackageName(String appDataDir) {
|
|
||||||
if (TextUtils.isEmpty(appDataDir)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
int lastIndex = appDataDir.lastIndexOf("/");
|
|
||||||
if (lastIndex < 1) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return appDataDir.substring(lastIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void installBootstrapHooks(boolean isSystem) {
|
|
||||||
// Initialize the Xposed framework
|
|
||||||
try {
|
|
||||||
if (!bootstrapHooked.compareAndSet(false, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Router.startBootstrapHook(isSystem);
|
|
||||||
XposedInit.initForZygote(isSystem);
|
|
||||||
//SandHookConfig.compiler = !isSystem;
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Utils.logE("error during Xposed initialization", t);
|
|
||||||
XposedBridge.disableHooks = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void loadModulesSafely(boolean isInZygote) {
|
|
||||||
try {
|
|
||||||
// FIXME some coredomain app can't reading modules.list
|
|
||||||
XposedInit.loadModules(isInZygote);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
Utils.logE("error loading module list", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startBootstrapHook(boolean isSystem) {
|
|
||||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
|
||||||
ClassLoader classLoader = XposedBridge.BOOTCLASSLOADER;
|
|
||||||
if (isSystem) {
|
|
||||||
if (useSandHook) {
|
|
||||||
XposedCompat.addHookers(classLoader, SysBootstrapHookInfo.hookItems);
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
SysBootstrapHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (useSandHook) {
|
|
||||||
XposedCompat.addHookers(classLoader, AppBootstrapHookInfo.hookItems);
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
AppBootstrapHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startSystemServerHook() {
|
|
||||||
if (useSandHook) {
|
|
||||||
XposedCompat.addHookers(SystemMainHooker.systemServerCL, SysInnerHookInfo.hookItems);
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
SystemMainHooker.systemServerCL,
|
|
||||||
SysInnerHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startWorkAroundHook() {
|
|
||||||
if (useSandHook) {
|
|
||||||
XposedCompat.addHookers(XposedBridge.BOOTCLASSLOADER, WorkAroundHookInfo.hookItems);
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
XposedBridge.BOOTCLASSLOADER,
|
|
||||||
WorkAroundHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onForkStart() {
|
|
||||||
forkCompleted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onForkFinish() {
|
|
||||||
forkCompleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onEnterChildProcess() {
|
|
||||||
DynamicBridge.onForkPost();
|
|
||||||
//enable compile in child process
|
|
||||||
//SandHook.enableCompiler(!XposedInit.startsSystemServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logD(String prefix) {
|
|
||||||
Utils.logD(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
|
||||||
AndroidAppHelper.currentProcessName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logE(String prefix, Throwable throwable) {
|
|
||||||
Utils.logE(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
|
||||||
AndroidAppHelper.currentProcessName()), throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void injectConfig() {
|
|
||||||
EdXpConfigGlobal.sConfig = new SandHookEdxpConfig();
|
|
||||||
EdXpConfigGlobal.sHookProvider = new SandHookProvider();
|
|
||||||
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.sandhook.entry;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.HandleBindAppHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.HandleBindAppHooker;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.LoadedApkConstructorHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.LoadedApkConstructorHooker;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.OnePlusWorkAroundHooker;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.SystemMainHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.SystemMainHooker;
|
||||||
|
|
||||||
public class SysBootstrapHookInfo implements KeepMembers {
|
public class SysBootstrapHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.sandhook.entry;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.StartBootstrapServicesHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.StartBootstrapServicesHooker;
|
||||||
|
|
||||||
public class SysInnerHookInfo implements KeepMembers {
|
public class SysInnerHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap;
|
package com.elderdrivers.riru.edxp.sandhook.entry;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
|
import com.elderdrivers.riru.edxp.sandhook.hooker.OnePlusWorkAroundHooker;
|
||||||
|
|
||||||
public class WorkAroundHookInfo implements KeepMembers {
|
public class WorkAroundHookInfo implements KeepMembers {
|
||||||
public static String[] hookItemNames = {
|
public static String[] hookItemNames = {
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.elderdrivers.riru.edxp.sandhook.hooker;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.HandleBindApp;
|
||||||
|
import com.swift.sandhook.SandHook;
|
||||||
|
import com.swift.sandhook.annotation.HookClass;
|
||||||
|
import com.swift.sandhook.annotation.HookMethod;
|
||||||
|
import com.swift.sandhook.annotation.HookMethodBackup;
|
||||||
|
import com.swift.sandhook.annotation.Param;
|
||||||
|
import com.swift.sandhook.annotation.SkipParamCheck;
|
||||||
|
import com.swift.sandhook.annotation.ThisObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
@HookClass(ActivityThread.class)
|
||||||
|
public class HandleBindAppHooker implements KeepMembers {
|
||||||
|
|
||||||
|
public static String className = "android.app.ActivityThread";
|
||||||
|
public static String methodName = "handleBindApplication";
|
||||||
|
public static String methodSig = "(Landroid/app/ActivityThread$AppBindData;)V";
|
||||||
|
|
||||||
|
@HookMethodBackup("handleBindApplication")
|
||||||
|
@SkipParamCheck
|
||||||
|
static Method backup;
|
||||||
|
|
||||||
|
@HookMethod("handleBindApplication")
|
||||||
|
public static void hook(@ThisObject ActivityThread thiz, @Param("android.app.ActivityThread$AppBindData") Object bindData) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new HandleBindApp();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = thiz;
|
||||||
|
param.args = new Object[]{bindData};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
backup(thiz, bindData);
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backup(Object thiz, Object bindData) throws Throwable {
|
||||||
|
SandHook.callOriginByBackup(backup, thiz, bindData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.elderdrivers.riru.edxp.sandhook.hooker;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
|
import android.app.LoadedApk;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.res.CompatibilityInfo;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.LoadedApkCstr;
|
||||||
|
import com.swift.sandhook.SandHook;
|
||||||
|
import com.swift.sandhook.annotation.HookClass;
|
||||||
|
import com.swift.sandhook.annotation.HookMethod;
|
||||||
|
import com.swift.sandhook.annotation.HookMethodBackup;
|
||||||
|
import com.swift.sandhook.annotation.SkipParamCheck;
|
||||||
|
import com.swift.sandhook.annotation.ThisObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
@HookClass(LoadedApk.class)
|
||||||
|
public class LoadedApkConstructorHooker implements KeepMembers {
|
||||||
|
public static String className = "android.app.LoadedApk";
|
||||||
|
public static String methodName = "<init>";
|
||||||
|
public static String methodSig = "(Landroid/app/ActivityThread;" +
|
||||||
|
"Landroid/content/pm/ApplicationInfo;" +
|
||||||
|
"Landroid/content/res/CompatibilityInfo;" +
|
||||||
|
"Ljava/lang/ClassLoader;ZZZ)V";
|
||||||
|
|
||||||
|
@HookMethodBackup
|
||||||
|
@SkipParamCheck
|
||||||
|
static Method backup;
|
||||||
|
|
||||||
|
@HookMethod
|
||||||
|
public static void hook(@ThisObject Object thiz, ActivityThread activityThread,
|
||||||
|
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
||||||
|
ClassLoader baseLoader, boolean securityViolation,
|
||||||
|
boolean includeCode, boolean registerPackage) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new LoadedApkCstr();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = thiz;
|
||||||
|
param.args = new Object[]{activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
||||||
|
includeCode, registerPackage};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
||||||
|
includeCode, registerPackage);
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backup(Object thiz, ActivityThread activityThread,
|
||||||
|
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
||||||
|
ClassLoader baseLoader, boolean securityViolation,
|
||||||
|
boolean includeCode, boolean registerPackage) throws Throwable {
|
||||||
|
SandHook.callOriginByBackup(backup, thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation, includeCode, registerPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.elderdrivers.riru.edxp.sandhook.hooker;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
|
||||||
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
|
import com.swift.sandhook.SandHook;
|
||||||
|
import com.swift.sandhook.annotation.HookClass;
|
||||||
|
import com.swift.sandhook.annotation.HookMethod;
|
||||||
|
import com.swift.sandhook.annotation.HookMethodBackup;
|
||||||
|
import com.swift.sandhook.annotation.SkipParamCheck;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import dalvik.system.BaseDexClassLoader;
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
@HookClass(BaseDexClassLoader.class)
|
||||||
|
public class OnePlusWorkAroundHooker implements KeepMembers {
|
||||||
|
|
||||||
|
static {
|
||||||
|
HookMain.addHookItemWhiteList(OnePlusWorkAroundHooker.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String className = "dalvik.system.BaseDexClassLoader";
|
||||||
|
public static String methodName = "inCompatConfigList";
|
||||||
|
public static String methodSig = "(ILjava/lang/String;)Z";
|
||||||
|
|
||||||
|
@HookMethodBackup("inCompatConfigList")
|
||||||
|
@SkipParamCheck
|
||||||
|
static Method backup;
|
||||||
|
|
||||||
|
@HookMethod("inCompatConfigList")
|
||||||
|
public static boolean hook(int type, String packageName) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new OneplusWorkaround();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = null;
|
||||||
|
param.args = new Object[]{type, packageName};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
param.setResult(backup(type, packageName));
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
return (boolean) param.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean backup(int type, String packageName) throws Throwable {
|
||||||
|
return (boolean) SandHook.callOriginByBackup(backup, null, type, packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.elderdrivers.riru.edxp.sandhook.hooker;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
|
import com.elderdrivers.riru.edxp._hooker.impl.StartBootstrapServices;
|
||||||
|
import com.swift.sandhook.SandHook;
|
||||||
|
import com.swift.sandhook.annotation.HookMethod;
|
||||||
|
import com.swift.sandhook.annotation.HookMethodBackup;
|
||||||
|
import com.swift.sandhook.annotation.HookReflectClass;
|
||||||
|
import com.swift.sandhook.annotation.SkipParamCheck;
|
||||||
|
import com.swift.sandhook.annotation.ThisObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
@HookReflectClass("com.android.server.SystemServer")
|
||||||
|
public class StartBootstrapServicesHooker implements KeepMembers {
|
||||||
|
public static String className = "com.android.server.SystemServer";
|
||||||
|
public static String methodName = "startBootstrapServices";
|
||||||
|
public static String methodSig = "()V";
|
||||||
|
|
||||||
|
@HookMethodBackup("startBootstrapServices")
|
||||||
|
@SkipParamCheck
|
||||||
|
static Method backup;
|
||||||
|
|
||||||
|
@HookMethod("startBootstrapServices")
|
||||||
|
public static void hook(@ThisObject Object systemServer) throws Throwable {
|
||||||
|
final XC_MethodHook methodHook = new StartBootstrapServices();
|
||||||
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = systemServer;
|
||||||
|
param.args = new Object[]{};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
backup(systemServer);
|
||||||
|
}
|
||||||
|
methodHook.callAfterHookedMethod(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backup(Object systemServer) throws Throwable {
|
||||||
|
SandHook.callOriginByBackup(backup, systemServer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook._hooker;
|
package com.elderdrivers.riru.edxp.sandhook.hooker;
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.swift.sandhook.SandHook;
|
import com.swift.sandhook.SandHook;
|
||||||
import com.swift.sandhook.annotation.HookClass;
|
import com.swift.sandhook.annotation.HookClass;
|
||||||
import com.swift.sandhook.annotation.HookMethod;
|
import com.swift.sandhook.annotation.HookMethod;
|
||||||
|
|
@ -12,7 +11,7 @@ import com.swift.sandhook.annotation.HookMethodBackup;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
||||||
|
|
||||||
// system_server initialization
|
// system_server initialization
|
||||||
|
|
@ -31,21 +30,16 @@ public class SystemMainHooker implements KeepMembers {
|
||||||
|
|
||||||
@HookMethod("systemMain")
|
@HookMethod("systemMain")
|
||||||
public static ActivityThread hook() throws Throwable {
|
public static ActivityThread hook() throws Throwable {
|
||||||
if (XposedBridge.disableHooks) {
|
final XC_MethodHook methodHook = new OneplusWorkaround();
|
||||||
return backup();
|
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
param.thisObject = null;
|
||||||
|
param.args = new Object[]{};
|
||||||
|
methodHook.callBeforeHookedMethod(param);
|
||||||
|
if (!param.returnEarly) {
|
||||||
|
param.setResult(backup());
|
||||||
}
|
}
|
||||||
Router.logD("ActivityThread#systemMain() starts");
|
methodHook.callAfterHookedMethod(param);
|
||||||
ActivityThread activityThread = backup();
|
return (ActivityThread) param.getResult();
|
||||||
try {
|
|
||||||
// get system_server classLoader
|
|
||||||
systemServerCL = Thread.currentThread().getContextClassLoader();
|
|
||||||
// deopt methods in SYSTEMSERVERCLASSPATH
|
|
||||||
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
|
||||||
Router.startSystemServerHook();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking systemMain", t);
|
|
||||||
}
|
|
||||||
return activityThread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ActivityThread backup() throws Throwable {
|
public static ActivityThread backup() throws Throwable {
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.proxy;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
import com.elderdrivers.riru.edxp.util.ProcessUtils;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Non dynamic mode
|
|
||||||
* - system_server is whitelisted
|
|
||||||
* * for all child processes of main zygote
|
|
||||||
* What've been done in main zygote pre-forking system_server
|
|
||||||
* 1) non dynamic flag set (no need to reset)
|
|
||||||
* 2) boot image methods deopted (no need to redo)
|
|
||||||
* 3) startSystemServer flag set to true (need to reset)
|
|
||||||
* 4) workaround hooks installed (need to redo)
|
|
||||||
* 5) module list loaded and initZygote called (no need to redo)
|
|
||||||
* 6) close all fds (no need to redo because of 5))
|
|
||||||
* * for all child processes of secondary zygote
|
|
||||||
* 1) do the same things pre-forking first child process
|
|
||||||
* - system_server is blacklisted:
|
|
||||||
* * for all child processes of both main zygote and secondary zygote
|
|
||||||
* 1) do the same things pre-forking first child process
|
|
||||||
* 2. Dynamic mode:
|
|
||||||
* to be continued
|
|
||||||
*/
|
|
||||||
public class BlackWhiteListProxy {
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
// should never happen
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// only enter here when isDynamicModulesMode is off
|
|
||||||
onForkPreForNonDynamicMode(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
onForkPostCommon(false, appDataDir, niceName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, long permittedCapabilities,
|
|
||||||
long effectiveCapabilities) {
|
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
// should never happen
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// only enter here when isDynamicModulesMode is off
|
|
||||||
onForkPreForNonDynamicMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
onForkPostCommon(true, getDataPathPrefix() + "android", "system_server");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some details are different between main zygote and secondary zygote.
|
|
||||||
*/
|
|
||||||
private static void onForkPreForNonDynamicMode(boolean isSystemServer) {
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// set startsSystemServer flag used when loadModules
|
|
||||||
Router.prepare(isSystemServer);
|
|
||||||
// deoptBootMethods once for all child processes of zygote
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
|
||||||
// we never install bootstrap hooks here in black/white list mode except workaround hooks
|
|
||||||
// because installed hooks would be propagated to all child processes of zygote
|
|
||||||
Router.startWorkAroundHook();
|
|
||||||
// loadModules once for all child processes of zygote
|
|
||||||
// TODO maybe just save initZygote callbacks and call them when whitelisted process forked?
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
|
||||||
Main.setAppDataDir(appDataDir);
|
|
||||||
Main.niceName = niceName;
|
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
|
||||||
if (!isDynamicModulesMode) {
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
}
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
if (!checkNeedHook(appDataDir, niceName)) {
|
|
||||||
// if is blacklisted, just stop here
|
|
||||||
Router.onForkFinish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
Router.initResourcesHook();
|
|
||||||
}
|
|
||||||
Router.prepare(isSystemServer);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
|
||||||
Router.installBootstrapHooks(isSystemServer);
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
}
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean checkNeedHook(String appDataDir, String niceName) {
|
|
||||||
boolean needHook;
|
|
||||||
if (TextUtils.isEmpty(appDataDir)) {
|
|
||||||
Utils.logE("niceName:" + niceName + ", procName:"
|
|
||||||
+ ProcessUtils.getCurrentProcessName(Main.appProcessName) + ", appDataDir is null, blacklisted!");
|
|
||||||
needHook = false;
|
|
||||||
} else {
|
|
||||||
// FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth
|
|
||||||
needHook = ConfigManager.isAppNeedHook(appDataDir);
|
|
||||||
}
|
|
||||||
if (!needHook) {
|
|
||||||
// clean up the scene
|
|
||||||
onBlackListed();
|
|
||||||
}
|
|
||||||
return needHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onBlackListed() {
|
|
||||||
XposedBridge.clearLoadedPackages();
|
|
||||||
XposedBridge.clearInitPackageResources();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.sandhook.proxy;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
|
||||||
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
|
||||||
|
|
||||||
public class NormalProxy {
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
// mainly for secondary zygote
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// call this to ensure the flag is set to false ASAP
|
|
||||||
Router.prepare(false);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
|
||||||
// install bootstrap hooks for secondary zygote
|
|
||||||
Router.installBootstrapHooks(false);
|
|
||||||
// only load modules for secondary zygote
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
// TODO consider processes without forkAndSpecializePost called
|
|
||||||
Main.setAppDataDir(appDataDir);
|
|
||||||
Main.niceName = niceName;
|
|
||||||
Router.prepare(false);
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
// load modules for each app process on its forked if dynamic modules mode is on
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
|
||||||
long permittedCapabilities, long effectiveCapabilities) {
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// set startsSystemServer flag used when loadModules
|
|
||||||
Router.prepare(true);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote
|
|
||||||
// install bootstrap hooks for main zygote as early as possible
|
|
||||||
// in case we miss some processes not forked via forkAndSpecialize
|
|
||||||
// for instance com.android.phone
|
|
||||||
Router.installBootstrapHooks(true);
|
|
||||||
// loadModules have to be executed in zygote even isDynamicModules is false
|
|
||||||
// because if not global hooks installed in initZygote might not be
|
|
||||||
// propagated to processes not forked via forkAndSpecialize
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
// in system_server process
|
|
||||||
Main.setAppDataDir(getDataPathPrefix() + "android");
|
|
||||||
Main.niceName = "system_server";
|
|
||||||
Router.prepare(true);
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
// reload module list if dynamic mode is on
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.swift.sandhook.SandHook;
|
import com.swift.sandhook.SandHook;
|
||||||
import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader;
|
import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader;
|
||||||
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
|
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
|
||||||
|
|
@ -21,8 +22,6 @@ import static com.swift.sandhook.xposedcompat.utils.FileUtils.IS_USING_PROTECTED
|
||||||
|
|
||||||
public class XposedCompat {
|
public class XposedCompat {
|
||||||
|
|
||||||
public static volatile String appDataDir;
|
|
||||||
|
|
||||||
// TODO initialize these variables
|
// TODO initialize these variables
|
||||||
public static volatile File cacheDir;
|
public static volatile File cacheDir;
|
||||||
public static volatile ClassLoader classLoader;
|
public static volatile ClassLoader classLoader;
|
||||||
|
|
@ -37,16 +36,17 @@ public class XposedCompat {
|
||||||
public static void addHookers(ClassLoader classLoader, Class[] hookers) {
|
public static void addHookers(ClassLoader classLoader, Class[] hookers) {
|
||||||
if (hookers == null)
|
if (hookers == null)
|
||||||
return;
|
return;
|
||||||
for (Class hooker:hookers) {
|
for (Class hooker : hookers) {
|
||||||
try {
|
try {
|
||||||
SandHook.addHookClass(classLoader, hooker);
|
SandHook.addHookClass(classLoader, hooker);
|
||||||
} catch (Throwable throwable) {}
|
} catch (Throwable throwable) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getCacheDir() {
|
public static File getCacheDir() {
|
||||||
if (cacheDir == null) {
|
if (cacheDir == null) {
|
||||||
String fixedAppDataDir = getDataPathPrefix() + getPackageName(appDataDir) + "/";
|
String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/";
|
||||||
cacheDir = new File(fixedAppDataDir, "/cache/sandhook/"
|
cacheDir = new File(fixedAppDataDir, "/cache/sandhook/"
|
||||||
+ ProcessUtils.getProcessName().replace(":", "_") + "/");
|
+ ProcessUtils.getProcessName().replace(":", "_") + "/");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.elderdrivers.riru.edxp.sandhook.core.SandHookEdxpImpl
|
||||||
|
|
@ -12,6 +12,7 @@ android {
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
multiDexEnabled true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
@ -60,7 +61,8 @@ afterEvaluate {
|
||||||
|
|
||||||
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
||||||
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
||||||
from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/"
|
from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/",
|
||||||
|
"${projectDir}/src/main/resources/"
|
||||||
destinationDir file(myTemplatePath + "system/framework/")
|
destinationDir file(myTemplatePath + "system/framework/")
|
||||||
baseName "edxp"
|
baseName "edxp"
|
||||||
doLast {
|
doLast {
|
||||||
|
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Process;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepAll;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.core.HookMethodResolver;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.proxy.BlackWhiteListProxy;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.proxy.NormalProxy;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
public class Main implements KeepAll {
|
|
||||||
|
|
||||||
public static String appDataDir = "";
|
|
||||||
public static String niceName = "";
|
|
||||||
public static String appProcessName = "";
|
|
||||||
private static String forkAndSpecializePramsStr = "";
|
|
||||||
private static String forkSystemServerPramsStr = "";
|
|
||||||
|
|
||||||
static {
|
|
||||||
Yahfa.init(Build.VERSION.SDK_INT);
|
|
||||||
HookMethodResolver.init();
|
|
||||||
Router.injectConfig();
|
|
||||||
InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName());
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// entry points
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
forkAndSpecializePramsStr = String.format(
|
|
||||||
"Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)",
|
|
||||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
|
||||||
mountExternal, seInfo, niceName, Arrays.toString(fdsToClose),
|
|
||||||
Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir);
|
|
||||||
}
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
|
|
||||||
mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote,
|
|
||||||
instructionSet, appDataDir);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
|
|
||||||
seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet,
|
|
||||||
appDataDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
if (pid == 0) {
|
|
||||||
Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid());
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in zygote process, res is child zygote pid
|
|
||||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
|
||||||
long permittedCapabilities, long effectiveCapabilities) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)",
|
|
||||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
|
||||||
permittedCapabilities, effectiveCapabilities);
|
|
||||||
}
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
|
||||||
permittedCapabilities, effectiveCapabilities);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
|
||||||
permittedCapabilities, effectiveCapabilities);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
if (pid == 0) {
|
|
||||||
Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid());
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkSystemServerPost(pid);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkSystemServerPost(pid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in zygote process, res is child zygote pid
|
|
||||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.AndroidAppHelper;
|
|
||||||
import android.app.LoadedApk;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
|
|
||||||
// when a package is loaded for an existing process, trigger the callbacks as well
|
|
||||||
// ed: remove resources related hooking
|
|
||||||
public class LoadedApkConstructorHooker implements KeepMembers {
|
|
||||||
public static String className = "android.app.LoadedApk";
|
|
||||||
public static String methodName = "<init>";
|
|
||||||
public static String methodSig = "(Landroid/app/ActivityThread;" +
|
|
||||||
"Landroid/content/pm/ApplicationInfo;" +
|
|
||||||
"Landroid/content/res/CompatibilityInfo;" +
|
|
||||||
"Ljava/lang/ClassLoader;ZZZ)V";
|
|
||||||
|
|
||||||
public static void hook(Object thiz, ActivityThread activityThread,
|
|
||||||
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
|
||||||
ClassLoader baseLoader, boolean securityViolation,
|
|
||||||
boolean includeCode, boolean registerPackage) {
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
|
||||||
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
|
||||||
includeCode, registerPackage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Router.logD("LoadedApk#<init> starts");
|
|
||||||
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
|
||||||
includeCode, registerPackage);
|
|
||||||
|
|
||||||
try {
|
|
||||||
LoadedApk loadedApk = (LoadedApk) thiz;
|
|
||||||
String packageName = loadedApk.getPackageName();
|
|
||||||
Object mAppDir = XposedHelpers.getObjectField(thiz, "mAppDir");
|
|
||||||
Router.logD("LoadedApk#<init> ends: " + mAppDir);
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks(packageName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageName.equals("android")) {
|
|
||||||
Router.logD("LoadedApk#<init> is android, skip: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mIncludeCode checking should go ahead of loadedPackagesInProcess added checking
|
|
||||||
if (!XposedHelpers.getBooleanField(loadedApk, "mIncludeCode")) {
|
|
||||||
Router.logD("LoadedApk#<init> mIncludeCode == false: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!XposedInit.loadedPackagesInProcess.add(packageName)) {
|
|
||||||
Router.logD("LoadedApk#<init> has been loaded before, skip: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnePlus magic...
|
|
||||||
if (Log.getStackTraceString(new Throwable()).
|
|
||||||
contains("android.app.ActivityThread$ApplicationThread.schedulePreload")) {
|
|
||||||
Router.logD("LoadedApk#<init> maybe oneplus's custom opt, skip");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceParentClassLoader(loadedApk.getClassLoader());
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = packageName;
|
|
||||||
lpparam.processName = AndroidAppHelper.currentProcessName();
|
|
||||||
lpparam.classLoader = loadedApk.getClassLoader();
|
|
||||||
lpparam.appInfo = loadedApk.getApplicationInfo();
|
|
||||||
lpparam.isFirstApplication = false;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking LoadedApk.<init>", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object thiz, ActivityThread activityThread,
|
|
||||||
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
|
||||||
ClassLoader baseLoader, boolean securityViolation,
|
|
||||||
boolean includeCode, boolean registerPackage) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale._hooker;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On OnePlus stock roms (Android Pie), {@link dalvik.system.BaseDexClassLoader#findClass(String)}
|
|
||||||
* will open /dev/binder to communicate with PackageManagerService to check whether
|
|
||||||
* current package name inCompatConfigList, which is an OnePlus OEM feature enabled only when
|
|
||||||
* system prop "persist.sys.oem.region" set to "CN".(detail of related source code:
|
|
||||||
* https://gist.github.com/solohsu/ecc07141759958fc096ba0781fac0a5f)
|
|
||||||
* If we invoke intZygoteCallbacks in
|
|
||||||
* {@link Main#forkAndSpecializePre}, where in zygote process,
|
|
||||||
* we would get a chance to invoke findclass, leaving fd of /dev/binder open in zygote process,
|
|
||||||
* which is not allowed because /dev/binder is not in predefined whitelist here:
|
|
||||||
* http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/jni/fd_utils.cpp#35
|
|
||||||
* So we just hook BaseDexClassLoader#inCompatConfigList to return false to prevent
|
|
||||||
* open of /dev/binder and we haven't found side effects yet.
|
|
||||||
* Other roms might share the same problems but not reported too.
|
|
||||||
*/
|
|
||||||
public class OnePlusWorkAroundHooker implements KeepMembers {
|
|
||||||
|
|
||||||
public static String className = "dalvik.system.BaseDexClassLoader";
|
|
||||||
public static String methodName = "inCompatConfigList";
|
|
||||||
public static String methodSig = "(ILjava/lang/String;)Z";
|
|
||||||
|
|
||||||
public static boolean hook(int type, String packageName) {
|
|
||||||
if (XposedBridge.disableHooks || Router.forkCompleted) {
|
|
||||||
return backup(type, packageName);
|
|
||||||
}
|
|
||||||
Router.logD("BaseDexClassLoader#inCompatConfigList() starts");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean backup(int type, String packageName) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale._hooker;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XC_MethodReplacement;
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
import static com.elderdrivers.riru.edxp.util.Utils.logD;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
|
||||||
|
|
||||||
public class StartBootstrapServicesHooker implements KeepMembers {
|
|
||||||
public static String className = "com.android.server.SystemServer";
|
|
||||||
public static String methodName = "startBootstrapServices";
|
|
||||||
public static String methodSig = "()V";
|
|
||||||
|
|
||||||
public static void hook(Object systemServer) {
|
|
||||||
|
|
||||||
if (XposedBridge.disableHooks) {
|
|
||||||
backup(systemServer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logD("SystemServer#startBootstrapServices() starts");
|
|
||||||
|
|
||||||
try {
|
|
||||||
XposedInit.loadedPackagesInProcess.add("android");
|
|
||||||
|
|
||||||
replaceParentClassLoader(SystemMainHooker.systemServerCL);
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = "android";
|
|
||||||
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
|
|
||||||
lpparam.classLoader = SystemMainHooker.systemServerCL;
|
|
||||||
lpparam.appInfo = null;
|
|
||||||
lpparam.isFirstApplication = true;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
|
|
||||||
// Huawei
|
|
||||||
try {
|
|
||||||
findAndHookMethod("com.android.server.pm.HwPackageManagerService", SystemMainHooker.systemServerCL, "isOdexMode", XC_MethodReplacement.returnConstant(false));
|
|
||||||
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
|
|
||||||
findAndHookMethod(className, SystemMainHooker.systemServerCL, "dexEntryExists", String.class, XC_MethodReplacement.returnConstant(true));
|
|
||||||
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking startBootstrapServices", t);
|
|
||||||
} finally {
|
|
||||||
backup(systemServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object systemServer) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
|
|
||||||
// system_server initialization
|
|
||||||
// ed: only support sdk >= 21 for now
|
|
||||||
public class SystemMainHooker implements KeepMembers {
|
|
||||||
|
|
||||||
public static String className = "android.app.ActivityThread";
|
|
||||||
public static String methodName = "systemMain";
|
|
||||||
public static String methodSig = "()Landroid/app/ActivityThread;";
|
|
||||||
|
|
||||||
public static ClassLoader systemServerCL;
|
|
||||||
|
|
||||||
public static ActivityThread hook() {
|
|
||||||
if (XposedBridge.disableHooks) {
|
|
||||||
return backup();
|
|
||||||
}
|
|
||||||
Router.logD("ActivityThread#systemMain() starts");
|
|
||||||
ActivityThread activityThread = backup();
|
|
||||||
try {
|
|
||||||
// get system_server classLoader
|
|
||||||
systemServerCL = Thread.currentThread().getContextClassLoader();
|
|
||||||
// deopt methods in SYSTEMSERVERCLASSPATH
|
|
||||||
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
|
||||||
Router.startSystemServerHook();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking systemMain", t);
|
|
||||||
}
|
|
||||||
return activityThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ActivityThread backup() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.config;
|
package com.elderdrivers.riru.edxp.whale.config;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfig;
|
import com.elderdrivers.riru.edxp.config.EdXpConfig;
|
||||||
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.config;
|
package com.elderdrivers.riru.edxp.whale.config;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.art.ClassLinker;
|
import com.elderdrivers.riru.edxp.art.ClassLinker;
|
||||||
import com.elderdrivers.riru.edxp.config.BaseHookProvider;
|
import com.elderdrivers.riru.edxp.config.BaseHookProvider;
|
||||||
import com.elderdrivers.riru.edxp.core.ResourcesHook;
|
import com.elderdrivers.riru.edxp.core.ResourcesHook;
|
||||||
|
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.core;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.art.Heap;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.OnePlusWorkAroundHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
|
|
||||||
public class HookMain {
|
|
||||||
|
|
||||||
private static Set<String> hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName());
|
|
||||||
|
|
||||||
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) {
|
|
||||||
try {
|
|
||||||
Class<?> hookInfoClass = Class.forName(hookInfoClassName, true, patchClassLoader);
|
|
||||||
String[] hookItemNames = (String[]) hookInfoClass.getField("hookItemNames").get(null);
|
|
||||||
for (String hookItemName : hookItemNames) {
|
|
||||||
doHookItemDefault(patchClassLoader, hookItemName, originClassLoader);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Utils.logE("error when hooking all in: " + hookInfoClassName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doHookItemDefault(ClassLoader patchClassLoader, String hookItemName, ClassLoader originClassLoader) {
|
|
||||||
try {
|
|
||||||
Utils.logD("Start hooking with item " + hookItemName);
|
|
||||||
Class<?> hookItem = Class.forName(hookItemName, true, patchClassLoader);
|
|
||||||
|
|
||||||
String className = (String) hookItem.getField("className").get(null);
|
|
||||||
String methodName = (String) hookItem.getField("methodName").get(null);
|
|
||||||
String methodSig = (String) hookItem.getField("methodSig").get(null);
|
|
||||||
|
|
||||||
if (className == null || className.equals("")) {
|
|
||||||
Utils.logW("No target class. Skipping...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Class<?> clazz = null;
|
|
||||||
try {
|
|
||||||
clazz = Class.forName(className, true, originClassLoader);
|
|
||||||
} catch (ClassNotFoundException cnfe) {
|
|
||||||
Utils.logE(className + " not found in " + originClassLoader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Modifier.isAbstract(clazz.getModifiers())) {
|
|
||||||
Utils.logW("Hook may fail for abstract class: " + className);
|
|
||||||
}
|
|
||||||
|
|
||||||
Method hook = null;
|
|
||||||
Method backup = null;
|
|
||||||
for (Method method : hookItem.getDeclaredMethods()) {
|
|
||||||
if (method.getName().equals("hook") && Modifier.isStatic(method.getModifiers())) {
|
|
||||||
hook = method;
|
|
||||||
} else if (method.getName().equals("backup") && Modifier.isStatic(method.getModifiers())) {
|
|
||||||
backup = method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hook == null) {
|
|
||||||
Utils.logE("Cannot find hook for " + methodName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
findAndBackupAndHook(clazz, methodName, methodSig, hook, backup);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!hookItemWhiteList.contains(hookItemName)) {
|
|
||||||
Utils.logE("error when hooking " + hookItemName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void findAndHook(Class targetClass, String methodName, String methodSig, Method hook) {
|
|
||||||
hook(findMethod(targetClass, methodName, methodSig), hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void findAndBackupAndHook(Class targetClass, String methodName, String methodSig,
|
|
||||||
Method hook, Method backup) {
|
|
||||||
backupAndHook(findMethod(targetClass, methodName, methodSig), hook, backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void hook(Object target, Method hook) {
|
|
||||||
backupAndHook(target, hook, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backupAndHook(Object target, Method hook, Method backup) {
|
|
||||||
Utils.logD(String.format("target=%s, hook=%s, backup=%s", target, hook, backup));
|
|
||||||
if (target == null) {
|
|
||||||
throw new IllegalArgumentException("null target method");
|
|
||||||
}
|
|
||||||
if (hook == null) {
|
|
||||||
throw new IllegalArgumentException("null hook method");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Modifier.isStatic(hook.getModifiers())) {
|
|
||||||
throw new IllegalArgumentException("Hook must be a static method: " + hook);
|
|
||||||
}
|
|
||||||
checkCompatibleMethods(target, hook, "Original", "Hook");
|
|
||||||
if (backup != null) {
|
|
||||||
if (!Modifier.isStatic(backup.getModifiers())) {
|
|
||||||
throw new IllegalArgumentException("Backup must be a static method: " + backup);
|
|
||||||
}
|
|
||||||
// backup is just a placeholder and the constraint could be less strict
|
|
||||||
checkCompatibleMethods(target, backup, "Original", "Backup");
|
|
||||||
}
|
|
||||||
if (backup != null) {
|
|
||||||
HookMethodResolver.resolveMethod(hook, backup);
|
|
||||||
}
|
|
||||||
// make sure GC completed before hook
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
int lastGcType = Heap.waitForGcToComplete(
|
|
||||||
XposedHelpers.getLongField(currentThread, "nativePeer"));
|
|
||||||
if (lastGcType < 0) {
|
|
||||||
Utils.logW("waitForGcToComplete failed, using fallback");
|
|
||||||
Runtime.getRuntime().gc();
|
|
||||||
}
|
|
||||||
if (!Yahfa.backupAndHookNative(target, hook, backup)) {
|
|
||||||
throw new RuntimeException("Failed to hook " + target + " with " + hook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object findMethod(Class cls, String methodName, String methodSig) {
|
|
||||||
if (cls == null) {
|
|
||||||
throw new IllegalArgumentException("null class");
|
|
||||||
}
|
|
||||||
if (methodName == null) {
|
|
||||||
throw new IllegalArgumentException("null method name");
|
|
||||||
}
|
|
||||||
if (methodSig == null) {
|
|
||||||
throw new IllegalArgumentException("null method signature");
|
|
||||||
}
|
|
||||||
return Yahfa.findMethodNative(cls, methodName, methodSig);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) {
|
|
||||||
ArrayList<Class<?>> originalParams;
|
|
||||||
if (original instanceof Method) {
|
|
||||||
originalParams = new ArrayList<>(Arrays.asList(((Method) original).getParameterTypes()));
|
|
||||||
} else if (original instanceof Constructor) {
|
|
||||||
originalParams = new ArrayList<>(Arrays.asList(((Constructor<?>) original).getParameterTypes()));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Type of target method is wrong");
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Class<?>> replacementParams = new ArrayList<>(Arrays.asList(replacement.getParameterTypes()));
|
|
||||||
|
|
||||||
if (original instanceof Method
|
|
||||||
&& !Modifier.isStatic(((Method) original).getModifiers())) {
|
|
||||||
originalParams.add(0, ((Method) original).getDeclaringClass());
|
|
||||||
} else if (original instanceof Constructor) {
|
|
||||||
originalParams.add(0, ((Constructor<?>) original).getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!Modifier.isStatic(replacement.getModifiers())) {
|
|
||||||
replacementParams.add(0, replacement.getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (original instanceof Method
|
|
||||||
&& !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) {
|
|
||||||
throw new IllegalArgumentException("Incompatible return types. " + originalName + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType());
|
|
||||||
} else if (original instanceof Constructor) {
|
|
||||||
if (replacement.getReturnType().equals(Void.class)) {
|
|
||||||
throw new IllegalArgumentException("Incompatible return types. " + "<init>" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originalParams.size() != replacementParams.size()) {
|
|
||||||
throw new IllegalArgumentException("Number of arguments don't match. " + originalName + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < originalParams.size(); i++) {
|
|
||||||
if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) {
|
|
||||||
throw new IllegalArgumentException("Incompatible argument #" + i + ": " + originalName + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.elderdrivers.riru.edxp.whale.core;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
|
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
||||||
|
import com.elderdrivers.riru.edxp.core.BaseEdxpImpl;
|
||||||
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMethodResolver;
|
||||||
|
|
||||||
|
public class WhaleEdxpImpl extends BaseEdxpImpl {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected com.elderdrivers.riru.edxp.proxy.Router createRouter() {
|
||||||
|
return new WhaleRouter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVariant() {
|
||||||
|
return WHALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
Yahfa.init(Build.VERSION.SDK_INT);
|
||||||
|
HookMethodResolver.init();
|
||||||
|
getRouter().injectConfig();
|
||||||
|
InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName());
|
||||||
|
|
||||||
|
setInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.elderdrivers.riru.edxp.whale.core;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
|
||||||
|
import com.elderdrivers.riru.edxp.whale.config.WhaleEdxpConfig;
|
||||||
|
import com.elderdrivers.riru.edxp.whale.config.WhaleHookProvider;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
public class WhaleRouter extends BaseRouter {
|
||||||
|
|
||||||
|
public void onEnterChildProcess() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectConfig() {
|
||||||
|
EdXpConfigGlobal.sConfig = new WhaleEdxpConfig();
|
||||||
|
EdXpConfigGlobal.sHookProvider = new WhaleHookProvider();
|
||||||
|
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.dexmaker;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.BuildConfig;
|
|
||||||
|
|
||||||
public class DexLog {
|
|
||||||
|
|
||||||
public static final String TAG = "EdXposed-dexmaker";
|
|
||||||
|
|
||||||
public static int v(String s) {
|
|
||||||
return Log.v(TAG, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int i(String s) {
|
|
||||||
return Log.i(TAG, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int d(String s) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
return Log.d(TAG, s);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int w(String s) {
|
|
||||||
return Log.w(TAG, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int e(String s) {
|
|
||||||
return Log.e(TAG, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int e(String s, Throwable t) {
|
|
||||||
return Log.e(TAG, s, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.dexmaker;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Member;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class MethodInfo {
|
|
||||||
|
|
||||||
public String className;
|
|
||||||
public String classDesc;
|
|
||||||
public String methodName;
|
|
||||||
public String methodSig;
|
|
||||||
public Method method;
|
|
||||||
public Constructor constructor;
|
|
||||||
public boolean isConstructor;
|
|
||||||
public ClassLoader classLoader;
|
|
||||||
|
|
||||||
public MethodInfo(Member member) {
|
|
||||||
if (member instanceof Method) {
|
|
||||||
method = (Method) member;
|
|
||||||
isConstructor = false;
|
|
||||||
classLoader = member.getDeclaringClass().getClassLoader();
|
|
||||||
generateMethodInfo();
|
|
||||||
} else if (member instanceof Constructor) {
|
|
||||||
constructor = (Constructor) member;
|
|
||||||
isConstructor = true;
|
|
||||||
classLoader = member.getDeclaringClass().getClassLoader();
|
|
||||||
generateConstructorInfo();
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("member should be Method or Constructor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateConstructorInfo() {
|
|
||||||
methodName = "<init>";
|
|
||||||
className = constructor.getDeclaringClass().getName();
|
|
||||||
generateCommonInfo(constructor.getParameterTypes(), void.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateMethodInfo() {
|
|
||||||
methodName = method.getName();
|
|
||||||
className = method.getDeclaringClass().getName();
|
|
||||||
generateCommonInfo(method.getParameterTypes(), method.getReturnType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateCommonInfo(Class[] parameterTypes, Class returnType) {
|
|
||||||
classDesc = "L" + className.replace(".", "/") + ";";
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
builder.append("(");
|
|
||||||
for (Class parameterType : parameterTypes) {
|
|
||||||
builder.append(getDescStr(parameterType));
|
|
||||||
}
|
|
||||||
builder.append(")");
|
|
||||||
builder.append(getDescStr(returnType));
|
|
||||||
methodSig = builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class getClassForSure() {
|
|
||||||
try {
|
|
||||||
// TODO does initialize make sense?
|
|
||||||
return Class.forName(className, true, classLoader);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
DexLog.e("error when getClassForSure", throwable);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getDescStr(Class clazz) {
|
|
||||||
if (clazz.equals(boolean.class)) {
|
|
||||||
return "Z";
|
|
||||||
} else if (clazz.equals(byte.class)) {
|
|
||||||
return "B";
|
|
||||||
} else if (clazz.equals(char.class)) {
|
|
||||||
return "C";
|
|
||||||
} else if (clazz.equals(double.class)) {
|
|
||||||
return "D";
|
|
||||||
} else if (clazz.equals(float.class)) {
|
|
||||||
return "F";
|
|
||||||
} else if (clazz.equals(int.class)) {
|
|
||||||
return "I";
|
|
||||||
} else if (clazz.equals(long.class)) {
|
|
||||||
return "J";
|
|
||||||
} else if (clazz.equals(short.class)) {
|
|
||||||
return "S";
|
|
||||||
} else if (clazz.equals(void.class)) {
|
|
||||||
return "V";
|
|
||||||
} else {
|
|
||||||
String prefix = clazz.isArray() ? "" : "L";
|
|
||||||
String suffix = clazz.isArray() ? "" : ";";
|
|
||||||
return prefix + clazz.getName().replace(".", "/") + suffix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.entry;
|
|
||||||
|
|
||||||
import android.app.AndroidAppHelper;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.config.WhaleEdxpConfig;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.config.WhaleHookProvider;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.core.HookMain;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.bootstrap.AppBootstrapHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.bootstrap.SysBootstrapHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.bootstrap.SysInnerHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.bootstrap.WorkAroundHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.SystemMainHooker;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
|
|
||||||
public class Router {
|
|
||||||
|
|
||||||
public volatile static boolean forkCompleted = false;
|
|
||||||
|
|
||||||
private static volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
public static void initResourcesHook() {
|
|
||||||
startWorkAroundHook(); // for OnePlus devices
|
|
||||||
XposedBridge.initXResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void prepare(boolean isSystem) {
|
|
||||||
// this flag is needed when loadModules
|
|
||||||
XposedInit.startsSystemServer = isSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkHookState(String appDataDir) {
|
|
||||||
// determine whether allow xposed or not
|
|
||||||
// XposedBridge.disableHooks = ConfigManager.shouldHook(parsePackageName(appDataDir));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String parsePackageName(String appDataDir) {
|
|
||||||
if (TextUtils.isEmpty(appDataDir)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
int lastIndex = appDataDir.lastIndexOf("/");
|
|
||||||
if (lastIndex < 1) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return appDataDir.substring(lastIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void installBootstrapHooks(boolean isSystem) {
|
|
||||||
// Initialize the Xposed framework
|
|
||||||
try {
|
|
||||||
if (!bootstrapHooked.compareAndSet(false, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Router.startBootstrapHook(isSystem);
|
|
||||||
XposedInit.initForZygote(isSystem);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Utils.logE("error during Xposed initialization", t);
|
|
||||||
XposedBridge.disableHooks = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void loadModulesSafely(boolean isInZygote) {
|
|
||||||
try {
|
|
||||||
// FIXME some coredomain app can't reading modules.list
|
|
||||||
XposedInit.loadModules(isInZygote);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
Utils.logE("error loading module list", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startBootstrapHook(boolean isSystem) {
|
|
||||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
|
||||||
ClassLoader classLoader = XposedBridge.BOOTCLASSLOADER;
|
|
||||||
if (isSystem) {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
SysBootstrapHookInfo.class.getName());
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
AppBootstrapHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startSystemServerHook() {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
SystemMainHooker.systemServerCL,
|
|
||||||
SysInnerHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startWorkAroundHook() {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
XposedBridge.BOOTCLASSLOADER,
|
|
||||||
WorkAroundHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onForkStart() {
|
|
||||||
forkCompleted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onForkFinish() {
|
|
||||||
forkCompleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onEnterChildProcess() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logD(String prefix) {
|
|
||||||
Utils.logD(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
|
||||||
AndroidAppHelper.currentProcessName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logE(String prefix, Throwable throwable) {
|
|
||||||
Utils.logE(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
|
||||||
AndroidAppHelper.currentProcessName()), throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void injectConfig() {
|
|
||||||
EdXpConfigGlobal.sConfig = new WhaleEdxpConfig();
|
|
||||||
EdXpConfigGlobal.sHookProvider = new WhaleHookProvider();
|
|
||||||
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.entry.bootstrap;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.HandleBindAppHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.LoadedApkConstructorHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.OnePlusWorkAroundHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.SystemMainHooker;
|
|
||||||
|
|
||||||
public class SysBootstrapHookInfo implements KeepMembers {
|
|
||||||
public static String[] hookItemNames = {
|
|
||||||
HandleBindAppHooker.class.getName(),
|
|
||||||
SystemMainHooker.class.getName(),
|
|
||||||
LoadedApkConstructorHooker.class.getName(),
|
|
||||||
OnePlusWorkAroundHooker.class.getName()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.entry.bootstrap;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.whale._hooker.StartBootstrapServicesHooker;
|
|
||||||
|
|
||||||
public class SysInnerHookInfo implements KeepMembers {
|
|
||||||
public static String[] hookItemNames = {
|
|
||||||
StartBootstrapServicesHooker.class.getName()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.proxy;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
|
||||||
import com.elderdrivers.riru.edxp.whale.entry.Router;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
|
||||||
|
|
||||||
public class NormalProxy {
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
// mainly for secondary zygote
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// call this to ensure the flag is set to false ASAP
|
|
||||||
Router.prepare(false);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
|
||||||
// install bootstrap hooks for secondary zygote
|
|
||||||
Router.installBootstrapHooks(false);
|
|
||||||
// only load modules for secondary zygote
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
// TODO consider processes without forkAndSpecializePost called
|
|
||||||
Main.appDataDir = appDataDir;
|
|
||||||
Main.niceName = niceName;
|
|
||||||
Router.prepare(false);
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
// load modules for each app process on its forked if dynamic modules mode is on
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
|
||||||
long permittedCapabilities, long effectiveCapabilities) {
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// set startsSystemServer flag used when loadModules
|
|
||||||
Router.prepare(true);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote
|
|
||||||
// install bootstrap hooks for main zygote as early as possible
|
|
||||||
// in case we miss some processes not forked via forkAndSpecialize
|
|
||||||
// for instance com.android.phone
|
|
||||||
Router.installBootstrapHooks(true);
|
|
||||||
// loadModules have to be executed in zygote even isDynamicModules is false
|
|
||||||
// because if not global hooks installed in initZygote might not be
|
|
||||||
// propagated to processes not forked via forkAndSpecialize
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
// in system_server process
|
|
||||||
Main.appDataDir = getDataPathPrefix() + "android";
|
|
||||||
Main.niceName = "system_server";
|
|
||||||
Router.prepare(true);
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
// reload module list if dynamic mode is on
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.elderdrivers.riru.edxp.whale.core.WhaleEdxpImpl
|
||||||
|
|
@ -12,6 +12,7 @@ android {
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
multiDexEnabled true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
@ -60,7 +61,8 @@ afterEvaluate {
|
||||||
|
|
||||||
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
||||||
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
||||||
from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/"
|
from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/",
|
||||||
|
"${projectDir}/src/main/resources/"
|
||||||
destinationDir file(myTemplatePath + "system/framework/")
|
destinationDir file(myTemplatePath + "system/framework/")
|
||||||
baseName "edxp"
|
baseName "edxp"
|
||||||
doLast {
|
doLast {
|
||||||
|
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Process;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepAll;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.core.HookMethodResolver;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.proxy.BlackWhiteListProxy;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.proxy.NormalProxy;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
public class Main implements KeepAll {
|
|
||||||
|
|
||||||
public static String appDataDir = "";
|
|
||||||
public static String niceName = "";
|
|
||||||
public static String appProcessName = "";
|
|
||||||
private static String forkAndSpecializePramsStr = "";
|
|
||||||
private static String forkSystemServerPramsStr = "";
|
|
||||||
|
|
||||||
static {
|
|
||||||
Yahfa.init(Build.VERSION.SDK_INT);
|
|
||||||
HookMethodResolver.init();
|
|
||||||
Router.injectConfig();
|
|
||||||
InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName());
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// entry points
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
forkAndSpecializePramsStr = String.format(
|
|
||||||
"Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)",
|
|
||||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
|
||||||
mountExternal, seInfo, niceName, Arrays.toString(fdsToClose),
|
|
||||||
Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir);
|
|
||||||
}
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
|
|
||||||
mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote,
|
|
||||||
instructionSet, appDataDir);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
|
|
||||||
seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet,
|
|
||||||
appDataDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
if (pid == 0) {
|
|
||||||
Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid());
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in zygote process, res is child zygote pid
|
|
||||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
|
||||||
long permittedCapabilities, long effectiveCapabilities) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)",
|
|
||||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
|
||||||
permittedCapabilities, effectiveCapabilities);
|
|
||||||
}
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
|
||||||
permittedCapabilities, effectiveCapabilities);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
|
||||||
permittedCapabilities, effectiveCapabilities);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
if (pid == 0) {
|
|
||||||
Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid());
|
|
||||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
|
||||||
BlackWhiteListProxy.forkSystemServerPost(pid);
|
|
||||||
} else {
|
|
||||||
NormalProxy.forkSystemServerPost(pid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in zygote process, res is child zygote pid
|
|
||||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.LoadedApk;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.SliceProviderFix;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedInstallerHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_PACKAGE_NAME;
|
|
||||||
import static com.elderdrivers.riru.edxp.hooker.SliceProviderFix.SYSTEMUI_PACKAGE_NAME;
|
|
||||||
import static com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker.BLACK_LIST_PACKAGE_NAME;
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
|
|
||||||
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
|
||||||
public class HandleBindAppHooker implements KeepMembers {
|
|
||||||
|
|
||||||
public static String className = "android.app.ActivityThread";
|
|
||||||
public static String methodName = "handleBindApplication";
|
|
||||||
public static String methodSig = "(Landroid/app/ActivityThread$AppBindData;)V";
|
|
||||||
|
|
||||||
public static void hook(Object thiz, Object bindData) {
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
|
||||||
backup(thiz, bindData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Router.logD("ActivityThread#handleBindApplication() starts");
|
|
||||||
ActivityThread activityThread = (ActivityThread) thiz;
|
|
||||||
ApplicationInfo appInfo = (ApplicationInfo) XposedHelpers.getObjectField(bindData, "appInfo");
|
|
||||||
// save app process name here for later use
|
|
||||||
Main.appProcessName = (String) XposedHelpers.getObjectField(bindData, "processName");
|
|
||||||
String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
|
|
||||||
Utils.logD("processName=" + Main.appProcessName +
|
|
||||||
", packageName=" + reportedPackageName + ", appDataDir=" + Main.appDataDir);
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks(reportedPackageName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentName instrumentationName = (ComponentName) XposedHelpers.getObjectField(bindData, "instrumentationName");
|
|
||||||
if (instrumentationName != null) {
|
|
||||||
Router.logD("Instrumentation detected, disabling framework for");
|
|
||||||
XposedBridge.disableHooks = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CompatibilityInfo compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(bindData, "compatInfo");
|
|
||||||
if (appInfo.sourceDir == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
XposedHelpers.setObjectField(activityThread, "mBoundApplication", bindData);
|
|
||||||
XposedInit.loadedPackagesInProcess.add(reportedPackageName);
|
|
||||||
LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
|
|
||||||
|
|
||||||
replaceParentClassLoader(loadedApk.getClassLoader());
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = reportedPackageName;
|
|
||||||
lpparam.processName = (String) XposedHelpers.getObjectField(bindData, "processName");
|
|
||||||
lpparam.classLoader = loadedApk.getClassLoader();
|
|
||||||
lpparam.appInfo = appInfo;
|
|
||||||
lpparam.isFirstApplication = true;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
|
|
||||||
if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME)) {
|
|
||||||
XposedInstallerHooker.hookXposedInstaller(lpparam.classLoader);
|
|
||||||
}
|
|
||||||
if (reportedPackageName.equals(BLACK_LIST_PACKAGE_NAME)) {
|
|
||||||
XposedBlackListHooker.hook(lpparam.classLoader);
|
|
||||||
}
|
|
||||||
if (reportedPackageName.equals(SYSTEMUI_PACKAGE_NAME)) {
|
|
||||||
SliceProviderFix.hook();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking bindApp", t);
|
|
||||||
} finally {
|
|
||||||
backup(thiz, bindData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object thiz, Object bindData) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.AndroidAppHelper;
|
|
||||||
import android.app.LoadedApk;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
|
|
||||||
|
|
||||||
// when a package is loaded for an existing process, trigger the callbacks as well
|
|
||||||
// ed: remove resources related hooking
|
|
||||||
public class LoadedApkConstructorHooker implements KeepMembers {
|
|
||||||
public static String className = "android.app.LoadedApk";
|
|
||||||
public static String methodName = "<init>";
|
|
||||||
public static String methodSig = "(Landroid/app/ActivityThread;" +
|
|
||||||
"Landroid/content/pm/ApplicationInfo;" +
|
|
||||||
"Landroid/content/res/CompatibilityInfo;" +
|
|
||||||
"Ljava/lang/ClassLoader;ZZZ)V";
|
|
||||||
|
|
||||||
public static void hook(Object thiz, ActivityThread activityThread,
|
|
||||||
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
|
||||||
ClassLoader baseLoader, boolean securityViolation,
|
|
||||||
boolean includeCode, boolean registerPackage) {
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks("")) {
|
|
||||||
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
|
||||||
includeCode, registerPackage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Router.logD("LoadedApk#<init> starts");
|
|
||||||
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
|
|
||||||
includeCode, registerPackage);
|
|
||||||
|
|
||||||
try {
|
|
||||||
LoadedApk loadedApk = (LoadedApk) thiz;
|
|
||||||
String packageName = loadedApk.getPackageName();
|
|
||||||
Object mAppDir = XposedHelpers.getObjectField(thiz, "mAppDir");
|
|
||||||
Router.logD("LoadedApk#<init> ends: " + mAppDir);
|
|
||||||
|
|
||||||
if (XposedBlackListHooker.shouldDisableHooks(packageName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageName.equals("android")) {
|
|
||||||
Router.logD("LoadedApk#<init> is android, skip: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mIncludeCode checking should go ahead of loadedPackagesInProcess added checking
|
|
||||||
if (!XposedHelpers.getBooleanField(loadedApk, "mIncludeCode")) {
|
|
||||||
Router.logD("LoadedApk#<init> mIncludeCode == false: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!XposedInit.loadedPackagesInProcess.add(packageName)) {
|
|
||||||
Router.logD("LoadedApk#<init> has been loaded before, skip: " + mAppDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnePlus magic...
|
|
||||||
if (Log.getStackTraceString(new Throwable()).
|
|
||||||
contains("android.app.ActivityThread$ApplicationThread.schedulePreload")) {
|
|
||||||
Router.logD("LoadedApk#<init> maybe oneplus's custom opt, skip");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceParentClassLoader(loadedApk.getClassLoader());
|
|
||||||
|
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
|
|
||||||
lpparam.packageName = packageName;
|
|
||||||
lpparam.processName = AndroidAppHelper.currentProcessName();
|
|
||||||
lpparam.classLoader = loadedApk.getClassLoader();
|
|
||||||
lpparam.appInfo = loadedApk.getApplicationInfo();
|
|
||||||
lpparam.isFirstApplication = false;
|
|
||||||
XC_LoadPackage.callAll(lpparam);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking LoadedApk.<init>", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backup(Object thiz, ActivityThread activityThread,
|
|
||||||
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
|
|
||||||
ClassLoader baseLoader, boolean securityViolation,
|
|
||||||
boolean includeCode, boolean registerPackage) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa._hooker;
|
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
|
|
||||||
// system_server initialization
|
|
||||||
// ed: only support sdk >= 21 for now
|
|
||||||
public class SystemMainHooker implements KeepMembers {
|
|
||||||
|
|
||||||
public static String className = "android.app.ActivityThread";
|
|
||||||
public static String methodName = "systemMain";
|
|
||||||
public static String methodSig = "()Landroid/app/ActivityThread;";
|
|
||||||
|
|
||||||
public static ClassLoader systemServerCL;
|
|
||||||
|
|
||||||
public static ActivityThread hook() {
|
|
||||||
if (XposedBridge.disableHooks) {
|
|
||||||
return backup();
|
|
||||||
}
|
|
||||||
Router.logD("ActivityThread#systemMain() starts");
|
|
||||||
ActivityThread activityThread = backup();
|
|
||||||
try {
|
|
||||||
// get system_server classLoader
|
|
||||||
systemServerCL = Thread.currentThread().getContextClassLoader();
|
|
||||||
// deopt methods in SYSTEMSERVERCLASSPATH
|
|
||||||
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
|
||||||
Router.startSystemServerHook();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Router.logE("error when hooking systemMain", t);
|
|
||||||
}
|
|
||||||
return activityThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ActivityThread backup() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.config;
|
package com.elderdrivers.riru.edxp.yahfa.config;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfig;
|
import com.elderdrivers.riru.edxp.config.EdXpConfig;
|
||||||
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
||||||
|
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.core;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create by Swift Gan on 14/01/2019
|
|
||||||
* To ensure method in resolved cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class HookMethodResolver {
|
|
||||||
|
|
||||||
public static Class artMethodClass;
|
|
||||||
|
|
||||||
public static Field resolvedMethodsField;
|
|
||||||
public static Field dexCacheField;
|
|
||||||
public static Field dexMethodIndexField;
|
|
||||||
public static Field artMethodField;
|
|
||||||
|
|
||||||
public static boolean canResolvedInJava = false;
|
|
||||||
public static boolean isArtMethod = false;
|
|
||||||
|
|
||||||
public static long resolvedMethodsAddress = 0;
|
|
||||||
public static int dexMethodIndex = 0;
|
|
||||||
|
|
||||||
public static Method testMethod;
|
|
||||||
public static Object testArtMethod;
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
checkSupport();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkSupport() {
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
isArtMethod = false;
|
|
||||||
canResolvedInJava = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testMethod = HookMethodResolver.class.getDeclaredMethod("init");
|
|
||||||
artMethodField = getField(Method.class, "artMethod");
|
|
||||||
|
|
||||||
testArtMethod = artMethodField.get(testMethod);
|
|
||||||
|
|
||||||
if (hasJavaArtMethod() && testArtMethod.getClass() == artMethodClass) {
|
|
||||||
checkSupportForArtMethod();
|
|
||||||
isArtMethod = true;
|
|
||||||
} else if (testArtMethod instanceof Long) {
|
|
||||||
checkSupportForArtMethodId();
|
|
||||||
isArtMethod = false;
|
|
||||||
} else {
|
|
||||||
canResolvedInJava = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
Utils.logE("error when checkSupport", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// may 5.0
|
|
||||||
private static void checkSupportForArtMethod() throws Exception {
|
|
||||||
dexMethodIndexField = getField(artMethodClass, "dexMethodIndex");
|
|
||||||
dexCacheField = getField(Class.class, "dexCache");
|
|
||||||
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
|
|
||||||
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
|
|
||||||
if (resolvedMethodsField.get(dexCache) instanceof Object[]) {
|
|
||||||
canResolvedInJava = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// may 6.0
|
|
||||||
private static void checkSupportForArtMethodId() throws Exception {
|
|
||||||
dexMethodIndexField = getField(Method.class, "dexMethodIndex");
|
|
||||||
dexMethodIndex = (int) dexMethodIndexField.get(testMethod);
|
|
||||||
dexCacheField = getField(Class.class, "dexCache");
|
|
||||||
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
|
|
||||||
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
|
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
|
||||||
if (resolvedMethods instanceof Long) {
|
|
||||||
canResolvedInJava = false;
|
|
||||||
resolvedMethodsAddress = (long) resolvedMethods;
|
|
||||||
} else if (resolvedMethods instanceof long[]) {
|
|
||||||
canResolvedInJava = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void resolveMethod(Method hook, Method backup) {
|
|
||||||
if (canResolvedInJava && artMethodField != null) {
|
|
||||||
// in java
|
|
||||||
try {
|
|
||||||
resolveInJava(hook, backup);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// in native
|
|
||||||
resolveInNative(hook, backup);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in native
|
|
||||||
resolveInNative(hook, backup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resolveInJava(Method hook, Method backup) throws Exception {
|
|
||||||
Object dexCache = dexCacheField.get(hook.getDeclaringClass());
|
|
||||||
if (isArtMethod) {
|
|
||||||
Object artMethod = artMethodField.get(backup);
|
|
||||||
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
|
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
|
||||||
((Object[])resolvedMethods)[dexMethodIndex] = artMethod;
|
|
||||||
} else {
|
|
||||||
int dexMethodIndex = (int) dexMethodIndexField.get(backup);
|
|
||||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
|
||||||
long artMethod = (long) artMethodField.get(backup);
|
|
||||||
((long[])resolvedMethods)[dexMethodIndex] = artMethod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resolveInNative(Method hook, Method backup) {
|
|
||||||
Yahfa.ensureMethodCached(hook, backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Field getField(Class topClass, String fieldName) throws NoSuchFieldException {
|
|
||||||
while (topClass != null && topClass != Object.class) {
|
|
||||||
try {
|
|
||||||
Field field = topClass.getDeclaredField(fieldName);
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field;
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
topClass = topClass.getSuperclass();
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasJavaArtMethod() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (artMethodClass != null)
|
|
||||||
return true;
|
|
||||||
try {
|
|
||||||
artMethodClass = Class.forName("java.lang.reflect.ArtMethod");
|
|
||||||
return true;
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.elderdrivers.riru.edxp.yahfa.core;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
|
import com.elderdrivers.riru.edxp.config.InstallerChooser;
|
||||||
|
import com.elderdrivers.riru.edxp.core.BaseEdxpImpl;
|
||||||
|
import com.elderdrivers.riru.edxp.core.EdxpImpl;
|
||||||
|
import com.elderdrivers.riru.edxp.core.Main;
|
||||||
|
import com.elderdrivers.riru.edxp.core.Proxy;
|
||||||
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMethodResolver;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.BlackWhiteListProxy;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.NormalProxy;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.Router;
|
||||||
|
|
||||||
|
public class YahfaEdxpImpl extends BaseEdxpImpl {
|
||||||
|
|
||||||
|
static {
|
||||||
|
final EdxpImpl edxpImpl = new YahfaEdxpImpl();
|
||||||
|
if (Main.setEdxpImpl(edxpImpl)) {
|
||||||
|
edxpImpl.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Variant
|
||||||
|
@Override
|
||||||
|
public int getVariant() {
|
||||||
|
return YAHFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
Yahfa.init(Build.VERSION.SDK_INT);
|
||||||
|
HookMethodResolver.init();
|
||||||
|
getRouter().injectConfig();
|
||||||
|
InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName());
|
||||||
|
|
||||||
|
setInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Proxy createBlackWhiteListProxy() {
|
||||||
|
return new BlackWhiteListProxy(getRouter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Proxy createNormalProxy() {
|
||||||
|
return new NormalProxy(getRouter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Router createRouter() {
|
||||||
|
return new YahfaRouter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.elderdrivers.riru.edxp.yahfa.core;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||||
|
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
|
||||||
|
import com.elderdrivers.riru.edxp.yahfa.config.YahfaEdxpConfig;
|
||||||
|
import com.elderdrivers.riru.edxp.yahfa.config.YahfaHookProvider;
|
||||||
|
import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
public class YahfaRouter extends BaseRouter {
|
||||||
|
|
||||||
|
public void onEnterChildProcess() {
|
||||||
|
DynamicBridge.onForkPost();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectConfig() {
|
||||||
|
EdXpConfigGlobal.sConfig = new YahfaEdxpConfig();
|
||||||
|
EdXpConfigGlobal.sHookProvider = new YahfaHookProvider();
|
||||||
|
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,8 @@ import android.app.AndroidAppHelper;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.yahfa.core.HookMain;
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
|
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|
@ -27,8 +26,8 @@ public class DexMakerUtils {
|
||||||
}
|
}
|
||||||
String packageName = AndroidAppHelper.currentPackageName();
|
String packageName = AndroidAppHelper.currentPackageName();
|
||||||
if (TextUtils.isEmpty(packageName)) { //default to true
|
if (TextUtils.isEmpty(packageName)) { //default to true
|
||||||
DexLog.w("packageName is empty, processName=" + Main.appProcessName
|
DexLog.w("packageName is empty, processName=" + ConfigManager.appProcessName
|
||||||
+ ", appDataDir=" + Main.appDataDir);
|
+ ", appDataDir=" + ConfigManager.appDataDir);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return !ConfigManager.shouldUseCompatMode(packageName);
|
return !ConfigManager.shouldUseCompatMode(packageName);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.dexmaker;
|
package com.elderdrivers.riru.edxp.yahfa.dexmaker;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
@ -13,10 +14,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
|
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getPackageName;
|
import static com.elderdrivers.riru.edxp.util.FileUtils.getPackageName;
|
||||||
import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName;
|
import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName;
|
||||||
|
import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
|
||||||
|
|
||||||
public final class DynamicBridge {
|
public final class DynamicBridge {
|
||||||
|
|
||||||
|
|
@ -74,12 +75,12 @@ public final class DynamicBridge {
|
||||||
try {
|
try {
|
||||||
// we always choose to use device encrypted storage data on android N and later
|
// we always choose to use device encrypted storage data on android N and later
|
||||||
// in case some app is installing hooks before phone is unlocked
|
// in case some app is installing hooks before phone is unlocked
|
||||||
String fixedAppDataDir = getDataPathPrefix() + getPackageName(Main.appDataDir) + "/";
|
String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/";
|
||||||
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
|
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
|
||||||
+ getCurrentProcessName(Main.appProcessName).replace(":", "_") + "/");
|
+ getCurrentProcessName(ConfigManager.appProcessName).replace(":", "_") + "/");
|
||||||
dexOptDir = new File(dexDir, "oat");
|
dexOptDir = new File(dexDir, "oat");
|
||||||
dexDir.mkdirs();
|
dexDir.mkdirs();
|
||||||
DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
|
DexLog.d(ConfigManager.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
DexLog.e("error when init dex path", throwable);
|
DexLog.e("error when init dex path", throwable);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@ import android.annotation.TargetApi;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||||
import com.elderdrivers.riru.edxp.yahfa.core.HookMain;
|
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.entry;
|
|
||||||
|
|
||||||
import android.app.AndroidAppHelper;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.config.YahfaEdxpConfig;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.config.YahfaHookProvider;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.core.HookMain;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.AppBootstrapHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.SysBootstrapHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.SysInnerHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.WorkAroundHookInfo;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.SystemMainHooker;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
|
||||||
|
|
||||||
public class Router {
|
|
||||||
|
|
||||||
public volatile static boolean forkCompleted = false;
|
|
||||||
|
|
||||||
private static volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
public static void initResourcesHook() {
|
|
||||||
startWorkAroundHook(); // for OnePlus devices
|
|
||||||
XposedBridge.initXResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void prepare(boolean isSystem) {
|
|
||||||
// this flag is needed when loadModules
|
|
||||||
XposedInit.startsSystemServer = isSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkHookState(String appDataDir) {
|
|
||||||
// determine whether allow xposed or not
|
|
||||||
// XposedBridge.disableHooks = ConfigManager.shouldHook(parsePackageName(appDataDir));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String parsePackageName(String appDataDir) {
|
|
||||||
if (TextUtils.isEmpty(appDataDir)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
int lastIndex = appDataDir.lastIndexOf("/");
|
|
||||||
if (lastIndex < 1) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return appDataDir.substring(lastIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void installBootstrapHooks(boolean isSystem) {
|
|
||||||
// Initialize the Xposed framework
|
|
||||||
try {
|
|
||||||
if (!bootstrapHooked.compareAndSet(false, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Router.startBootstrapHook(isSystem);
|
|
||||||
XposedInit.initForZygote(isSystem);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Utils.logE("error during Xposed initialization", t);
|
|
||||||
XposedBridge.disableHooks = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void loadModulesSafely(boolean isInZygote) {
|
|
||||||
try {
|
|
||||||
// FIXME some coredomain app can't reading modules.list
|
|
||||||
XposedInit.loadModules(isInZygote);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
Utils.logE("error loading module list", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startBootstrapHook(boolean isSystem) {
|
|
||||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
|
||||||
ClassLoader classLoader = XposedBridge.BOOTCLASSLOADER;
|
|
||||||
if (isSystem) {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
SysBootstrapHookInfo.class.getName());
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
AppBootstrapHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startSystemServerHook() {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
SystemMainHooker.systemServerCL,
|
|
||||||
SysInnerHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startWorkAroundHook() {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
Router.class.getClassLoader(),
|
|
||||||
XposedBridge.BOOTCLASSLOADER,
|
|
||||||
WorkAroundHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onForkStart() {
|
|
||||||
forkCompleted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onForkFinish() {
|
|
||||||
forkCompleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onEnterChildProcess() {
|
|
||||||
DynamicBridge.onForkPost();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logD(String prefix) {
|
|
||||||
Utils.logD(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
|
||||||
AndroidAppHelper.currentProcessName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logE(String prefix, Throwable throwable) {
|
|
||||||
Utils.logE(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
|
|
||||||
AndroidAppHelper.currentProcessName()), throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void injectConfig() {
|
|
||||||
EdXpConfigGlobal.sConfig = new YahfaEdxpConfig();
|
|
||||||
EdXpConfigGlobal.sHookProvider = new YahfaHookProvider();
|
|
||||||
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.HandleBindAppHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.LoadedApkConstructorHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker;
|
|
||||||
|
|
||||||
public class AppBootstrapHookInfo implements KeepMembers {
|
|
||||||
public static String[] hookItemNames = {
|
|
||||||
HandleBindAppHooker.class.getName(),
|
|
||||||
LoadedApkConstructorHooker.class.getName(),
|
|
||||||
OnePlusWorkAroundHooker.class.getName()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker;
|
|
||||||
|
|
||||||
public class WorkAroundHookInfo implements KeepMembers {
|
|
||||||
public static String[] hookItemNames = {
|
|
||||||
OnePlusWorkAroundHooker.class.getName()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.proxy;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
|
||||||
import com.elderdrivers.riru.edxp.util.ProcessUtils;
|
|
||||||
import com.elderdrivers.riru.edxp.util.Utils;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Non dynamic mode
|
|
||||||
* - system_server is whitelisted
|
|
||||||
* * for all child processes of main zygote
|
|
||||||
* What've been done in main zygote pre-forking system_server
|
|
||||||
* 1) non dynamic flag set (no need to reset)
|
|
||||||
* 2) boot image methods deopted (no need to redo)
|
|
||||||
* 3) startSystemServer flag set to true (need to reset)
|
|
||||||
* 4) workaround hooks installed (need to redo)
|
|
||||||
* 5) module list loaded and initZygote called (no need to redo)
|
|
||||||
* 6) close all fds (no need to redo because of 5))
|
|
||||||
* * for all child processes of secondary zygote
|
|
||||||
* 1) do the same things pre-forking first child process
|
|
||||||
* - system_server is blacklisted:
|
|
||||||
* * for all child processes of both main zygote and secondary zygote
|
|
||||||
* 1) do the same things pre-forking first child process
|
|
||||||
* 2. Dynamic mode:
|
|
||||||
* to be continued
|
|
||||||
*/
|
|
||||||
public class BlackWhiteListProxy {
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
// should never happen
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// only enter here when isDynamicModulesMode is off
|
|
||||||
onForkPreForNonDynamicMode(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
onForkPostCommon(false, appDataDir, niceName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, long permittedCapabilities,
|
|
||||||
long effectiveCapabilities) {
|
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
// should never happen
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// only enter here when isDynamicModulesMode is off
|
|
||||||
onForkPreForNonDynamicMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
onForkPostCommon(true, getDataPathPrefix() + "android", "system_server");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some details are different between main zygote and secondary zygote.
|
|
||||||
*/
|
|
||||||
private static void onForkPreForNonDynamicMode(boolean isSystemServer) {
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// set startsSystemServer flag used when loadModules
|
|
||||||
Router.prepare(isSystemServer);
|
|
||||||
// deoptBootMethods once for all child processes of zygote
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
|
||||||
// we never install bootstrap hooks here in black/white list mode except workaround hooks
|
|
||||||
// because installed hooks would be propagated to all child processes of zygote
|
|
||||||
Router.startWorkAroundHook();
|
|
||||||
// loadModules once for all child processes of zygote
|
|
||||||
// TODO maybe just save initZygote callbacks and call them when whitelisted process forked?
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
|
||||||
Main.appDataDir = appDataDir;
|
|
||||||
Main.niceName = niceName;
|
|
||||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
|
||||||
if (!isDynamicModulesMode) {
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
}
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
if (!checkNeedHook(appDataDir, niceName)) {
|
|
||||||
// if is blacklisted, just stop here
|
|
||||||
Router.onForkFinish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
Router.initResourcesHook();
|
|
||||||
}
|
|
||||||
Router.prepare(isSystemServer);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
|
||||||
Router.installBootstrapHooks(isSystemServer);
|
|
||||||
if (isDynamicModulesMode) {
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
}
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean checkNeedHook(String appDataDir, String niceName) {
|
|
||||||
boolean needHook;
|
|
||||||
if (TextUtils.isEmpty(appDataDir)) {
|
|
||||||
Utils.logE("niceName:" + niceName + ", procName:"
|
|
||||||
+ ProcessUtils.getCurrentProcessName(Main.appProcessName) + ", appDataDir is null, blacklisted!");
|
|
||||||
needHook = false;
|
|
||||||
} else {
|
|
||||||
// FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth
|
|
||||||
needHook = ConfigManager.isAppNeedHook(appDataDir);
|
|
||||||
}
|
|
||||||
if (!needHook) {
|
|
||||||
// clean up the scene
|
|
||||||
onBlackListed();
|
|
||||||
}
|
|
||||||
return needHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onBlackListed() {
|
|
||||||
XposedBridge.clearLoadedPackages();
|
|
||||||
XposedBridge.clearInitPackageResources();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.proxy;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.Main;
|
|
||||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
|
||||||
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
|
|
||||||
|
|
||||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
|
||||||
|
|
||||||
public class NormalProxy {
|
|
||||||
|
|
||||||
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
|
||||||
int[][] rlimits, int mountExternal, String seInfo,
|
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
|
||||||
boolean startChildZygote, String instructionSet,
|
|
||||||
String appDataDir) {
|
|
||||||
// mainly for secondary zygote
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// call this to ensure the flag is set to false ASAP
|
|
||||||
Router.prepare(false);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
|
||||||
// install bootstrap hooks for secondary zygote
|
|
||||||
Router.installBootstrapHooks(false);
|
|
||||||
// only load modules for secondary zygote
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
|
||||||
// TODO consider processes without forkAndSpecializePost called
|
|
||||||
Main.appDataDir = appDataDir;
|
|
||||||
Main.niceName = niceName;
|
|
||||||
Router.prepare(false);
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
// load modules for each app process on its forked if dynamic modules mode is on
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
|
||||||
long permittedCapabilities, long effectiveCapabilities) {
|
|
||||||
Router.onForkStart();
|
|
||||||
Router.initResourcesHook();
|
|
||||||
// set startsSystemServer flag used when loadModules
|
|
||||||
Router.prepare(true);
|
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote
|
|
||||||
// install bootstrap hooks for main zygote as early as possible
|
|
||||||
// in case we miss some processes not forked via forkAndSpecialize
|
|
||||||
// for instance com.android.phone
|
|
||||||
Router.installBootstrapHooks(true);
|
|
||||||
// loadModules have to be executed in zygote even isDynamicModules is false
|
|
||||||
// because if not global hooks installed in initZygote might not be
|
|
||||||
// propagated to processes not forked via forkAndSpecialize
|
|
||||||
Router.loadModulesSafely(true);
|
|
||||||
Zygote.closeFilesBeforeFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
|
||||||
// in system_server process
|
|
||||||
Main.appDataDir = getDataPathPrefix() + "android";
|
|
||||||
Main.niceName = "system_server";
|
|
||||||
Router.prepare(true);
|
|
||||||
Zygote.reopenFilesAfterFork();
|
|
||||||
Router.onEnterChildProcess();
|
|
||||||
// reload module list if dynamic mode is on
|
|
||||||
Router.loadModulesSafely(false);
|
|
||||||
Router.onForkFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.elderdrivers.riru.edxp.yahfa.core.YahfaEdxpImpl
|
||||||
Loading…
Reference in New Issue