SandHook: Cherry-pick from branch sandhook

e027b877732e90f41bf5ac48b5e652909ffc2140,
66d21874edb5b918ba16b2c8e3aebb80cc1558ff,
713e2e3b03038d87f2349a642d52a74c8695a6a9,
eccdb67251c39910ff22f968597f9a309c9601b7,
9af84bdf02bd4321e9c1bd54b39d9638aa43a810,
68ce389a48c8bd5ae4eeefdbbc2e74363d103d89,
dd7960c771a7c0ad453b5073df05c80a988ddb5e,
061288a6b835207c4e9e3948b366f2aba1d85d24,
8d2ea787e70e6e992581e54053f080aacafd9e25,
227fc5a43c94e8a0a976050fc1f2f43fdc9b0047
This commit is contained in:
solohsu 2019-03-21 23:02:38 +08:00
parent d546ecd81c
commit d3cf2246ec
30 changed files with 818 additions and 454 deletions

View File

@ -12,4 +12,8 @@
#define ENTRY_CLASS_NAME "com.elderdrivers.riru.edxp.Main"
#define CLASS_SAND_HOOK "com.swift.sandhook.SandHook"
#define CLASS_NEVER_CALL "com.swift.sandhook.ClassNeverCall"
#endif //CONFIG_H

View File

@ -168,6 +168,29 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) {
} else {
LOGE("HookEntry class is null. %d", getpid());
}
//load lib sandhook
void* lib_sandhook;
if (sizeof(void*) == 8) {
lib_sandhook = dlopen("/system/lib64/libsandhook.edxp.so", RTLD_NOW);
} else {
lib_sandhook = dlopen("/system/lib/libsandhook.edxp.so", RTLD_NOW);
}
if (!lib_sandhook) {
LOGW("libsandhook open failed. %s", dlerror());
return;
}
bool* (*jni_load)(JNIEnv*, jclass, jclass) = reinterpret_cast<bool *(*)(JNIEnv *, jclass,
jclass)>(dlsym(lib_sandhook, "JNI_Load_Ex"));
jclass sandhook_class = findClassFromLoader(env, myClassLoader, CLASS_SAND_HOOK);
jclass nevercall_class = findClassFromLoader(env, myClassLoader, CLASS_NEVER_CALL);
if (!sandhook_class || !nevercall_class) { // fail-fast
return;
}
if (!jni_load(env, sandhook_class, nevercall_class)) {
LOGE("SandHook: HookEntry class error. %d", getpid());
}
}
jstring getThrowableMessage(JNIEnv *env, jobject throwable) {

View File

@ -24,7 +24,7 @@ dependencies {
compileOnly files("libs/framework-stub.jar")
implementation project(':edxp-common')
implementation project(':xposed-bridge')
implementation 'com.swift.sandhook:hooklib:3.0.1'
implementation 'com.swift.sandhook:hooklib:3.2.2'
compileOnly project(':dexmaker')
}

View File

@ -60,8 +60,9 @@ TEMP_STUB_INFO = """
"""
STUB_SIZES = [10,20,30,30,30,30,30,20,10,10,5,5,3]
HAS_BACKUP = False;
STUB_SIZES_32 = [10,20,30,30,30,30,30,20,10,10,5,5,3]
STUB_SIZES_64 = [10,20,30,30,30,30,50,50]
HAS_BACKUP = False
def getMethodId(args, index):
@ -126,20 +127,29 @@ def genCallOriginClass(is64Bit, args, index):
method = TEMP_STUB_CALL_ORIGIN_CLASS % (getCallOriginClassName(args, index), getMethodBackupName(index), genArgsListForCallOriginMethod(is64Bit, args))
return method
def genStubInfo():
def genStubInfo32():
hasStub = "true" if HAS_BACKUP else "false"
stubSizes = ""
for args in range(len(STUB_SIZES)):
for args in range(len(STUB_SIZES_32)):
if (args != 0):
stubSizes += ", "
stubSizes += str(STUB_SIZES[args])
stubSizes += str(STUB_SIZES_32[args])
return TEMP_STUB_INFO % (hasStub, stubSizes)
def genStubInfo64():
hasStub = "true" if HAS_BACKUP else "false"
stubSizes = ""
for args in range(len(STUB_SIZES_64)):
if (args != 0):
stubSizes += ", "
stubSizes += str(STUB_SIZES_64[args])
return TEMP_STUB_INFO % (hasStub, stubSizes)
def gen32Stub(packageDir):
class_content = genStubInfo()
class_content = genStubInfo32()
class_name = STUB_FILE_NAME + "32"
for args in range(len(STUB_SIZES)):
for index in range(STUB_SIZES[args]):
for args in range(len(STUB_SIZES_32)):
for index in range(STUB_SIZES_32[args]):
class_content += """\n\n\t//stub of arg size %d, index %d""" % (args, index)
class_content += genHookMethod(False, args, index)
if HAS_BACKUP:
@ -155,10 +165,10 @@ def gen32Stub(packageDir):
def gen64Stub(packageDir):
class_content = genStubInfo()
class_content = genStubInfo64()
class_name = STUB_FILE_NAME + "64"
for args in range(len(STUB_SIZES)):
for index in range(STUB_SIZES[args]):
for args in range(len(STUB_SIZES_64)):
for index in range(STUB_SIZES_64[args]):
class_content += """\n\n\t//stub of arg size %d, index %d""" % (args, index)
class_content += genHookMethod(True, args, index)
if HAS_BACKUP:

View File

@ -12,6 +12,7 @@ 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.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
import java.lang.reflect.Method;
@ -31,7 +32,12 @@ public class Main implements KeepAll {
HookMethodResolver.init();
Router.injectConfig();
InstallerChooser.setInstallerPackageName(getInstallerPkgName());
SandHookXposedBridge.setLibPath();
SandHookXposedBridge.init();
}
public static void setAppDataDir(String appDataDir) {
Main.appDataDir = appDataDir;
XposedCompat.appDataDir = appDataDir;
}
///////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,11 +1,15 @@
package com.elderdrivers.riru.edxp.sandhook.config;
import android.util.Log;
import com.elderdrivers.riru.edxp.hook.HookProvider;
import com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils;
import com.elderdrivers.riru.edxp.sandhook.dexmaker.DynamicBridge;
import com.elderdrivers.riru.edxp.sandhook.util.PrebuiltMethodsDeopter;
import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import de.robv.android.xposed.XposedBridge;
@ -13,12 +17,27 @@ import de.robv.android.xposed.XposedBridge;
public class SandHookProvider implements HookProvider {
@Override
public void hookMethod(Member method, XposedBridge.AdditionalHookInfo additionalInfo) {
if (SandHookXposedBridge.hooked(method) || DynamicBridge.hooked(method)) {
return;
}
if (method.getDeclaringClass() == Log.class) {
Log.e(XposedBridge.TAG, "some one hook Log!");
return;
}
XposedCompat.hookMethod(method, additionalInfo);
}
@Override
public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable {
return SandHookXposedBridge.invokeOriginalMethod(method, thisObject, args);
if (SandHookXposedBridge.hooked(method)) {
try {
return SandHookXposedBridge.invokeOriginalMethod(method, thisObject, args);
} catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
} else {
return DynamicBridge.invokeOriginalMethod(method, thisObject, args);
}
}
@Override

View File

@ -9,6 +9,7 @@ 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;
@ -20,7 +21,7 @@ import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.shouldU
public final class DynamicBridge {
private static final HashMap<Member, Method> hookedInfo = new HashMap<>();
private static final ConcurrentHashMap<Member, Method> hookedInfo = new ConcurrentHashMap<>();
private static final HookerDexMaker dexMaker = new HookerDexMaker();
private static final AtomicBoolean dexPathInited = new AtomicBoolean(false);
private static File dexDir;
@ -33,6 +34,10 @@ public final class DynamicBridge {
dexPathInited.set(false);
}
public static boolean hooked(Member member) {
return hookedInfo.containsKey(member);
}
public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) {
DexLog.d("hooking " + hookMethod);
if (!checkMember(hookMethod)) {

View File

@ -14,6 +14,8 @@ 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.entry.hooker.SystemMainHooker;
import com.swift.sandhook.SandHookConfig;
import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
import java.util.concurrent.atomic.AtomicBoolean;
@ -21,6 +23,8 @@ 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;
@ -30,7 +34,7 @@ public class Router {
public static void prepare(boolean isSystem) {
// this flag is needed when loadModules
XposedInit.startsSystemServer = isSystem;
startsSystemServer = isSystem;
// InstallerChooser.setup();
}
@ -81,19 +85,22 @@ public class Router {
Router.class.getClassLoader(),
classLoader,
SysBootstrapHookInfo.class.getName());
XposedCompat.addHookers(classLoader, SysBootstrapHookInfo.hookItems);
} else {
HookMain.doHookDefault(
Router.class.getClassLoader(),
classLoader,
AppBootstrapHookInfo.class.getName());
XposedCompat.addHookers(classLoader, AppBootstrapHookInfo.hookItems);
}
}
public static void startSystemServerHook() {
HookMain.doHookDefault(
Router.class.getClassLoader(),
SystemMainHooker.systemServerCL,
SysInnerHookInfo.class.getName());
// HookMain.doHookDefault(
// Router.class.getClassLoader(),
// SystemMainHooker.systemServerCL,
// SysInnerHookInfo.class.getName());
XposedCompat.addHookers(SystemMainHooker.systemServerCL, SysInnerHookInfo.hookItems);
}
public static void startWorkAroundHook() {
@ -106,7 +113,8 @@ public class Router {
public static void onEnterChildProcess() {
forkCompleted = true;
DynamicBridge.onForkPost();
SandHookXposedBridge.onForkPost();
//enable compile in child process
//SandHook.enableCompiler(!XposedInit.startsSystemServer);
}
public static void logD(String prefix) {
@ -122,5 +130,6 @@ public class Router {
public static void injectConfig() {
EdXpConfigGlobal.sConfig = new SandHookEdxpConfig();
EdXpConfigGlobal.sHookProvider = new SandHookProvider();
SandHookConfig.compiler = !startsSystemServer;
}
}

View File

@ -7,8 +7,11 @@ import com.elderdrivers.riru.edxp.sandhook.entry.hooker.OnePlusWorkAroundHooker;
public class AppBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {
HandleBindAppHooker.class.getName(),
LoadedApkConstructorHooker.class.getName(),
OnePlusWorkAroundHooker.class.getName()
};
public static Class[] hookItems = {
HandleBindAppHooker.class,
LoadedApkConstructorHooker.class
};
}

View File

@ -8,9 +8,12 @@ import com.elderdrivers.riru.edxp.sandhook.entry.hooker.SystemMainHooker;
public class SysBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {
HandleBindAppHooker.class.getName(),
SystemMainHooker.class.getName(),
LoadedApkConstructorHooker.class.getName(),
OnePlusWorkAroundHooker.class.getName()
};
public static Class[] hookItems = {
HandleBindAppHooker.class,
SystemMainHooker.class,
LoadedApkConstructorHooker.class
};
}

View File

@ -7,4 +7,8 @@ public class SysInnerHookInfo implements KeepMembers {
public static String[] hookItemNames = {
StartBootstrapServicesHooker.class.getName()
};
public static Class[] hookItems = {
StartBootstrapServicesHooker.class
};
}

View File

@ -7,4 +7,7 @@ public class WorkAroundHookInfo implements KeepMembers {
public static String[] hookItemNames = {
OnePlusWorkAroundHooker.class.getName()
};
public static Class[] hookItems = {
};
}

View File

@ -10,6 +10,16 @@ import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.util.Utils;
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.HookMode;
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;
@ -21,15 +31,21 @@ import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClas
import static com.elderdrivers.riru.edxp.sandhook.entry.hooker.XposedBlackListHooker.BLACK_LIST_PACKAGE_NAME;
// 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";
public static void hook(Object thiz, Object bindData) {
@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);
SandHook.callOriginByBackup(backup, thiz, bindData);
return;
}
try {
@ -80,7 +96,7 @@ public class HandleBindAppHooker implements KeepMembers {
} catch (Throwable t) {
Router.logE("error when hooking bindApp", t);
} finally {
backup(thiz, bindData);
SandHook.callOriginByBackup(backup, thiz, bindData);
}
}

View File

@ -9,6 +9,14 @@ import android.util.Log;
import com.elderdrivers.riru.common.KeepMembers;
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;
@ -19,6 +27,7 @@ import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClas
// 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>";
@ -27,20 +36,24 @@ public class LoadedApkConstructorHooker implements KeepMembers {
"Landroid/content/res/CompatibilityInfo;" +
"Ljava/lang/ClassLoader;ZZZ)V";
public static void hook(Object thiz, ActivityThread activityThread,
@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) {
boolean includeCode, boolean registerPackage) throws Throwable {
if (XposedBlackListHooker.shouldDisableHooks("")) {
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
includeCode, registerPackage);
SandHook.callOriginByBackup(backup, thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation, includeCode, registerPackage);
return;
}
Router.logD("LoadedApk#<init> starts");
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
includeCode, registerPackage);
SandHook.callOriginByBackup(backup, thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation, includeCode, registerPackage);
try {
LoadedApk loadedApk = (LoadedApk) thiz;

View File

@ -3,7 +3,14 @@ package com.elderdrivers.riru.edxp.sandhook.entry.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.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;
/**
@ -21,12 +28,18 @@ 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.
*/
@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) {
if (XposedBridge.disableHooks || Router.forkCompleted) {
return backup(type, packageName);

View File

@ -4,6 +4,15 @@ 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.HookMode;
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;
@ -15,15 +24,21 @@ 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;
@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";
public static void hook(Object systemServer) {
@HookMethodBackup("startBootstrapServices")
@SkipParamCheck
static Method backup;
@HookMethod("startBootstrapServices")
public static void hook(@ThisObject Object systemServer) throws Throwable {
if (XposedBridge.disableHooks) {
backup(systemServer);
SandHook.callOriginByBackup(backup, systemServer);
return;
}
@ -56,7 +71,7 @@ public class StartBootstrapServicesHooker implements KeepMembers {
} catch (Throwable t) {
Router.logE("error when hooking startBootstrapServices", t);
} finally {
backup(systemServer);
SandHook.callOriginByBackup(backup, systemServer);
}
}

View File

@ -5,12 +5,20 @@ import android.app.ActivityThread;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.sandhook.entry.Router;
import com.elderdrivers.riru.edxp.sandhook.util.PrebuiltMethodsDeopter;
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.HookMode;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedBridge;
// system_server initialization
// ed: only support sdk >= 21 for now
@HookClass(ActivityThread.class)
public class SystemMainHooker implements KeepMembers {
public static String className = "android.app.ActivityThread";
@ -19,12 +27,16 @@ public class SystemMainHooker implements KeepMembers {
public static ClassLoader systemServerCL;
public static ActivityThread hook() {
@HookMethodBackup("systemMain")
static Method backup;
@HookMethod("systemMain")
public static ActivityThread hook() throws Throwable {
if (XposedBridge.disableHooks) {
return backup();
return (ActivityThread) SandHook.callOriginByBackup(backup, null);
}
Router.logD("ActivityThread#systemMain() starts");
ActivityThread activityThread = backup();
ActivityThread activityThread = (ActivityThread) SandHook.callOriginByBackup(backup, null);
try {
// get system_server classLoader
systemServerCL = Thread.currentThread().getContextClassLoader();

View File

@ -88,7 +88,7 @@ public class BlackWhiteListProxy {
}
private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
Main.appDataDir = appDataDir;
Main.setAppDataDir(appDataDir);
Main.niceName = niceName;
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);

View File

@ -29,7 +29,7 @@ public class NormalProxy {
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
// TODO consider processes without forkAndSpecializePost called
Main.appDataDir = appDataDir;
Main.setAppDataDir(appDataDir);
Main.niceName = niceName;
Router.prepare(false);
Main.reopenFilesAfterForkNative();
@ -58,7 +58,7 @@ public class NormalProxy {
public static void forkSystemServerPost(int pid) {
// in system_server process
Main.appDataDir = getDataPathPrefix() + "android";
Main.setAppDataDir(getDataPathPrefix() + "android");
Main.niceName = "system_server";
Router.prepare(true);
Main.reopenFilesAfterForkNative();

View File

@ -1,23 +1,65 @@
package com.swift.sandhook.xposedcompat;
import android.annotation.SuppressLint;
import android.os.Process;
import android.text.TextUtils;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader;
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
import com.swift.sandhook.xposedcompat.utils.ApplicationUtils;
import com.swift.sandhook.xposedcompat.utils.FileUtils;
import com.swift.sandhook.xposedcompat.utils.ProcessUtils;
import java.io.File;
import java.lang.reflect.Member;
import de.robv.android.xposed.XposedBridge;
import static com.elderdrivers.riru.edxp.util.ProcessUtils.PER_USER_RANGE;
import static com.swift.sandhook.xposedcompat.utils.FileUtils.IS_USING_PROTECTED_STORAGE;
public class XposedCompat {
public static volatile String appDataDir;
// TODO initialize these variables
public static volatile File cacheDir;
public static volatile ClassLoader classLoader;
//try to use internal stub hooker & backup method to speed up hook
public static volatile boolean useInternalStub = true;
public static volatile boolean useNewDexMaker = true;
public static volatile boolean useNewCallBackup = true;
public static volatile boolean retryWhenCallOriginError = false;
private static ClassLoader sandHookXposedClassLoader;
public static void addHookers(ClassLoader classLoader, Class[] hookers) {
if (hookers == null)
return;
for (Class hooker:hookers) {
try {
SandHook.addHookClass(classLoader, hooker);
} catch (Throwable throwable) {}
}
}
public static File getCacheDir() {
if (cacheDir == null) {
String fixedAppDataDir = getDataPathPrefix() + getPackageName(appDataDir) + "/";
cacheDir = new File(fixedAppDataDir, "/cache/sandhook/"
+ ProcessUtils.getProcessName().replace(":", "_") + "/");
}
return cacheDir;
}
public static ClassLoader getClassLoader() {
if (classLoader == null) {
classLoader = getSandHookXposedClassLoader(ApplicationUtils.currentApplication().getClassLoader(), XposedCompat.class.getClassLoader());
}
return classLoader;
}
public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) {
SandHookXposedBridge.hookMethod(hookMethod, additionalHookInfo);
}
@ -31,18 +73,37 @@ public class XposedCompat {
}
}
// public static boolean clearCache() {
// try {
// FileUtils.delete(cacheDir);
// cacheDir.mkdirs();
// return true;
// } catch (Throwable throwable) {
// return false;
// }
// }
//
// public static void clearOatCache() {
// SandHookXposedBridge.clearOatFile();
// }
public static boolean clearCache() {
try {
FileUtils.delete(getCacheDir());
getCacheDir().mkdirs();
return true;
} catch (Throwable throwable) {
return false;
}
}
public static void clearOatCache() {
SandHookXposedBridge.clearOatFile();
}
public static String getPackageName(String dataDir) {
if (TextUtils.isEmpty(dataDir)) {
return "";
}
int lastIndex = dataDir.lastIndexOf("/");
if (lastIndex < 0) {
return dataDir;
}
return dataDir.substring(lastIndex + 1);
}
// FIXME: Although multi-users is considered here, but compat mode doesn't support other users' apps on Oreo and later yet.
@SuppressLint("SdCardPath")
public static String getDataPathPrefix() {
int userId = Process.myUid() / PER_USER_RANGE;
String format = IS_USING_PROTECTED_STORAGE ? "/data/user_de/%d/" : "/data/user/%d/";
return String.format(format, userId);
}
}

View File

@ -6,6 +6,7 @@ import com.swift.sandhook.SandHook;
import com.swift.sandhook.SandHookMethodResolver;
import com.swift.sandhook.utils.ParamWrapper;
import com.swift.sandhook.wrapper.BackupMethodStubs;
import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.utils.DexLog;
import java.lang.reflect.Constructor;
@ -23,12 +24,15 @@ import static de.robv.android.xposed.XposedBridge.sHookedMethodCallbacks;
public class HookStubManager {
public static volatile boolean is64Bit;
//64bits arg0 - arg7 is in reg x1 - x7 and > 7 is in stack, but can not match
public final static int MAX_64_ARGS = 7;
public static int MAX_STUB_ARGS = 0;
public static int[] stubSizes;
public static boolean hasStubBackup = false;
public static boolean hasStubBackup;
public static AtomicInteger[] curUseStubIndexes;
@ -41,10 +45,11 @@ public class HookStubManager {
= sHookedMethodCallbacks;
static {
Class stubClass = SandHook.is64Bit() ? MethodHookerStubs64.class : MethodHookerStubs32.class;
is64Bit = SandHook.is64Bit();
Class stubClass = is64Bit ? MethodHookerStubs64.class : MethodHookerStubs32.class;
stubSizes = (int[]) XposedHelpers.getStaticObjectField(stubClass, "stubSizes");
Boolean hasBackup = (Boolean) XposedHelpers.getStaticObjectField(stubClass, "hasStubBackup");
hasStubBackup = hasBackup == null ? false : hasBackup;
hasStubBackup = hasBackup != null && (hasBackup && !XposedCompat.useNewCallBackup);
if (stubSizes != null && stubSizes.length > 0) {
MAX_STUB_ARGS = stubSizes.length - 1;
curUseStubIndexes = new AtomicInteger[MAX_STUB_ARGS + 1];
@ -89,6 +94,8 @@ public class HookStubManager {
needStubArgCount += parType.length;
if (needStubArgCount > MAX_STUB_ARGS)
return null;
if (is64Bit && needStubArgCount > MAX_64_ARGS)
return null;
for (Class par:parType) {
if (!ParamWrapper.support(par))
return null;
@ -98,7 +105,7 @@ public class HookStubManager {
}
synchronized (HookStubManager.class) {
StubMethodsInfo stubMethodInfo = getStubMethodPair(SandHook.is64Bit(), needStubArgCount);
StubMethodsInfo stubMethodInfo = getStubMethodPair(is64Bit, needStubArgCount);
if (stubMethodInfo == null)
return null;
HookMethodEntity entity = new HookMethodEntity(origin, stubMethodInfo.hook, stubMethodInfo.backup);
@ -180,7 +187,7 @@ public class HookStubManager {
}
public static Method getCallOriginMethod(int args, int index) {
Class stubClass = SandHook.is64Bit() ? MethodHookerStubs64.class : MethodHookerStubs32.class;
Class stubClass = is64Bit ? MethodHookerStubs64.class : MethodHookerStubs32.class;
String className = stubClass.getName();
className += "$";
className += getCallOriginClassName(args, index);
@ -294,7 +301,6 @@ public class HookStubManager {
param.setResult(SandHook.callOriginMethod(originMethod, thiz, param.args));
}
} catch (Throwable e) {
XposedBridge.log(e);
param.setThrowable(e);
}
}
@ -365,7 +371,6 @@ public class HookStubManager {
try {
param.setResult(SandHook.callOriginMethod(origin, thiz, param.args));
} catch (Throwable e) {
XposedBridge.log(e);
param.setThrowable(e);
}
}

View File

@ -9,12 +9,15 @@ import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.utils.DexLog;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Map;
import dalvik.system.InMemoryDexClassLoader;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import external.com.android.dx.BinaryOp;
@ -218,7 +221,13 @@ public class HookerDexMaker implements HookMaker {
throw new IllegalArgumentException("dexDirPath should not be empty!!!");
}
// Create the dex file and load it.
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName);
try {
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName);
} catch (IOException e) {
//can not write file
byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
}
return loadHookerClass(loader, className);
}

View File

@ -7,12 +7,15 @@ import com.swift.sandhook.wrapper.HookWrapper;
import com.swift.sandhook.xposedcompat.hookstub.HookStubManager;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Map;
import dalvik.system.InMemoryDexClassLoader;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import external.com.android.dx.Code;
@ -172,7 +175,13 @@ public class HookerDexMakerNew implements HookMaker {
throw new IllegalArgumentException("dexDirPath should not be empty!!!");
}
// Create the dex file and load it.
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName);
try {
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName);
} catch (IOException e) {
//can not write file
byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
}
return loadHookerClass(loader, className);
}
@ -233,7 +242,7 @@ public class HookerDexMakerNew implements HookMaker {
Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC);
Local<Member> method = code.newLocal(memberTypeId);
// Local<Method> backupMethod = code.newLocal(methodTypeId);
// Local<Method> backupMethod = code.newLocal(methodTypeId);
Local<Object> thisObject = code.newLocal(TypeId.OBJECT);
Local<Object[]> args = code.newLocal(objArrayTypeId);
Local<Integer> actualParamSize = code.newLocal(TypeId.INT);

View File

@ -3,7 +3,6 @@ package com.swift.sandhook.xposedcompat.methodgen;
import android.os.Process;
import android.os.Trace;
import com.elderdrivers.riru.edxp.Main;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.SandHookConfig;
import com.swift.sandhook.wrapper.HookWrapper;
@ -18,27 +17,23 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import de.robv.android.xposed.XposedBridge;
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;
public final class SandHookXposedBridge {
private static final HashMap<Member, Method> hookedInfo = new HashMap<>();
private static HookMaker hookMaker = XposedCompat.useNewDexMaker ? new HookerDexMakerNew() : new HookerDexMaker();
private static final Map<Member, Method> hookedInfo = new ConcurrentHashMap<>();
private static HookMaker hookMaker = XposedCompat.useNewCallBackup ? new HookerDexMakerNew() : new HookerDexMaker();
private static final AtomicBoolean dexPathInited = new AtomicBoolean(false);
private static File dexDir;
public static Map<Member, HookMethodEntity> entityMap = new HashMap<>();
public static Map<Member,HookMethodEntity> entityMap = new ConcurrentHashMap<>();
public static void onForkPost() {
dexPathInited.set(false);
public static boolean hooked(Member member) {
return hookedInfo.containsKey(member) || entityMap.containsKey(member);
}
public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) {
@ -53,8 +48,17 @@ public final class SandHookXposedBridge {
}
try {
setupDexCachePath();
Trace.beginSection("SandHook-Xposed");
if (dexPathInited.compareAndSet(false, true)) {
try {
String fixedAppDataDir = XposedCompat.getCacheDir().getAbsolutePath();
dexDir = new File(fixedAppDataDir, "/hookers/");
if (!dexDir.exists())
dexDir.mkdirs();
} catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable);
}
}
Trace.beginSection("SandXposed");
long timeStart = System.currentTimeMillis();
HookMethodEntity stub = null;
if (XposedCompat.useInternalStub) {
@ -65,7 +69,7 @@ public final class SandHookXposedBridge {
entityMap.put(hookMethod, stub);
} else {
hookMaker.start(hookMethod, additionalHookInfo,
null, dexDir == null ? null : dexDir.getAbsolutePath());
hookMethod.getDeclaringClass().getClassLoader(), dexDir == null ? null : dexDir.getAbsolutePath());
hookedInfo.put(hookMethod, hookMaker.getCallBackupMethod());
}
DexLog.d("hook method <" + hookMethod.toString() + "> cost " + (System.currentTimeMillis() - timeStart) + " ms, by " + (stub != null ? "internal stub." : "dex maker"));
@ -75,35 +79,18 @@ public final class SandHookXposedBridge {
}
}
private static void setupDexCachePath() {
// using file based DexClassLoader
if (!dexPathInited.compareAndSet(false, true)) {
public static void clearOatFile() {
String fixedAppDataDir = XposedCompat.getCacheDir().getAbsolutePath();
File dexOatDir = new File(fixedAppDataDir, "/hookers/oat/");
if (!dexOatDir.exists())
return;
}
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) + "/";
dexDir = new File(fixedAppDataDir, "/cache/sandxposed/"
+ getCurrentProcessName(Main.appProcessName).replace(":", "_") + "/");
dexDir.mkdirs();
FileUtils.delete(dexOatDir);
dexOatDir.mkdirs();
} catch (Throwable throwable) {
com.elderdrivers.riru.edxp.sandhook.dexmaker.DexLog.e("error when init dex path", throwable);
}
}
// public static void clearOatFile() {
// String fixedAppDataDir = XposedCompat.cacheDir.getAbsolutePath();
// File dexOatDir = new File(fixedAppDataDir, "/sandxposed/oat/");
// if (!dexOatDir.exists())
// return;
// try {
// FileUtils.delete(dexOatDir);
// dexOatDir.mkdirs();
// } catch (Throwable throwable) {
// }
// }
private static boolean checkMember(Member member) {
if (member instanceof Method) {
@ -124,37 +111,24 @@ public final class SandHookXposedBridge {
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
throws Throwable {
Method callBackup = hookedInfo.get(method);
if (callBackup == null) {
//method hook use internal stub
return SandHook.callOriginMethod(method, thisObject, args);
}
if (!Modifier.isStatic(callBackup.getModifiers())) {
throw new IllegalStateException("original method is not static, something must be wrong!");
}
callBackup.setAccessible(true);
if (args == null) {
args = new Object[0];
}
final int argsSize = args.length;
if (Modifier.isStatic(method.getModifiers())) {
return callBackup.invoke(null, args);
} else {
Object[] newArgs = new Object[argsSize + 1];
newArgs[0] = thisObject;
for (int i = 1; i < newArgs.length; i++) {
newArgs[i] = args[i - 1];
}
return callBackup.invoke(null, newArgs);
}
return SandHook.callOriginMethod(method, thisObject, args);
}
public static void setLibPath() {
public static void init() {
if (Process.is64Bit()) {
SandHookConfig.libSandHookPath = "/system/lib64/libsandhook.edxp.so";
} else {
SandHookConfig.libSandHookPath = "/system/lib/libsandhook.edxp.so";
}
SandHookConfig.libLoader = new SandHookConfig.LibLoader() {
@Override
public void loadLib() {
//do it in loadDexAndInit
}
};
SandHookConfig.DEBUG = true;
//in zygote disable compile
SandHookConfig.compiler = false;
}
}

View File

@ -7,12 +7,16 @@ import java.lang.reflect.Member;
public class DexLog {
public static final String TAG = "SandXposed-dexmaker";
public static final String TAG = "SandXposed";
public static boolean DEBUG = true;
public static volatile boolean DEBUG = true;
public static int v(String s) {
return Log.v(TAG, s);
if (DEBUG) {
return Log.v(TAG, s);
} else {
return 0;
}
}
public static int i(String s) {
@ -25,26 +29,38 @@ public class DexLog {
public static void printMethodHookIn(Member member) {
if (DEBUG && member != null) {
Log.d("SandHook-Xposed", "method <" + member.toString() + "> hook in");
Log.d("SandHook", "method <" + member.toString() + "> hook in");
}
}
public static void printCallOriginError(Member member) {
if (member != null) {
Log.e("SandHook-Xposed", "method <" + member.toString() + "> call origin error!");
if (DEBUG && member != null) {
Log.d("SandHook", "method <" + member.toString() + "> call origin error!");
}
}
public static int w(String s) {
return Log.w(TAG, s);
if (DEBUG) {
return Log.w(TAG, s);
} else {
return 0;
}
}
public static int e(String s) {
return Log.e(TAG, s);
if (DEBUG) {
return Log.e(TAG, s);
} else {
return 0;
}
}
public static int e(String s, Throwable t) {
return Log.e(TAG, s, t);
if (DEBUG) {
return Log.e(TAG, s, t);
} else {
return 0;
}
}

View File

@ -5,8 +5,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Process;
import android.text.TextUtils;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
@ -18,10 +22,10 @@ public class ProcessUtils {
private static volatile String processName = null;
public static String getProcessName(Context context) {
public static String getProcessName() {
if (!TextUtils.isEmpty(processName))
return processName;
processName = doGetProcessName(context);
processName = getProcessName(Process.myPid());
return processName;
}
@ -41,8 +45,35 @@ public class ProcessUtils {
return context.getPackageName();
}
public static String getProcessName(int pid) {
BufferedReader cmdlineReader = null;
try {
cmdlineReader = new BufferedReader(new InputStreamReader(
new FileInputStream(
"/proc/" + pid + "/cmdline"),
"iso-8859-1"));
int c;
StringBuilder processName = new StringBuilder();
while ((c = cmdlineReader.read()) > 0) {
processName.append((char) c);
}
return processName.toString();
} catch (Throwable throwable) {
DexLog.w("getProcessName: " + throwable.getMessage());
} finally {
try {
if (cmdlineReader != null) {
cmdlineReader.close();
}
} catch (Throwable throwable) {
DexLog.e("getProcessName: " + throwable.getMessage());
}
}
return "";
}
public static boolean isMainProcess(Context context) {
String processName = getProcessName(context);
String processName = getProcessName();
String pkgName = context.getPackageName();
if (!TextUtils.isEmpty(processName) && !TextUtils.equals(processName, pkgName)) {
return false;

View File

@ -1 +0,0 @@
libsandhook.edxp.so

Binary file not shown.

Binary file not shown.