Refactor Java code

This commit is contained in:
solohsu 2019-06-03 23:58:56 +08:00
parent c362449e33
commit c38affbf3a
99 changed files with 1338 additions and 3089 deletions

View File

@ -23,6 +23,7 @@ dependencies {
compileOnly project(':hiddenapi-stubs')
implementation project(':xposed-bridge')
compileOnly project(':dexmaker')
api "androidx.annotation:annotation:1.1.0-rc01"
}

View File

@ -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.LoadedApk;
@ -6,14 +6,14 @@ 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.config.ConfigManager;
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.Hookers;
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.XposedHelpers;
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;
// 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";
public static String methodName = "handleBindApplication";
public static String methodSig = "(Landroid/app/ActivityThread$AppBindData;)V";
public static void hook(Object thiz, Object bindData) {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (XposedBlackListHooker.shouldDisableHooks("")) {
backup(thiz, bindData);
return;
}
try {
Router.logD("ActivityThread#handleBindApplication() starts");
ActivityThread activityThread = (ActivityThread) thiz;
Hookers.logD("ActivityThread#handleBindApplication() starts");
ActivityThread activityThread = (ActivityThread) param.thisObject;
Object bindData = param.args[0];
ApplicationInfo appInfo = (ApplicationInfo) XposedHelpers.getObjectField(bindData, "appInfo");
// 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;
Utils.logD("processName=" + Main.appProcessName +
", packageName=" + reportedPackageName + ", appDataDir=" + Main.appDataDir);
Utils.logD("processName=" + ConfigManager.appProcessName +
", packageName=" + reportedPackageName + ", appDataDir=" + ConfigManager.appDataDir);
if (XposedBlackListHooker.shouldDisableHooks(reportedPackageName)) {
return;
@ -52,7 +49,7 @@ public class HandleBindAppHooker implements KeepMembers {
ComponentName instrumentationName = (ComponentName) XposedHelpers.getObjectField(bindData, "instrumentationName");
if (instrumentationName != null) {
Router.logD("Instrumentation detected, disabling framework for");
Hookers.logD("Instrumentation detected, disabling framework for");
XposedBridge.disableHooks = true;
return;
}
@ -85,12 +82,7 @@ public class HandleBindAppHooker implements KeepMembers {
SliceProviderFix.hook();
}
} catch (Throwable t) {
Router.logE("error when hooking bindApp", t);
} finally {
backup(thiz, bindData);
Hookers.logE("error when hooking bindApp", t);
}
}
public static void backup(Object thiz, Object bindData) {
}
}
}

View File

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

View File

@ -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.Main;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import com.elderdrivers.riru.edxp.core.Main;
import com.elderdrivers.riru.edxp.util.Hookers;
import dalvik.system.BaseDexClassLoader;
import de.robv.android.xposed.XC_MethodHook;
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
* 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:
@ -21,21 +22,15 @@ import de.robv.android.xposed.XposedBridge;
* 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 class OneplusWorkaround extends XC_MethodHook {
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);
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (XposedBridge.disableHooks || Main.getEdxpImpl().getRouter().isForkCompleted()) {
return;
}
Router.logD("BaseDexClassLoader#inCompatConfigList() starts");
return false;
Hookers.logD("BaseDexClassLoader#inCompatConfigList() starts");
param.setResult(false);
}
public static boolean backup(int type, String packageName) {
return false;
}
}
}

View File

@ -1,10 +1,10 @@
package com.elderdrivers.riru.edxp.yahfa._hooker;
package com.elderdrivers.riru.edxp._hooker.impl;
import android.os.Build;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import com.elderdrivers.riru.edxp.util.Hookers;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
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 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) {
public class StartBootstrapServices extends XC_MethodHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (XposedBridge.disableHooks) {
backup(systemServer);
return;
}
@ -32,35 +28,33 @@ public class StartBootstrapServicesHooker implements KeepMembers {
try {
XposedInit.loadedPackagesInProcess.add("android");
replaceParentClassLoader(SystemMainHooker.systemServerCL);
replaceParentClassLoader(SystemMain.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.classLoader = SystemMain.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));
findAndHookMethod("com.android.server.pm.HwPackageManagerService",
SystemMain.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));
findAndHookMethod(className, SystemMain.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);
Hookers.logE("error when hooking startBootstrapServices", t);
}
}
public static void backup(Object systemServer) {
}
}

View File

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

View File

@ -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) {
}
}

View File

@ -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) {
}
}

View File

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

View File

@ -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) {
}
}

View File

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

View File

@ -1,27 +1,19 @@
package com.elderdrivers.riru.edxp.config;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
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_PACKAGE_NAME;
public class ConfigManager {
private static final String BLACK_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/blacklist/";
private static final String WHITE_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/whitelist/";
private static final String COMPAT_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/compatlist/";
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 String appDataDir = "";
public static String niceName = "";
public static String appProcessName = "";
public static boolean shouldUseWhitelist() {
return isFileExists(USE_WHITE_LIST);
}
private static final String COMPAT_LIST_PATH = INSTALLER_DATA_BASE_DIR + "conf/compatlist/";
private static final HashMap<String, Boolean> compatModeCache = new HashMap<>();
public static boolean shouldUseCompatMode(String packageName) {
Boolean result;
@ -34,17 +26,6 @@ public class ConfigManager {
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) {
return SELinuxHelper.getAppDataFileService().checkFileExists(path);
}

View File

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

View File

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

View File

@ -1,44 +1,30 @@
package com.elderdrivers.riru.edxp;
package com.elderdrivers.riru.edxp.core;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Process;
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.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.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.Iterator;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
@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());
SandHookXposedBridge.init();
}
private static final AtomicReference<EdxpImpl> edxpImplRef = new AtomicReference<>(null);
public static void setAppDataDir(String appDataDir) {
Main.appDataDir = appDataDir;
XposedCompat.appDataDir = appDataDir;
static {
loadEdxpImpls();
}
///////////////////////////////////////////////////////////////////////////////////////////////
@ -50,6 +36,10 @@ public class Main implements KeepAll {
String niceName, int[] fdsToClose, int[] fdsToIgnore,
boolean startChildZygote, String instructionSet,
String appDataDir) {
final EdxpImpl edxp = getEdxpImpl();
if (edxp == null || !edxp.isInitialized()) {
return;
}
if (BuildConfig.DEBUG) {
forkAndSpecializePramsStr = String.format(
"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),
Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir);
}
if (ConfigManager.isBlackWhiteListEnabled()) {
BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
edxp.getBlackWhiteListProxy().forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote,
instructionSet, appDataDir);
} else {
NormalProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
edxp.getNormalProxy().forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet,
appDataDir);
}
}
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
final EdxpImpl edxp = getEdxpImpl();
if (edxp == null || !edxp.isInitialized()) {
return;
}
if (pid == 0) {
Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid());
if (ConfigManager.isBlackWhiteListEnabled()) {
BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName);
edxp.getBlackWhiteListProxy().forkAndSpecializePost(pid, appDataDir, niceName);
} else {
NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName);
edxp.getNormalProxy().forkAndSpecializePost(pid, appDataDir, niceName);
}
} else {
// 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,
long permittedCapabilities, long effectiveCapabilities) {
final EdxpImpl edxp = getEdxpImpl();
if (edxp == null || !edxp.isInitialized()) {
return;
}
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,
edxp.getBlackWhiteListProxy().forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
} else {
NormalProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
edxp.getNormalProxy().forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
}
}
public static void forkSystemServerPost(int pid) {
final EdxpImpl edxp = getEdxpImpl();
if (edxp == null || !edxp.isInitialized()) {
return;
}
if (pid == 0) {
Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid());
if (ConfigManager.isBlackWhiteListEnabled()) {
BlackWhiteListProxy.forkSystemServerPost(pid);
edxp.getBlackWhiteListProxy().forkSystemServerPost(pid);
} else {
NormalProxy.forkSystemServerPost(pid);
edxp.getNormalProxy().forkSystemServerPost(pid);
}
} else {
// 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;
}
});
}
}

View File

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

View File

@ -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.core.Yahfa;
import com.elderdrivers.riru.edxp.util.Utils;
import com.elderdrivers.riru.edxp.yahfa._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.HashSet;
import java.util.Set;
import de.robv.android.xposed.XposedHelpers;
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) {
try {

View File

@ -1,8 +1,7 @@
package com.elderdrivers.riru.edxp.whale.core;
package com.elderdrivers.riru.edxp.core.yahfa;
import android.os.Build;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.core.Yahfa;
import com.elderdrivers.riru.edxp.util.Utils;
@ -113,12 +112,12 @@ public class HookMethodResolver {
Object artMethod = artMethodField.get(backup);
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
Object resolvedMethods = resolvedMethodsField.get(dexCache);
((Object[])resolvedMethods)[dexMethodIndex] = artMethod;
((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;
((long[]) resolvedMethods)[dexMethodIndex] = artMethod;
}
}

View File

@ -0,0 +1,4 @@
package com.elderdrivers.riru.edxp.entry;
public interface Hook {
}

View File

@ -0,0 +1,5 @@
package com.elderdrivers.riru.edxp.entry;
public class HookImpl<T> implements Hook {
}

View File

@ -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.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._hooker.yahfa.HandleBindAppHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
public class AppBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

@ -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.edxp.yahfa._hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.yahfa._hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.yahfa._hooker.SystemMainHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.SystemMainHooker;
public class SysBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

@ -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.edxp.yahfa._hooker.StartBootstrapServicesHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.StartBootstrapServicesHooker;
public class SysInnerHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

@ -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.edxp.whale._hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
public class WorkAroundHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

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

View File

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

View File

@ -1,14 +1,12 @@
package com.elderdrivers.riru.edxp.whale.proxy;
package com.elderdrivers.riru.edxp.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.whale.entry.Router;
import de.robv.android.xposed.XposedBridge;
@ -33,13 +31,17 @@ import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
* 2. Dynamic mode:
* to be continued
*/
public class BlackWhiteListProxy {
public class BlackWhiteListProxy extends BaseProxy {
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) {
public BlackWhiteListProxy(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) {
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
if (isDynamicModulesMode) {
// should never happen
@ -49,13 +51,13 @@ public class BlackWhiteListProxy {
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);
}
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities,
long effectiveCapabilities) {
public 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
@ -65,59 +67,59 @@ public class BlackWhiteListProxy {
onForkPreForNonDynamicMode(true);
}
public static void forkSystemServerPost(int pid) {
public 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();
private void onForkPreForNonDynamicMode(boolean isSystemServer) {
mRouter.onForkStart();
mRouter.initResourcesHook();
// set startsSystemServer flag used when loadModules
Router.prepare(isSystemServer);
mRouter.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();
mRouter.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);
mRouter.loadModulesSafely(true);
Zygote.closeFilesBeforeFork();
}
private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
Main.appDataDir = appDataDir;
Main.niceName = niceName;
private void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
ConfigManager.appDataDir = appDataDir;
ConfigManager.niceName = niceName;
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
if (!isDynamicModulesMode) {
Zygote.reopenFilesAfterFork();
}
Router.onEnterChildProcess();
mRouter.onEnterChildProcess();
if (!checkNeedHook(appDataDir, niceName)) {
// if is blacklisted, just stop here
Router.onForkFinish();
mRouter.onForkFinish();
return;
}
if (isDynamicModulesMode) {
Router.initResourcesHook();
mRouter.initResourcesHook();
}
Router.prepare(isSystemServer);
mRouter.prepare(isSystemServer);
PrebuiltMethodsDeopter.deoptBootMethods();
Router.installBootstrapHooks(isSystemServer);
mRouter.installBootstrapHooks(isSystemServer);
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;
if (TextUtils.isEmpty(appDataDir)) {
Utils.logE("niceName:" + niceName + ", procName:"
+ ProcessUtils.getCurrentProcessName(Main.appProcessName) + ", appDataDir is null, blacklisted!");
+ ProcessUtils.getCurrentProcessName(ConfigManager.appProcessName) + ", appDataDir is null, blacklisted!");
needHook = false;
} else {
// FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth

View File

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

View File

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

View File

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

View File

@ -3,10 +3,10 @@ import org.gradle.internal.os.OperatingSystem
apply plugin: 'com.android.library'
version "v0.4.3.0_alpha"
version "v0.4.3.2_alpha"
ext {
versionCode = "4300"
versionCode = "4320"
module_name = "EdXposed"
jar_dest_dir = "${projectDir}/template_override/system/framework/"
is_windows = OperatingSystem.current().isWindows()

View File

@ -3,10 +3,15 @@
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion);
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,
jobject target, jobject hook,
@ -18,6 +23,10 @@ void Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(JNIEnv *env, jclass clazz
void setNonCompilable(void *method);
static void* getResolvedMethodsAddr(JNIEnv*, jobject);
static void *getResolvedMethodsAddr(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif // HOOK_MAIN_H

View File

@ -20,7 +20,7 @@ namespace edxp {
static constexpr const char *kInjectDexPath = "/system/framework/edxp.jar:"
"/system/framework/eddalvikdx.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 *kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall";

View File

@ -1,9 +1,5 @@
extern "C"
{
#include "HookMain.h"
}
#include <nativehelper/jni_macros.h>
#include "jni.h"
#include "native_util.h"

View File

@ -1,6 +1,6 @@
#!/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`
BUILD_DESC=`getprop ro.build.description`
PRODUCT=`getprop ro.build.product`

View File

@ -12,6 +12,7 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0"
multiDexEnabled true
}
buildTypes {
@ -61,7 +62,8 @@ afterEvaluate {
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
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/")
baseName "edxp"
doLast {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,5 @@
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.EdXpConfig;
import com.elderdrivers.riru.edxp.config.InstallerChooser;

View File

@ -2,7 +2,6 @@ package com.elderdrivers.riru.edxp.sandhook.config;
import android.util.Log;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.art.ClassLinker;
import com.elderdrivers.riru.edxp.config.BaseHookProvider;
import com.elderdrivers.riru.edxp.core.ResourcesHook;

View File

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

View File

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

View File

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

View File

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

View File

@ -5,8 +5,7 @@ import android.os.Build;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.config.ConfigManager;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.sandhook.core.HookMain;
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
import java.lang.reflect.Member;
import java.security.MessageDigest;
@ -27,8 +26,8 @@ public class DexMakerUtils {
}
String packageName = AndroidAppHelper.currentPackageName();
if (TextUtils.isEmpty(packageName)) { //default to true
DexLog.w("packageName is empty, processName=" + Main.appProcessName
+ ", appDataDir=" + Main.appDataDir);
DexLog.w("packageName is empty, processName=" + ConfigManager.appProcessName
+ ", appDataDir=" + ConfigManager.appDataDir);
return true;
}
return !ConfigManager.shouldUseCompatMode(packageName);

View File

@ -1,6 +1,6 @@
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.lang.reflect.Constructor;
@ -8,16 +8,15 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
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.getPackageName;
import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName;
import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
public final class DynamicBridge {
@ -78,9 +77,9 @@ public final class DynamicBridge {
try {
// 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
String fixedAppDataDir = getDataPathPrefix() + getPackageName(Main.appDataDir) + "/";
String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/";
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
+ getCurrentProcessName(Main.appProcessName).replace(":", "_") + "/");
+ getCurrentProcessName(ConfigManager.appProcessName).replace(":", "_") + "/");
dexDir.mkdirs();
} catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable);

View File

@ -4,9 +4,8 @@ import android.annotation.TargetApi;
import android.os.Build;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.Main;
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.lang.reflect.Constructor;

View File

@ -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.edxp.sandhook._hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.sandhook._hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.OnePlusWorkAroundHooker;
public class AppBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

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

View File

@ -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.edxp.sandhook._hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.sandhook._hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.sandhook._hooker.SystemMainHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.SystemMainHooker;
public class SysBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

@ -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.edxp.sandhook._hooker.StartBootstrapServicesHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.StartBootstrapServicesHooker;
public class SysInnerHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

@ -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.edxp.sandhook._hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.sandhook.hooker.OnePlusWorkAroundHooker;
public class WorkAroundHookInfo implements KeepMembers {
public static String[] hookItemNames = {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,9 @@
package com.elderdrivers.riru.edxp.sandhook._hooker;
package com.elderdrivers.riru.edxp.sandhook.hooker;
import android.app.ActivityThread;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.annotation.HookClass;
import com.swift.sandhook.annotation.HookMethod;
@ -12,7 +11,7 @@ import com.swift.sandhook.annotation.HookMethodBackup;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XC_MethodHook;
// system_server initialization
@ -31,21 +30,16 @@ public class SystemMainHooker implements KeepMembers {
@HookMethod("systemMain")
public static ActivityThread hook() throws Throwable {
if (XposedBridge.disableHooks) {
return backup();
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());
}
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;
methodHook.callAfterHookedMethod(param);
return (ActivityThread) param.getResult();
}
public static ActivityThread backup() throws Throwable {

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.os.Process;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.config.ConfigManager;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader;
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 static volatile String appDataDir;
// TODO initialize these variables
public static volatile File cacheDir;
public static volatile ClassLoader classLoader;
@ -37,16 +36,17 @@ public class XposedCompat {
public static void addHookers(ClassLoader classLoader, Class[] hookers) {
if (hookers == null)
return;
for (Class hooker:hookers) {
for (Class hooker : hookers) {
try {
SandHook.addHookClass(classLoader, hooker);
} catch (Throwable throwable) {}
} catch (Throwable throwable) {
}
}
}
public static File getCacheDir() {
if (cacheDir == null) {
String fixedAppDataDir = getDataPathPrefix() + getPackageName(appDataDir) + "/";
String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/";
cacheDir = new File(fixedAppDataDir, "/cache/sandhook/"
+ ProcessUtils.getProcessName().replace(":", "_") + "/");
}

View File

@ -0,0 +1 @@
com.elderdrivers.riru.edxp.sandhook.core.SandHookEdxpImpl

View File

@ -12,6 +12,7 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0"
multiDexEnabled true
}
buildTypes {
@ -60,7 +61,8 @@ afterEvaluate {
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
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/")
baseName "edxp"
doLast {

View File

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

View File

@ -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) {
}
}

View File

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

View File

@ -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) {
}
}

View File

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

View File

@ -1,6 +1,5 @@
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.EdXpConfig;
import com.elderdrivers.riru.edxp.config.InstallerChooser;

View File

@ -1,6 +1,5 @@
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.config.BaseHookProvider;
import com.elderdrivers.riru.edxp.core.ResourcesHook;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
com.elderdrivers.riru.edxp.whale.core.WhaleEdxpImpl

View File

@ -12,6 +12,7 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0"
multiDexEnabled true
}
buildTypes {
@ -60,7 +61,8 @@ afterEvaluate {
task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
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/")
baseName "edxp"
doLast {

View File

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

View File

@ -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) {
}
}

View File

@ -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) {
}
}

View File

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

View File

@ -1,6 +1,5 @@
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.EdXpConfig;
import com.elderdrivers.riru.edxp.config.InstallerChooser;

View File

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

View File

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

View File

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

View File

@ -4,9 +4,8 @@ import android.app.AndroidAppHelper;
import android.os.Build;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.Main;
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.security.MessageDigest;
@ -27,8 +26,8 @@ public class DexMakerUtils {
}
String packageName = AndroidAppHelper.currentPackageName();
if (TextUtils.isEmpty(packageName)) { //default to true
DexLog.w("packageName is empty, processName=" + Main.appProcessName
+ ", appDataDir=" + Main.appDataDir);
DexLog.w("packageName is empty, processName=" + ConfigManager.appProcessName
+ ", appDataDir=" + ConfigManager.appDataDir);
return true;
}
return !ConfigManager.shouldUseCompatMode(packageName);

View File

@ -1,6 +1,7 @@
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.lang.reflect.Constructor;
@ -13,10 +14,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
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.getPackageName;
import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName;
import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
public final class DynamicBridge {
@ -74,12 +75,12 @@ public final class DynamicBridge {
try {
// 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
String fixedAppDataDir = getDataPathPrefix() + getPackageName(Main.appDataDir) + "/";
String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/";
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
+ getCurrentProcessName(Main.appProcessName).replace(":", "_") + "/");
+ getCurrentProcessName(ConfigManager.appProcessName).replace(":", "_") + "/");
dexOptDir = new File(dexDir, "oat");
dexDir.mkdirs();
DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
DexLog.d(ConfigManager.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
} catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable);
}

View File

@ -4,9 +4,8 @@ import android.annotation.TargetApi;
import android.os.Build;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.Main;
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.lang.reflect.Constructor;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
com.elderdrivers.riru.edxp.yahfa.core.YahfaEdxpImpl