From e858ebd70b24610668c70023b70cf088b2565ab9 Mon Sep 17 00:00:00 2001 From: solohsu Date: Wed, 20 Mar 2019 00:32:33 +0800 Subject: [PATCH] Introduce SandHook --- edxp-core/build.gradle | 22 + edxp-core/jni/main/include/config.h | 2 +- .../system/etc/public.libraries-edxp.txt | 1 + .../system/lib/libsandhook.edxp.so | Bin 0 -> 141008 bytes .../system/lib64/libsandhook.edxp.so | Bin 0 -> 235688 bytes edxp-sandhook/.gitignore | 1 + edxp-sandhook/build.gradle | 63 + edxp-sandhook/genhookstubs.py | 186 +++ edxp-sandhook/libs/framework-stub.jar | Bin 0 -> 15295 bytes edxp-sandhook/proguard-rules.pro | 33 + edxp-sandhook/src/main/AndroidManifest.xml | 1 + .../java/com/elderdrivers/riru/edxp/Main.java | 144 ++ .../sandhook/config/SandHookEdxpConfig.java | 23 + .../sandhook/config/SandHookProvider.java | 35 + .../riru/edxp/sandhook/core/HookMain.java | 186 +++ .../sandhook/core/HookMethodResolver.java | 155 ++ .../riru/edxp/sandhook/dexmaker/DexLog.java | 37 + .../edxp/sandhook/dexmaker/DexMakerUtils.java | 261 +++ .../edxp/sandhook/dexmaker/DynamicBridge.java | 130 ++ .../sandhook/dexmaker/HookerDexMaker.java | 569 +++++++ .../edxp/sandhook/dexmaker/MethodInfo.java | 94 ++ .../riru/edxp/sandhook/entry/Router.java | 126 ++ .../entry/bootstrap/AppBootstrapHookInfo.java | 14 + .../entry/bootstrap/SysBootstrapHookInfo.java | 16 + .../entry/bootstrap/SysInnerHookInfo.java | 10 + .../entry/bootstrap/WorkAroundHookInfo.java | 10 + .../entry/hooker/HandleBindAppHooker.java | 89 ++ .../hooker/LoadedApkConstructorHooker.java | 98 ++ .../entry/hooker/OnePlusWorkAroundHooker.java | 41 + .../hooker/StartBootstrapServicesHooker.java | 66 + .../entry/hooker/SystemMainHooker.java | 43 + .../entry/hooker/XposedBlackListHooker.java | 87 + .../entry/hooker/XposedInstallerHooker.java | 64 + .../sandhook/proxy/BlackWhiteListProxy.java | 132 ++ .../riru/edxp/sandhook/proxy/NormalProxy.java | 70 + .../sandhook/util/InlinedMethodCallers.java | 49 + .../sandhook/util/PrebuiltMethodsDeopter.java | 41 + .../sandhook/xposedcompat/XposedCompat.java | 48 + .../classloaders/ComposeClassLoader.java | 34 + .../hookstub/CallOriginCallBack.java | 5 + .../hookstub/HookMethodEntity.java | 93 ++ .../hookstub/HookStubManager.java | 409 +++++ .../hookstub/MethodHookerStubs32.java | 1413 +++++++++++++++++ .../hookstub/MethodHookerStubs64.java | 1413 +++++++++++++++++ .../xposedcompat/methodgen/ErrorCatch.java | 22 + .../xposedcompat/methodgen/HookMaker.java | 14 + .../methodgen/HookerDexMaker.java | 698 ++++++++ .../methodgen/HookerDexMakerNew.java | 298 ++++ .../methodgen/SandHookXposedBridge.java | 161 ++ .../xposedcompat/utils/ApplicationUtils.java | 34 + .../xposedcompat/utils/ClassLoaderUtils.java | 106 ++ .../sandhook/xposedcompat/utils/DexLog.java | 51 + .../xposedcompat/utils/DexMakerUtils.java | 224 +++ .../sandhook/xposedcompat/utils/DexUtils.java | 66 + .../xposedcompat/utils/FileUtils.java | 77 + .../xposedcompat/utils/ProcessUtils.java | 64 + .../riru/edxp/{yahfa => }/Main.java | 2 +- .../edxp/yahfa/config/YahfaEdxpConfig.java | 2 +- .../edxp/yahfa/config/YahfaHookProvider.java | 5 +- .../riru/edxp/yahfa/core/HookMain.java | 6 +- .../edxp/yahfa/core/HookMethodResolver.java | 2 +- .../edxp/yahfa/dexmaker/DexMakerUtils.java | 3 +- .../edxp/yahfa/dexmaker/DynamicBridge.java | 2 +- .../edxp/yahfa/dexmaker/HookerDexMaker.java | 2 +- .../entry/hooker/HandleBindAppHooker.java | 2 +- .../entry/hooker/OnePlusWorkAroundHooker.java | 2 +- .../edxp/yahfa/proxy/BlackWhiteListProxy.java | 4 +- .../riru/edxp/yahfa/proxy/NormalProxy.java | 2 +- .../yahfa/util/PrebuiltMethodsDeopter.java | 2 +- settings.gradle | 2 +- .../riru/edxp/hook/HookProvider.java | 2 +- .../de/robv/android/xposed/XposedBridge.java | 6 +- 72 files changed, 8150 insertions(+), 25 deletions(-) create mode 100644 edxp-core/template_override/system/etc/public.libraries-edxp.txt create mode 100644 edxp-core/template_override/system/lib/libsandhook.edxp.so create mode 100644 edxp-core/template_override/system/lib64/libsandhook.edxp.so create mode 100644 edxp-sandhook/.gitignore create mode 100644 edxp-sandhook/build.gradle create mode 100644 edxp-sandhook/genhookstubs.py create mode 100644 edxp-sandhook/libs/framework-stub.jar create mode 100644 edxp-sandhook/proguard-rules.pro create mode 100644 edxp-sandhook/src/main/AndroidManifest.xml create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexLog.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexMakerUtils.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DynamicBridge.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/MethodInfo.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/Router.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/AppBootstrapHookInfo.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysBootstrapHookInfo.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysInnerHookInfo.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/WorkAroundHookInfo.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/HandleBindAppHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/LoadedApkConstructorHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/OnePlusWorkAroundHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/StartBootstrapServicesHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/SystemMainHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedBlackListHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedInstallerHooker.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/InlinedMethodCallers.java create mode 100644 edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/PrebuiltMethodsDeopter.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/XposedCompat.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/classloaders/ComposeClassLoader.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/CallOriginCallBack.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookMethodEntity.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookStubManager.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs32.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs64.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/ErrorCatch.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookMaker.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMaker.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMakerNew.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/SandHookXposedBridge.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ApplicationUtils.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ClassLoaderUtils.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexLog.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexMakerUtils.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexUtils.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/FileUtils.java create mode 100644 edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ProcessUtils.java rename edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/{yahfa => }/Main.java (99%) diff --git a/edxp-core/build.gradle b/edxp-core/build.gradle index 3f22d263..9e1a3998 100644 --- a/edxp-core/build.gradle +++ b/edxp-core/build.gradle @@ -47,6 +47,28 @@ afterEvaluate { } pushTask.dependsOn(zipTask) + + + zipTask = task("zipSandhook${nameCapped}", type: Exec, dependsOn: ":edxp-sandhook:makeAndCopy${nameCapped}") { + workingDir '..' + commandLine 'sh', 'build.sh', \ + project.name, \ + "Sandhook-${project.version}-${nameLowered}", \ + "${project.extensions['module_name']}" + } + + pushTask = task("pushSandhook${nameCapped}", type: Exec) { + workingDir 'release' + def commands = ["adb", "push", "magisk-${project.extensions['module_name']}-Sandhook" + + "-${project.version}-${nameLowered}.zip", "/sdcard/"] + if (OperatingSystem.current().isWindows()) { + commandLine 'cmd', '/c', commands.join(" ") + } else { + commandLine commands + } + } + + pushTask.dependsOn(zipTask) } } diff --git a/edxp-core/jni/main/include/config.h b/edxp-core/jni/main/include/config.h index cd963cad..eda55916 100644 --- a/edxp-core/jni/main/include/config.h +++ b/edxp-core/jni/main/include/config.h @@ -10,6 +10,6 @@ #define INJECT_DEX_PATH \ "/system/framework/edxp.jar:/system/framework/eddalvikdx.jar:/system/framework/eddexmaker.jar" -#define ENTRY_CLASS_NAME "com.elderdrivers.riru.edxp.yahfa.Main" +#define ENTRY_CLASS_NAME "com.elderdrivers.riru.edxp.Main" #endif //CONFIG_H \ No newline at end of file diff --git a/edxp-core/template_override/system/etc/public.libraries-edxp.txt b/edxp-core/template_override/system/etc/public.libraries-edxp.txt new file mode 100644 index 00000000..3c8badba --- /dev/null +++ b/edxp-core/template_override/system/etc/public.libraries-edxp.txt @@ -0,0 +1 @@ +libsandhook.edxp.so diff --git a/edxp-core/template_override/system/lib/libsandhook.edxp.so b/edxp-core/template_override/system/lib/libsandhook.edxp.so new file mode 100644 index 0000000000000000000000000000000000000000..8232129121331342c95d691388944e85c8b7de1a GIT binary patch literal 141008 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

ql9s}%b0iI$a$s|2nPi&=3!57!J%0|xP4 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 WMQSD4QWBMKHmzg~2W%li%l;q9E){|R literal 0 HcmV?d00001 diff --git a/edxp-core/template_override/system/lib64/libsandhook.edxp.so b/edxp-core/template_override/system/lib64/libsandhook.edxp.so new file mode 100644 index 0000000000000000000000000000000000000000..cca107d37518458f57b3f5403fabaf39da1b238b GIT binary patch literal 235688 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%0DSd&)lJI?>^oFtr$52wV&e2w_pQ$?#7eAIcjYp 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¨^g_qcY*-NAhSQA> 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 literal 0 HcmV?d00001 diff --git a/edxp-sandhook/.gitignore b/edxp-sandhook/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/edxp-sandhook/.gitignore @@ -0,0 +1 @@ +/build diff --git a/edxp-sandhook/build.gradle b/edxp-sandhook/build.gradle new file mode 100644 index 00000000..322e96ff --- /dev/null +++ b/edxp-sandhook/build.gradle @@ -0,0 +1,63 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + + defaultConfig { + applicationId "com.elderdrivers.riru.edxp.sandhook" + minSdkVersion 26 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + compileOnly files("libs/framework-stub.jar") + implementation project(':edxp-common') + implementation project(':xposed-bridge') + implementation 'com.swift.sandhook:hooklib:3.0.1' + compileOnly project(':dexmaker') +} + + +preBuild.doLast { + def imlFile = file(project.name + ".iml") + println 'Change ' + project.name + '.iml order' + try { + def parsedXml = (new groovy.util.XmlParser()).parse(imlFile) + def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' } + parsedXml.component[1].remove(jdkNode) + def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform" + new groovy.util.Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK']) + groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile)) + } catch (FileNotFoundException e) { + // nop, iml not found + } +} + +afterEvaluate { + + tasks.withType(JavaCompile) { + options.compilerArgs.add("-Xbootclasspath/p:${projectDir.absolutePath}/libs/framework-stub.jar") + } + + android.applicationVariants.all { variant -> + def nameCapped = variant.name.capitalize() + def nameLowered = variant.name.toLowerCase() + + def makeAndCopyTask = task("makeAndCopy${nameCapped}", type: Jar, dependsOn: "assemble${nameCapped}") { + from "build/intermediates/dex/${nameLowered}/mergeDex${nameCapped}/out/" + destinationDir file("../edxp-core/template_override/system/framework/") + baseName "edxp" + } + } +} \ No newline at end of file diff --git a/edxp-sandhook/genhookstubs.py b/edxp-sandhook/genhookstubs.py new file mode 100644 index 00000000..feddcd89 --- /dev/null +++ b/edxp-sandhook/genhookstubs.py @@ -0,0 +1,186 @@ +#!/usr/bin/python + +import os + +STUB_FILE_NAME = "MethodHookerStubs" + +TEMP_STUB_CLASS_WRAPPER = """package com.swift.sandhook.xposedcompat.hookstub; + +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.hookBridge; +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.getMethodId; +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.originMethods; +import static com.swift.sandhook.xposedcompat.utils.DexLog.printCallOriginError; + +/** +* this file is auto gen by genhookstubs.py +* it is for sandhook internal hooker & backup methods +**/ +public class MethodHookerStubs%d { +%s +} +""" + +TEMP_STUB_HOOK_METHOD_NAME = """stub_hook_%d""" +TEMP_STUB_HOOK_BACKUP_NAME = """stub_backup_%d""" +TEMP_STUB_CALL_ORIGIN_NAME = """call_origin_%d_%d""" + +TEMP_STUB_GET_METHOD_ID_NAME = """getMethodId(%d, %d)""" + +JAVA_TYPE_INT = "int" +JAVA_CAST_INT = "(int)" +JAVA_TYPE_LONG = "long" + +TEMP_STUB_HOOK_METHOD = """ + public static %s %s(%s) throws Throwable { + return %s hookBridge(%s, %s %s); + } +""" + +TEMP_STUB_BACKUP_METHOD = """ + public static %s %s(%s) throws Throwable { + try { + printCallOriginError(originMethods[%s]); + } catch (Throwable throwable) {} + return 0; + } +""" + +TEMP_STUB_CALL_ORIGIN_CLASS = """ + static class %s implements CallOriginCallBack { + @Override + public long call(long... args) throws Throwable { + return %s(%s); + } + } +""" + +TEMP_STUB_INFO = """ + public static boolean hasStubBackup = %s; + public static int[] stubSizes = {%s}; +""" + + +STUB_SIZES = [10,20,30,30,30,30,30,20,10,10,5,5,3] +HAS_BACKUP = False; + + +def getMethodId(args, index): + return TEMP_STUB_GET_METHOD_ID_NAME % (args, index) + +def getMethodHookName(index): + return TEMP_STUB_HOOK_METHOD_NAME % index + +def getMethodBackupName(index): + return TEMP_STUB_HOOK_BACKUP_NAME % index + +def getCallOriginClassName(args, index): + return TEMP_STUB_CALL_ORIGIN_NAME % (args, index) + + +def genArgsList(is64Bit, isDefine, length): + args_list = "" + for i in range(length): + if (i != 0): + args_list += ", " + if isDefine: + if (is64Bit): + args_list += (JAVA_TYPE_LONG + " " + "a" + str(i)) + else: + args_list += (JAVA_TYPE_INT + " " + "a" + str(i)) + else: + args_list += ("a" + str(i)) + return args_list + + +def genArgsListForCallOriginMethod(is64Bit, length): + arg_name = """args[%s]""" + args_list = "" + for i in range(length): + if (i != 0): + args_list += ", " + if (is64Bit): + args_list += arg_name % i + else: + args_list += (JAVA_CAST_INT + arg_name % i) + return args_list + + +def genHookMethod(is64Bit, args, index): + java_type = JAVA_TYPE_LONG if is64Bit else JAVA_TYPE_INT + cast = "" if is64Bit else JAVA_CAST_INT + args_list_pre = ", " if args > 0 else "" + args_list = genArgsList(is64Bit, False, args) + args_list_def = genArgsList(is64Bit, True, args) + call_origin_obj = ("new " + getCallOriginClassName(args, index) + "()") if HAS_BACKUP else "null" + method = TEMP_STUB_HOOK_METHOD % (java_type, getMethodHookName(index), args_list_def, cast, getMethodId(args, index), call_origin_obj, args_list_pre + args_list) + return method + + +def genBackupMethod(is64Bit, args, index): + java_type = JAVA_TYPE_LONG if is64Bit else JAVA_TYPE_INT + args_list_def = genArgsList(is64Bit, True, args) + method = TEMP_STUB_BACKUP_METHOD % (java_type, getMethodBackupName(index), args_list_def, getMethodId(args, index)) + return method + +def genCallOriginClass(is64Bit, args, index): + method = TEMP_STUB_CALL_ORIGIN_CLASS % (getCallOriginClassName(args, index), getMethodBackupName(index), genArgsListForCallOriginMethod(is64Bit, args)) + return method + +def genStubInfo(): + hasStub = "true" if HAS_BACKUP else "false" + stubSizes = "" + for args in range(len(STUB_SIZES)): + if (args != 0): + stubSizes += ", " + stubSizes += str(STUB_SIZES[args]) + return TEMP_STUB_INFO % (hasStub, stubSizes) + +def gen32Stub(packageDir): + class_content = genStubInfo() + class_name = STUB_FILE_NAME + "32" + for args in range(len(STUB_SIZES)): + for index in range(STUB_SIZES[args]): + class_content += """\n\n\t//stub of arg size %d, index %d""" % (args, index) + class_content += genHookMethod(False, args, index) + if HAS_BACKUP: + class_content += "\n" + class_content += genCallOriginClass(False, args, index) + class_content += "\n" + class_content += genBackupMethod(False, args, index) + class_content += "\n" + class_str = TEMP_STUB_CLASS_WRAPPER % (32, class_content) + javaFile = open(os.path.join(packageDir, class_name + ".java"), "w") + javaFile.write(class_str) + javaFile.close() + + +def gen64Stub(packageDir): + class_content = genStubInfo() + class_name = STUB_FILE_NAME + "64" + for args in range(len(STUB_SIZES)): + for index in range(STUB_SIZES[args]): + class_content += """\n\n\t//stub of arg size %d, index %d""" % (args, index) + class_content += genHookMethod(True, args, index) + if HAS_BACKUP: + class_content += "\n" + class_content += genCallOriginClass(True, args, index) + class_content += "\n" + class_content += genBackupMethod(True, args, index) + class_content += "\n" + class_str = TEMP_STUB_CLASS_WRAPPER % (64, class_content) + javaFile = open(os.path.join(packageDir, class_name + ".java"), "w") + javaFile.write(class_str) + javaFile.close() + + +def genStub(packageDir): + for fileName in os.listdir(packageDir): + if fileName.startswith(STUB_FILE_NAME): + os.remove(os.path.join(packageDir, fileName)) + gen32Stub(packageDir) + gen64Stub(packageDir) + + +if __name__ == "__main__": + genStub(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "src/main/java/com/swift/sandhook/xposedcompat/hookstub")) diff --git a/edxp-sandhook/libs/framework-stub.jar b/edxp-sandhook/libs/framework-stub.jar new file mode 100644 index 0000000000000000000000000000000000000000..36cd86b3c4fd658ed66ff5195ae0cc54d6ebc485 GIT binary patch literal 15295 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 SP*C)sAAQg^FUtb{>;D0!&FVP- literal 0 HcmV?d00001 diff --git a/edxp-sandhook/proguard-rules.pro b/edxp-sandhook/proguard-rules.pro new file mode 100644 index 00000000..48bb9c90 --- /dev/null +++ b/edxp-sandhook/proguard-rules.pro @@ -0,0 +1,33 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-dontobfuscate +-keep class de.robv.android.xposed.** {*;} +-keep class android.** { *; } + +-keep interface com.elderdrivers.riru.common.KeepAll +-keep interface com.elderdrivers.riru.common.KeepMembers + +-keep class * implements com.elderdrivers.riru.common.KeepAll { *; } +-keepclassmembers class * implements com.elderdrivers.riru.common.KeepMembers { *; } + +-keep class com.swift.sandhook.** {*;} \ No newline at end of file diff --git a/edxp-sandhook/src/main/AndroidManifest.xml b/edxp-sandhook/src/main/AndroidManifest.xml new file mode 100644 index 00000000..4d54a3c5 --- /dev/null +++ b/edxp-sandhook/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java new file mode 100644 index 00000000..b4f0ab86 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -0,0 +1,144 @@ +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.sandhook.BuildConfig; +import com.elderdrivers.riru.edxp.config.InstallerChooser; +import com.elderdrivers.riru.edxp.util.Utils; +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.methodgen.SandHookXposedBridge; + +import java.lang.reflect.Method; +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 { + init(Build.VERSION.SDK_INT); + HookMethodResolver.init(); + Router.injectConfig(); + InstallerChooser.setInstallerPackageName(getInstallerPkgName()); + SandHookXposedBridge.setLibPath(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // 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 (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 (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 (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 (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 + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // native methods + /////////////////////////////////////////////////////////////////////////////////////////////// + + public static native boolean backupAndHookNative(Object target, Method hook, Method backup); + + public static native void setMethodNonCompilable(Object member); + + public static native void ensureMethodCached(Method hook, Method backup); + + // JNI.ToReflectedMethod() could return either Method or Constructor + public static native Object findMethodNative(Class targetClass, String methodName, String methodSig); + + private static native void init(int SDK_version); + + public static native String getInstallerPkgName(); + + public static native boolean isBlackWhiteListEnabled(); + + public static native boolean isDynamicModulesEnabled(); + + public static native boolean isAppNeedHook(String appDataDir); + + // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote + // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff + public static native void closeFilesBeforeForkNative(); + + public static native void reopenFilesAfterForkNative(); + + public static native void deoptMethodNative(Object object); + + public static native long suspendAllThreads(); + + public static native void resumeAllThreads(long obj); + + public static native int waitForGcToComplete(long thread); +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java new file mode 100644 index 00000000..c3a62805 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java @@ -0,0 +1,23 @@ +package com.elderdrivers.riru.edxp.sandhook.config; + +import com.elderdrivers.riru.edxp.config.EdXpConfig; +import com.elderdrivers.riru.edxp.config.InstallerChooser; +import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.XposedBlackListHooker; + +public class SandHookEdxpConfig implements EdXpConfig { + @Override + public String getInstallerBaseDir() { + return InstallerChooser.INSTALLER_DATA_BASE_DIR; + } + + @Override + public String getBlackListModulePackageName() { + return XposedBlackListHooker.BLACK_LIST_PACKAGE_NAME; + } + + @Override + public boolean isDynamicModulesMode() { + return Main.isDynamicModulesEnabled(); + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java new file mode 100644 index 00000000..da08315b --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java @@ -0,0 +1,35 @@ +package com.elderdrivers.riru.edxp.sandhook.config; + +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; + +public class SandHookProvider implements HookProvider { + @Override + public void hookMethod(Member method, XposedBridge.AdditionalHookInfo additionalInfo) { + XposedCompat.hookMethod(method, additionalInfo); + } + + @Override + public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable { + return SandHookXposedBridge.invokeOriginalMethod(method, thisObject, args); + } + + @Override + public Member findMethodNative(Member hookMethod) { + return DexMakerUtils.findMethodNative(hookMethod); + } + + @Override + public void deoptMethods(String packageName, ClassLoader classLoader) { + PrebuiltMethodsDeopter.deoptMethods(packageName, classLoader); + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java new file mode 100644 index 00000000..eeaa50ca --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java @@ -0,0 +1,186 @@ +package com.elderdrivers.riru.edxp.sandhook.core; + +import com.elderdrivers.riru.edxp.util.Utils; +import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.sandhook.entry.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; + +import static com.elderdrivers.riru.edxp.Main.backupAndHookNative; +import static com.elderdrivers.riru.edxp.Main.findMethodNative; + +public class HookMain { + + private static Set 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 = Main.waitForGcToComplete( + XposedHelpers.getLongField(currentThread, "nativePeer")); + if (lastGcType < 0) { + Utils.logW("waitForGcToComplete failed, using fallback"); + Runtime.getRuntime().gc(); + } + if (!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 findMethodNative(cls, methodName, methodSig); + } + + private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) { + ArrayList> 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> 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. " + "" + ": " + "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)); + } + } + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java new file mode 100644 index 00000000..c5290914 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java @@ -0,0 +1,155 @@ +package com.elderdrivers.riru.edxp.sandhook.core; + +import android.os.Build; + +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) { + Main.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; + } + } + +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexLog.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexLog.java new file mode 100644 index 00000000..b2dd0f11 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexLog.java @@ -0,0 +1,37 @@ +package com.elderdrivers.riru.edxp.sandhook.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); + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexMakerUtils.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexMakerUtils.java new file mode 100644 index 00000000..5ad71082 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DexMakerUtils.java @@ -0,0 +1,261 @@ +package com.elderdrivers.riru.edxp.sandhook.dexmaker; + +import android.app.AndroidAppHelper; +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 java.lang.reflect.Member; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.Map; + +import external.com.android.dx.Code; +import external.com.android.dx.Local; +import external.com.android.dx.TypeId; + +public class DexMakerUtils { + + private static final boolean IN_MEMORY_DEX_ELIGIBLE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + + public static boolean shouldUseInMemoryHook() { + if (!IN_MEMORY_DEX_ELIGIBLE) { + return false; + } + String packageName = AndroidAppHelper.currentPackageName(); + if (TextUtils.isEmpty(packageName)) { //default to true + DexLog.w("packageName is empty, processName=" + Main.appProcessName + + ", appDataDir=" + Main.appDataDir); + return true; + } + return !ConfigManager.shouldUseCompatMode(packageName); + } + + public static void autoBoxIfNecessary(Code code, Local target, Local source) { + String boxMethod = "valueOf"; + TypeId boxTypeId; + TypeId typeId = source.getType(); + if (typeId.equals(TypeId.BOOLEAN)) { + boxTypeId = TypeId.get(Boolean.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.BOOLEAN), target, source); + } else if (typeId.equals(TypeId.BYTE)) { + boxTypeId = TypeId.get(Byte.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.BYTE), target, source); + } else if (typeId.equals(TypeId.CHAR)) { + boxTypeId = TypeId.get(Character.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.CHAR), target, source); + } else if (typeId.equals(TypeId.DOUBLE)) { + boxTypeId = TypeId.get(Double.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.DOUBLE), target, source); + } else if (typeId.equals(TypeId.FLOAT)) { + boxTypeId = TypeId.get(Float.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.FLOAT), target, source); + } else if (typeId.equals(TypeId.INT)) { + boxTypeId = TypeId.get(Integer.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.INT), target, source); + } else if (typeId.equals(TypeId.LONG)) { + boxTypeId = TypeId.get(Long.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.LONG), target, source); + } else if (typeId.equals(TypeId.SHORT)) { + boxTypeId = TypeId.get(Short.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.SHORT), target, source); + } else if (typeId.equals(TypeId.VOID)) { + code.loadConstant(target, null); + } else { + code.move(target, source); + } + } + + public static void autoUnboxIfNecessary(Code code, Local target, Local source, + Map tmpLocals, boolean castObj) { + String unboxMethod; + TypeId typeId = target.getType(); + TypeId boxTypeId; + if (typeId.equals(TypeId.BOOLEAN)) { + unboxMethod = "booleanValue"; + boxTypeId = TypeId.get("Ljava/lang/Boolean;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.BOOLEAN, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.BYTE)) { + unboxMethod = "byteValue"; + boxTypeId = TypeId.get("Ljava/lang/Byte;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.BYTE, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.CHAR)) { + unboxMethod = "charValue"; + boxTypeId = TypeId.get("Ljava/lang/Character;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.CHAR, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.DOUBLE)) { + unboxMethod = "doubleValue"; + boxTypeId = TypeId.get("Ljava/lang/Double;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.DOUBLE, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.FLOAT)) { + unboxMethod = "floatValue"; + boxTypeId = TypeId.get("Ljava/lang/Float;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.FLOAT, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.INT)) { + unboxMethod = "intValue"; + boxTypeId = TypeId.get("Ljava/lang/Integer;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.INT, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.LONG)) { + unboxMethod = "longValue"; + boxTypeId = TypeId.get("Ljava/lang/Long;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.LONG, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.SHORT)) { + unboxMethod = "shortValue"; + boxTypeId = TypeId.get("Ljava/lang/Short;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.SHORT, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.VOID)) { + code.loadConstant(target, null); + } else if (castObj) { + code.cast(target, source); + } else { + code.move(target, source); + } + } + + public static Map createResultLocals(Code code) { + HashMap resultMap = new HashMap<>(); + Local booleanLocal = code.newLocal(TypeId.BOOLEAN); + Local byteLocal = code.newLocal(TypeId.BYTE); + Local charLocal = code.newLocal(TypeId.CHAR); + Local doubleLocal = code.newLocal(TypeId.DOUBLE); + Local floatLocal = code.newLocal(TypeId.FLOAT); + Local intLocal = code.newLocal(TypeId.INT); + Local longLocal = code.newLocal(TypeId.LONG); + Local shortLocal = code.newLocal(TypeId.SHORT); + Local voidLocal = code.newLocal(TypeId.VOID); + Local objectLocal = code.newLocal(TypeId.OBJECT); + + Local booleanObjLocal = code.newLocal(TypeId.get("Ljava/lang/Boolean;")); + Local byteObjLocal = code.newLocal(TypeId.get("Ljava/lang/Byte;")); + Local charObjLocal = code.newLocal(TypeId.get("Ljava/lang/Character;")); + Local doubleObjLocal = code.newLocal(TypeId.get("Ljava/lang/Double;")); + Local floatObjLocal = code.newLocal(TypeId.get("Ljava/lang/Float;")); + Local intObjLocal = code.newLocal(TypeId.get("Ljava/lang/Integer;")); + Local longObjLocal = code.newLocal(TypeId.get("Ljava/lang/Long;")); + Local shortObjLocal = code.newLocal(TypeId.get("Ljava/lang/Short;")); + Local voidObjLocal = code.newLocal(TypeId.get("Ljava/lang/Void;")); + + // backup need initialized locals + code.loadConstant(booleanLocal, false); + code.loadConstant(byteLocal, (byte) 0); + code.loadConstant(charLocal, '\0'); + code.loadConstant(doubleLocal, 0.0); + code.loadConstant(floatLocal, 0.0f); + code.loadConstant(intLocal, 0); + code.loadConstant(longLocal, 0L); + code.loadConstant(shortLocal, (short) 0); + code.loadConstant(voidLocal, null); + code.loadConstant(objectLocal, null); + // all to null + code.loadConstant(booleanObjLocal, null); + code.loadConstant(byteObjLocal, null); + code.loadConstant(charObjLocal, null); + code.loadConstant(doubleObjLocal, null); + code.loadConstant(floatObjLocal, null); + code.loadConstant(intObjLocal, null); + code.loadConstant(longObjLocal, null); + code.loadConstant(shortObjLocal, null); + code.loadConstant(voidObjLocal, null); + // package all + resultMap.put(TypeId.BOOLEAN, booleanLocal); + resultMap.put(TypeId.BYTE, byteLocal); + resultMap.put(TypeId.CHAR, charLocal); + resultMap.put(TypeId.DOUBLE, doubleLocal); + resultMap.put(TypeId.FLOAT, floatLocal); + resultMap.put(TypeId.INT, intLocal); + resultMap.put(TypeId.LONG, longLocal); + resultMap.put(TypeId.SHORT, shortLocal); + resultMap.put(TypeId.VOID, voidLocal); + resultMap.put(TypeId.OBJECT, objectLocal); + + resultMap.put(TypeId.get("Ljava/lang/Boolean;"), booleanObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Byte;"), byteObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Character;"), charObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Double;"), doubleObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Float;"), floatObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Integer;"), intObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Long;"), longObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Short;"), shortObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Void;"), voidObjLocal); + + return resultMap; + } + + public static TypeId getObjTypeIdIfPrimitive(TypeId typeId) { + if (typeId.equals(TypeId.BOOLEAN)) { + return TypeId.get("Ljava/lang/Boolean;"); + } else if (typeId.equals(TypeId.BYTE)) { + return TypeId.get("Ljava/lang/Byte;"); + } else if (typeId.equals(TypeId.CHAR)) { + return TypeId.get("Ljava/lang/Character;"); + } else if (typeId.equals(TypeId.DOUBLE)) { + return TypeId.get("Ljava/lang/Double;"); + } else if (typeId.equals(TypeId.FLOAT)) { + return TypeId.get("Ljava/lang/Float;"); + } else if (typeId.equals(TypeId.INT)) { + return TypeId.get("Ljava/lang/Integer;"); + } else if (typeId.equals(TypeId.LONG)) { + return TypeId.get("Ljava/lang/Long;"); + } else if (typeId.equals(TypeId.SHORT)) { + return TypeId.get("Ljava/lang/Short;"); + } else if (typeId.equals(TypeId.VOID)) { + return TypeId.get("Ljava/lang/Void;"); + } else { + return typeId; + } + } + + public static void returnRightValue(Code code, Class returnType, Map resultLocals) { + String unboxMethod; + TypeId boxTypeId; + code.returnValue(resultLocals.get(returnType)); + } + + public static String getSha1Hex(String text) { + final MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + byte[] result = digest.digest(text.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte b : result) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (Exception e) { + DexLog.e("error hashing target method: " + text, e); + } + return ""; + } + + public static Member findMethodNative(Member hookMethod) { + MethodInfo methodInfo = new MethodInfo(hookMethod); + Class declaringClass = methodInfo.getClassForSure(); + Member reflectMethod = (Member) HookMain.findMethod( + declaringClass, methodInfo.methodName, methodInfo.methodSig); + if (reflectMethod == null) { + DexLog.e("method not found: name=" + + methodInfo.methodName + ", sig=" + methodInfo.methodSig); + reflectMethod = hookMethod; + } + return reflectMethod; + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DynamicBridge.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DynamicBridge.java new file mode 100644 index 00000000..66fca6c7 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/DynamicBridge.java @@ -0,0 +1,130 @@ +package com.elderdrivers.riru.edxp.sandhook.dexmaker; + +import com.elderdrivers.riru.edxp.Main; + +import java.io.File; +import java.lang.reflect.Constructor; +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.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; +import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.shouldUseInMemoryHook; + +public final class DynamicBridge { + + private static final HashMap hookedInfo = new HashMap<>(); + private static final HookerDexMaker dexMaker = new HookerDexMaker(); + private static final AtomicBoolean dexPathInited = new AtomicBoolean(false); + private static File dexDir; + + /** + * Reset dexPathInited flag once we enter child process + * since it might have been set to true in zygote process + */ + public static void onForkPost() { + dexPathInited.set(false); + } + + public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) { + DexLog.d("hooking " + hookMethod); + if (!checkMember(hookMethod)) { + return; + } + + if (hookedInfo.containsKey(hookMethod)) { + DexLog.w("already hook method:" + hookMethod.toString()); + return; + } + + DexLog.d("start to generate class for: " + hookMethod); + try { + // for Android Oreo and later use InMemoryClassLoader + if (!shouldUseInMemoryHook()) { + setupDexCachePath(); + } + dexMaker.start(hookMethod, additionalHookInfo, + hookMethod.getDeclaringClass().getClassLoader(), getDexDirPath()); + hookedInfo.put(hookMethod, dexMaker.getCallBackupMethod()); + } catch (Exception e) { + DexLog.e("error occur when generating dex. dexDir=" + dexDir, e); + } + } + + private static String getDexDirPath() { + if (dexDir == null) { + return null; + } + return dexDir.getAbsolutePath(); + } + + private static void setupDexCachePath() { + // using file based DexClassLoader + if (!dexPathInited.compareAndSet(false, true)) { + 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/edhookers/" + + getCurrentProcessName(Main.appProcessName).replace(":", "_") + "/"); + dexDir.mkdirs(); + } catch (Throwable throwable) { + DexLog.e("error when init dex path", throwable); + } + } + + private static boolean checkMember(Member member) { + + if (member instanceof Method) { + return true; + } else if (member instanceof Constructor) { + return true; + } else if (member.getDeclaringClass().isInterface()) { + DexLog.e("Cannot hook interfaces: " + member.toString()); + return false; + } else if (Modifier.isAbstract(member.getModifiers())) { + DexLog.e("Cannot hook abstract methods: " + member.toString()); + return false; + } else { + DexLog.e("Only methods and constructors can be hooked: " + member.toString()); + return false; + } + } + + public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) + throws InvocationTargetException, IllegalAccessException { + Method callBackup = hookedInfo.get(method); + if (callBackup == null) { + throw new IllegalStateException("method not hooked, cannot call original method."); + } + 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); + } + } +} + + diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java new file mode 100644 index 00000000..537a217a --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java @@ -0,0 +1,569 @@ +package com.elderdrivers.riru.edxp.sandhook.dexmaker; + +import android.annotation.TargetApi; +import android.os.Build; +import android.text.TextUtils; + +import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.sandhook.core.HookMain; + +import java.io.File; +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 java.util.concurrent.atomic.AtomicLong; + +import dalvik.system.InMemoryDexClassLoader; +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; +import external.com.android.dx.BinaryOp; +import external.com.android.dx.Code; +import external.com.android.dx.Comparison; +import external.com.android.dx.DexMaker; +import external.com.android.dx.FieldId; +import external.com.android.dx.Label; +import external.com.android.dx.Local; +import external.com.android.dx.MethodId; +import external.com.android.dx.TypeId; + +import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.autoBoxIfNecessary; +import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.autoUnboxIfNecessary; +import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.createResultLocals; +import static com.elderdrivers.riru.edxp.sandhook.dexmaker.DexMakerUtils.getObjTypeIdIfPrimitive; + +public class HookerDexMaker { + + public static final String METHOD_NAME_BACKUP = "backup"; + public static final String METHOD_NAME_HOOK = "hook"; + public static final String METHOD_NAME_CALL_BACKUP = "callBackup"; + public static final String METHOD_NAME_SETUP = "setup"; + public static final TypeId objArrayTypeId = TypeId.get(Object[].class); + private static final String CLASS_DESC_PREFIX = "L"; + /** + * Note: this identifier is used in native codes to pass class access verification. + */ + private static final String CLASS_NAME_PREFIX = "EdHooker_"; + private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; + private static final String FIELD_NAME_METHOD = "method"; + private static final String PARAMS_FIELD_NAME_METHOD = "method"; + private static final String PARAMS_FIELD_NAME_THIS_OBJECT = "thisObject"; + private static final String PARAMS_FIELD_NAME_ARGS = "args"; + private static final String CALLBACK_METHOD_NAME_BEFORE = "callBeforeHookedMethod"; + private static final String CALLBACK_METHOD_NAME_AFTER = "callAfterHookedMethod"; + private static final String PARAMS_METHOD_NAME_IS_EARLY_RETURN = "isEarlyReturn"; + private static final TypeId throwableTypeId = TypeId.get(Throwable.class); + private static final TypeId memberTypeId = TypeId.get(Member.class); + private static final TypeId callbackTypeId = TypeId.get(XC_MethodHook.class); + private static final TypeId hookInfoTypeId + = TypeId.get(XposedBridge.AdditionalHookInfo.class); + private static final TypeId callbacksTypeId + = TypeId.get(XposedBridge.CopyOnWriteSortedSet.class); + private static final TypeId paramTypeId + = TypeId.get(XC_MethodHook.MethodHookParam.class); + private static final MethodId setResultMethodId = + paramTypeId.getMethod(TypeId.VOID, "setResult", TypeId.OBJECT); + private static final MethodId setThrowableMethodId = + paramTypeId.getMethod(TypeId.VOID, "setThrowable", throwableTypeId); + private static final MethodId getResultMethodId = + paramTypeId.getMethod(TypeId.OBJECT, "getResult"); + private static final MethodId getThrowableMethodId = + paramTypeId.getMethod(throwableTypeId, "getThrowable"); + private static final MethodId hasThrowableMethodId = + paramTypeId.getMethod(TypeId.BOOLEAN, "hasThrowable"); + private static final MethodId callAfterCallbackMethodId = + callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_AFTER, paramTypeId); + private static final MethodId callBeforeCallbackMethodId = + callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_BEFORE, paramTypeId); + private static final FieldId returnEarlyFieldId = + paramTypeId.getField(TypeId.BOOLEAN, "returnEarly"); + private static final TypeId xposedBridgeTypeId = TypeId.get(XposedBridge.class); + private static final MethodId logThrowableMethodId = + xposedBridgeTypeId.getMethod(TypeId.VOID, "log", throwableTypeId); + private static final MethodId logStrMethodId = + xposedBridgeTypeId.getMethod(TypeId.VOID, "log", TypeId.STRING); + + private static AtomicLong sClassNameSuffix = new AtomicLong(1); + + private FieldId mHookInfoFieldId; + private FieldId mMethodFieldId; + private MethodId mBackupMethodId; + private MethodId mCallBackupMethodId; + private MethodId mHookMethodId; + + private TypeId mHookerTypeId; + private TypeId[] mParameterTypeIds; + private Class[] mActualParameterTypes; + private Class mReturnType; + private TypeId mReturnTypeId; + private boolean mIsStatic; + // TODO use this to generate methods + private boolean mHasThrowable; + + private DexMaker mDexMaker; + private Member mMember; + private XposedBridge.AdditionalHookInfo mHookInfo; + private ClassLoader mAppClassLoader; + private Class mHookClass; + private Method mHookMethod; + private Method mBackupMethod; + private Method mCallBackupMethod; + private String mDexDirPath; + + private static TypeId[] getParameterTypeIds(Class[] parameterTypes, boolean isStatic) { + int parameterSize = parameterTypes.length; + int targetParameterSize = isStatic ? parameterSize : parameterSize + 1; + TypeId[] parameterTypeIds = new TypeId[targetParameterSize]; + int offset = 0; + if (!isStatic) { + parameterTypeIds[0] = TypeId.OBJECT; + offset = 1; + } + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypeIds[i + offset] = TypeId.get(parameterTypes[i]); + } + return parameterTypeIds; + } + + private static Class[] getParameterTypes(Class[] parameterTypes, boolean isStatic) { + if (isStatic) { + return parameterTypes; + } + int parameterSize = parameterTypes.length; + int targetParameterSize = parameterSize + 1; + Class[] newParameterTypes = new Class[targetParameterSize]; + int offset = 1; + newParameterTypes[0] = Object.class; + System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length); + return newParameterTypes; + } + + public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, + ClassLoader appClassLoader, String dexDirPath) throws Exception { + if (member instanceof Method) { + Method method = (Method) member; + mIsStatic = Modifier.isStatic(method.getModifiers()); + mReturnType = method.getReturnType(); + if (mReturnType.equals(Void.class) || mReturnType.equals(void.class) + || mReturnType.isPrimitive()) { + mReturnTypeId = TypeId.get(mReturnType); + } else { + // all others fallback to plain Object for convenience + mReturnType = Object.class; + mReturnTypeId = TypeId.OBJECT; + } + mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic); + mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic); + mHasThrowable = method.getExceptionTypes().length > 0; + } else if (member instanceof Constructor) { + Constructor constructor = (Constructor) member; + mIsStatic = false; + mReturnType = void.class; + mReturnTypeId = TypeId.VOID; + mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic); + mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic); + mHasThrowable = constructor.getExceptionTypes().length > 0; + } else if (member.getDeclaringClass().isInterface()) { + throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString()); + } else if (Modifier.isAbstract(member.getModifiers())) { + throw new IllegalArgumentException("Cannot hook abstract methods: " + member.toString()); + } else { + throw new IllegalArgumentException("Only methods and constructors can be hooked: " + member.toString()); + } + mMember = member; + mHookInfo = hookInfo; + mDexDirPath = dexDirPath; + if (appClassLoader == null + || appClassLoader.getClass().getName().equals("java.lang.BootClassLoader")) { + mAppClassLoader = this.getClass().getClassLoader(); + } else { + mAppClassLoader = appClassLoader; + } + doMake(); + } + + @TargetApi(Build.VERSION_CODES.O) + private void doMake() throws Exception { + final boolean useInMemoryCl = TextUtils.isEmpty(mDexDirPath); + mDexMaker = new DexMaker(); + ClassLoader loader; + // Generate a Hooker class. + String className = CLASS_NAME_PREFIX; + if (!useInMemoryCl) { + // if not using InMemoryDexClassLoader, className is also used as dex file name + // so it should be different from each other + String suffix = DexMakerUtils.getSha1Hex(mMember.toString()); + if (TextUtils.isEmpty(suffix)) { // just in case + suffix = String.valueOf(sClassNameSuffix.getAndIncrement()); + } + className = className + suffix; + if (!new File(mDexDirPath, className).exists()) { + // if file exists, reuse it and skip generating + doGenerate(className); + } + // load dex file from disk + loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), className); + } else { + // do everything in memory + doGenerate(className); + byte[] dexBytes = mDexMaker.generate(); + loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader); + } + + mHookClass = loader.loadClass(className); + // Execute our newly-generated code in-process. + mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class) + .invoke(null, mMember, mHookInfo); + mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); + mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); + mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); + Main.setMethodNonCompilable(mCallBackupMethod); + HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod); + } + + private void doGenerate(String className) { + String classDesc = CLASS_DESC_PREFIX + className + ";"; + mHookerTypeId = TypeId.get(classDesc); + mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT); + generateFields(); + generateSetupMethod(); + generateBackupMethod(); + generateHookMethod(); + generateCallBackupMethod(); + } + + public Method getHookMethod() { + return mHookMethod; + } + + public Method getBackupMethod() { + return mBackupMethod; + } + + public Method getCallBackupMethod() { + return mCallBackupMethod; + } + + public Class getHookClass() { + return mHookClass; + } + + private void generateFields() { + mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO); + mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD); + mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null); + mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null); + } + + private void generateSetupMethod() { + MethodId setupMethodId = mHookerTypeId.getMethod( + TypeId.VOID, METHOD_NAME_SETUP, memberTypeId, hookInfoTypeId); + Code code = mDexMaker.declare(setupMethodId, Modifier.PUBLIC | Modifier.STATIC); + // init logic + // get parameters + Local method = code.getParameter(0, memberTypeId); + Local hookInfo = code.getParameter(1, hookInfoTypeId); + // save params to static + code.sput(mMethodFieldId, method); + code.sput(mHookInfoFieldId, hookInfo); + code.returnVoid(); + } + + private void generateBackupMethod() { + mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds); + Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + Map resultLocals = createResultLocals(code); + // do nothing + if (mReturnTypeId.equals(TypeId.VOID)) { + code.returnVoid(); + } else { + // we have limited the returnType to primitives or Object, so this should be safe + code.returnValue(resultLocals.get(mReturnTypeId)); + } + } + + private void generateCallBackupMethod() { + mCallBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_CALL_BACKUP, mParameterTypeIds); + Code code = mDexMaker.declare(mCallBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + // just call backup and return its result + Local[] allArgsLocals = createParameterLocals(code); + Map resultLocals = createResultLocals(code); + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mBackupMethodId, null, allArgsLocals); + code.returnVoid(); + } else { + Local result = resultLocals.get(mReturnTypeId); + code.invokeStatic(mBackupMethodId, result, allArgsLocals); + code.returnValue(result); + } + } + + private void generateHookMethod() { + mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds); + Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC); + + // code starts + + // prepare common labels + Label noHookReturn = new Label(); + Label incrementAndCheckBefore = new Label(); + Label tryBeforeCatch = new Label(); + Label noExceptionBefore = new Label(); + Label checkAndCallBackup = new Label(); + Label beginCallBefore = new Label(); + Label beginCallAfter = new Label(); + Label tryOrigCatch = new Label(); + Label noExceptionOrig = new Label(); + Label tryAfterCatch = new Label(); + Label decrementAndCheckAfter = new Label(); + Label noBackupThrowable = new Label(); + Label throwThrowable = new Label(); + // prepare locals + Local disableHooks = code.newLocal(TypeId.BOOLEAN); + Local hookInfo = code.newLocal(hookInfoTypeId); + Local callbacks = code.newLocal(callbacksTypeId); + Local snapshot = code.newLocal(objArrayTypeId); + Local snapshotLen = code.newLocal(TypeId.INT); + Local callbackObj = code.newLocal(TypeId.OBJECT); + Local callback = code.newLocal(callbackTypeId); + + Local resultObj = code.newLocal(TypeId.OBJECT); // as a temp Local + Local one = code.newLocal(TypeId.INT); + Local nullObj = code.newLocal(TypeId.OBJECT); + Local throwable = code.newLocal(throwableTypeId); + + Local param = code.newLocal(paramTypeId); + Local method = code.newLocal(memberTypeId); + Local thisObject = code.newLocal(TypeId.OBJECT); + Local args = code.newLocal(objArrayTypeId); + Local returnEarly = code.newLocal(TypeId.BOOLEAN); + + Local actualParamSize = code.newLocal(TypeId.INT); + Local argIndex = code.newLocal(TypeId.INT); + + Local beforeIdx = code.newLocal(TypeId.INT); + Local lastResult = code.newLocal(TypeId.OBJECT); + Local lastThrowable = code.newLocal(throwableTypeId); + Local hasThrowable = code.newLocal(TypeId.BOOLEAN); + + Local[] allArgsLocals = createParameterLocals(code); + + Map resultLocals = createResultLocals(code); + + code.loadConstant(args, null); + code.loadConstant(argIndex, 0); + code.loadConstant(one, 1); + code.loadConstant(snapshotLen, 0); + code.loadConstant(nullObj, null); + + // check XposedBridge.disableHooks flag + + FieldId disableHooksField = + xposedBridgeTypeId.getField(TypeId.BOOLEAN, "disableHooks"); + code.sget(disableHooksField, disableHooks); + // disableHooks == true => no hooking + code.compareZ(Comparison.NE, noHookReturn, disableHooks); + + // check callbacks length + code.sget(mHookInfoFieldId, hookInfo); + code.iget(hookInfoTypeId.getField(callbacksTypeId, "callbacks"), callbacks, hookInfo); + code.invokeVirtual(callbacksTypeId.getMethod(objArrayTypeId, "getSnapshot"), snapshot, callbacks); + code.arrayLength(snapshotLen, snapshot); + // snapshotLen == 0 => no hooking + code.compareZ(Comparison.EQ, noHookReturn, snapshotLen); + + // start hooking + + // prepare hooking locals + int paramsSize = mParameterTypeIds.length; + int offset = 0; + // thisObject + if (mIsStatic) { + // thisObject = null + code.loadConstant(thisObject, null); + } else { + // thisObject = args[0] + offset = 1; + code.move(thisObject, allArgsLocals[0]); + } + // actual args (exclude thisObject if this is not a static method) + code.loadConstant(actualParamSize, paramsSize - offset); + code.newArray(args, actualParamSize); + for (int i = offset; i < paramsSize; i++) { + Local parameter = allArgsLocals[i]; + // save parameter to resultObj as Object + autoBoxIfNecessary(code, resultObj, parameter); + code.loadConstant(argIndex, i - offset); + // save Object to args + code.aput(args, argIndex, resultObj); + } + // create param + code.newInstance(param, paramTypeId.getConstructor()); + // set method, thisObject, args + code.sget(mMethodFieldId, method); + code.iput(paramTypeId.getField(memberTypeId, "method"), param, method); + code.iput(paramTypeId.getField(TypeId.OBJECT, "thisObject"), param, thisObject); + code.iput(paramTypeId.getField(objArrayTypeId, "args"), param, args); + + // call beforeCallbacks + code.loadConstant(beforeIdx, 0); + + code.mark(beginCallBefore); + // start of try + code.addCatchClause(throwableTypeId, tryBeforeCatch); + + code.aget(callbackObj, snapshot, beforeIdx); + code.cast(callback, callbackObj); + code.invokeVirtual(callBeforeCallbackMethodId, null, callback, param); + code.jump(noExceptionBefore); + + // end of try + code.removeCatchClause(throwableTypeId); + + // start of catch + code.mark(tryBeforeCatch); + code.moveException(throwable); + code.invokeStatic(logThrowableMethodId, null, throwable); + code.invokeVirtual(setResultMethodId, null, param, nullObj); + code.loadConstant(returnEarly, false); + code.iput(returnEarlyFieldId, param, returnEarly); + code.jump(incrementAndCheckBefore); + + // no exception when calling beforeCallbacks + code.mark(noExceptionBefore); + code.iget(returnEarlyFieldId, returnEarly, param); + // if returnEarly == false, continue + code.compareZ(Comparison.EQ, incrementAndCheckBefore, returnEarly); + // returnEarly == true, break + code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one); + code.jump(checkAndCallBackup); + + // increment and check to continue + code.mark(incrementAndCheckBefore); + code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one); + code.compare(Comparison.LT, beginCallBefore, beforeIdx, snapshotLen); + + // check and call backup + code.mark(checkAndCallBackup); + code.iget(returnEarlyFieldId, returnEarly, param); + // if returnEarly == true, go to call afterCallbacks directly + code.compareZ(Comparison.NE, noExceptionOrig, returnEarly); + // try to call backup + // try start + code.addCatchClause(throwableTypeId, tryOrigCatch); + // we have to load args[] to paramLocals + // because args[] may be changed in beforeHookedMethod + // should consider first param is thisObj if hooked method is not static + offset = mIsStatic ? 0 : 1; + for (int i = offset; i < allArgsLocals.length; i++) { + code.loadConstant(argIndex, i - offset); + code.aget(resultObj, args, argIndex); + autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true); + } + // get pre-created Local with a matching typeId + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mBackupMethodId, null, allArgsLocals); + // TODO maybe keep preset result to do some magic? + code.invokeVirtual(setResultMethodId, null, param, nullObj); + } else { + Local returnedResult = resultLocals.get(mReturnTypeId); + code.invokeStatic(mBackupMethodId, returnedResult, allArgsLocals); + // save returnedResult to resultObj as a Object + autoBoxIfNecessary(code, resultObj, returnedResult); + // save resultObj to param + code.invokeVirtual(setResultMethodId, null, param, resultObj); + } + // go to call afterCallbacks + code.jump(noExceptionOrig); + // try end + code.removeCatchClause(throwableTypeId); + // catch + code.mark(tryOrigCatch); + code.moveException(throwable); + // exception occurred when calling backup, save throwable to param + code.invokeVirtual(setThrowableMethodId, null, param, throwable); + + code.mark(noExceptionOrig); + code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one); + + // call afterCallbacks + code.mark(beginCallAfter); + // save results of backup calling + code.invokeVirtual(getResultMethodId, lastResult, param); + code.invokeVirtual(getThrowableMethodId, lastThrowable, param); + // try start + code.addCatchClause(throwableTypeId, tryAfterCatch); + code.aget(callbackObj, snapshot, beforeIdx); + code.cast(callback, callbackObj); + code.invokeVirtual(callAfterCallbackMethodId, null, callback, param); + // all good, just continue + code.jump(decrementAndCheckAfter); + // try end + code.removeCatchClause(throwableTypeId); + // catch + code.mark(tryAfterCatch); + code.moveException(throwable); + code.invokeStatic(logThrowableMethodId, null, throwable); + // if lastThrowable == null, go to recover lastResult + code.compareZ(Comparison.EQ, noBackupThrowable, lastThrowable); + // lastThrowable != null, recover lastThrowable + code.invokeVirtual(setThrowableMethodId, null, param, lastThrowable); + // continue + code.jump(decrementAndCheckAfter); + code.mark(noBackupThrowable); + // recover lastResult and continue + code.invokeVirtual(setResultMethodId, null, param, lastResult); + // decrement and check continue + code.mark(decrementAndCheckAfter); + code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one); + code.compareZ(Comparison.GE, beginCallAfter, beforeIdx); + + // callbacks end + // return + code.invokeVirtual(hasThrowableMethodId, hasThrowable, param); + // if hasThrowable, throw the throwable and return + code.compareZ(Comparison.NE, throwThrowable, hasThrowable); + // return getResult + if (mReturnTypeId.equals(TypeId.VOID)) { + code.returnVoid(); + } else { + // getResult always return an Object, so save to resultObj + code.invokeVirtual(getResultMethodId, resultObj, param); + // have to unbox it if returnType is primitive + // casting Object + TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId); + Local matchObjLocal = resultLocals.get(objTypeId); + code.cast(matchObjLocal, resultObj); + // have to use matching typed Object(Integer, Double ...) to do unboxing + Local toReturn = resultLocals.get(mReturnTypeId); + autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true); + // return + code.returnValue(toReturn); + } + // throw throwable + code.mark(throwThrowable); + code.invokeVirtual(getThrowableMethodId, throwable, param); + code.throwValue(throwable); + + // call backup and return + code.mark(noHookReturn); + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mBackupMethodId, null, allArgsLocals); + code.returnVoid(); + } else { + Local result = resultLocals.get(mReturnTypeId); + code.invokeStatic(mBackupMethodId, result, allArgsLocals); + code.returnValue(result); + } + } + + private Local[] createParameterLocals(Code code) { + Local[] paramLocals = new Local[mParameterTypeIds.length]; + for (int i = 0; i < mParameterTypeIds.length; i++) { + paramLocals[i] = code.getParameter(i, mParameterTypeIds[i]); + } + return paramLocals; + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/MethodInfo.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/MethodInfo.java new file mode 100644 index 00000000..b9908e93 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/MethodInfo.java @@ -0,0 +1,94 @@ +package com.elderdrivers.riru.edxp.sandhook.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 = ""; + 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; + } + } + +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/Router.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/Router.java new file mode 100644 index 00000000..281c1bd8 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/Router.java @@ -0,0 +1,126 @@ +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.util.Utils; +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.entry.hooker.SystemMainHooker; +import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge; + +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 prepare(boolean isSystem) { + // this flag is needed when loadModules + XposedInit.startsSystemServer = isSystem; +// InstallerChooser.setup(); + } + + 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 onEnterChildProcess() { + forkCompleted = true; + DynamicBridge.onForkPost(); + SandHookXposedBridge.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 SandHookEdxpConfig(); + EdXpConfigGlobal.sHookProvider = new SandHookProvider(); + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/AppBootstrapHookInfo.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/AppBootstrapHookInfo.java new file mode 100644 index 00000000..7e630062 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/AppBootstrapHookInfo.java @@ -0,0 +1,14 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap; + +import com.elderdrivers.riru.common.KeepMembers; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.HandleBindAppHooker; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.LoadedApkConstructorHooker; +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() + }; +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysBootstrapHookInfo.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysBootstrapHookInfo.java new file mode 100644 index 00000000..8fed0f42 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysBootstrapHookInfo.java @@ -0,0 +1,16 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap; + +import com.elderdrivers.riru.common.KeepMembers; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.HandleBindAppHooker; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.LoadedApkConstructorHooker; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.OnePlusWorkAroundHooker; +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() + }; +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysInnerHookInfo.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysInnerHookInfo.java new file mode 100644 index 00000000..797ad9aa --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/SysInnerHookInfo.java @@ -0,0 +1,10 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap; + +import com.elderdrivers.riru.common.KeepMembers; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.StartBootstrapServicesHooker; + +public class SysInnerHookInfo implements KeepMembers { + public static String[] hookItemNames = { + StartBootstrapServicesHooker.class.getName() + }; +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/WorkAroundHookInfo.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/WorkAroundHookInfo.java new file mode 100644 index 00000000..8713185c --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/bootstrap/WorkAroundHookInfo.java @@ -0,0 +1,10 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.bootstrap; + +import com.elderdrivers.riru.common.KeepMembers; +import com.elderdrivers.riru.edxp.sandhook.entry.hooker.OnePlusWorkAroundHooker; + +public class WorkAroundHookInfo implements KeepMembers { + public static String[] hookItemNames = { + OnePlusWorkAroundHooker.class.getName() + }; +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/HandleBindAppHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/HandleBindAppHooker.java new file mode 100644 index 00000000..db2fe706 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/HandleBindAppHooker.java @@ -0,0 +1,89 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.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.util.Utils; +import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.sandhook.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.util.ClassLoaderUtils.replaceParentClassLoader; +import static com.elderdrivers.riru.edxp.sandhook.entry.hooker.XposedBlackListHooker.BLACK_LIST_PACKAGE_NAME; + +// 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); + } + } catch (Throwable t) { + Router.logE("error when hooking bindApp", t); + } finally { + backup(thiz, bindData); + } + } + + public static void backup(Object thiz, Object bindData) { + } +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/LoadedApkConstructorHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/LoadedApkConstructorHooker.java new file mode 100644 index 00000000..d31aa728 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/LoadedApkConstructorHooker.java @@ -0,0 +1,98 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.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.sandhook.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 = ""; + 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# 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# ends: " + mAppDir); + + if (XposedBlackListHooker.shouldDisableHooks(packageName)) { + return; + } + + if (packageName.equals("android")) { + Router.logD("LoadedApk# is android, skip: " + mAppDir); + return; + } + + // mIncludeCode checking should go ahead of loadedPackagesInProcess added checking + if (!XposedHelpers.getBooleanField(loadedApk, "mIncludeCode")) { + Router.logD("LoadedApk# mIncludeCode == false: " + mAppDir); + return; + } + + if (!XposedInit.loadedPackagesInProcess.add(packageName)) { + Router.logD("LoadedApk# has been loaded before, skip: " + mAppDir); + return; + } + + // OnePlus magic... + if (Log.getStackTraceString(new Throwable()). + contains("android.app.ActivityThread$ApplicationThread.schedulePreload")) { + Router.logD("LoadedApk# 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.", t); + } + } + + public static void backup(Object thiz, ActivityThread activityThread, + ApplicationInfo aInfo, CompatibilityInfo compatInfo, + ClassLoader baseLoader, boolean securityViolation, + boolean includeCode, boolean registerPackage) { + + } +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/OnePlusWorkAroundHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/OnePlusWorkAroundHooker.java new file mode 100644 index 00000000..e83b1f45 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/OnePlusWorkAroundHooker.java @@ -0,0 +1,41 @@ +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 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; + } +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/StartBootstrapServicesHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/StartBootstrapServicesHooker.java new file mode 100644 index 00000000..0b2f8428 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/StartBootstrapServicesHooker.java @@ -0,0 +1,66 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.hooker; + +import android.os.Build; + +import com.elderdrivers.riru.common.KeepMembers; +import com.elderdrivers.riru.edxp.sandhook.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) { + + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/SystemMainHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/SystemMainHooker.java new file mode 100644 index 00000000..f33db3e7 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/SystemMainHooker.java @@ -0,0 +1,43 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.hooker; + +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 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; + } +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedBlackListHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedBlackListHooker.java new file mode 100644 index 00000000..c86dddfc --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedBlackListHooker.java @@ -0,0 +1,87 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.hooker; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.os.Build; + +import com.elderdrivers.riru.edxp.util.Utils; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XSharedPreferences; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; + +import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_PACKAGE_NAME; +import static com.elderdrivers.riru.edxp.util.FileUtils.IS_USING_PROTECTED_STORAGE; + +public class XposedBlackListHooker { + + public static final String BLACK_LIST_PACKAGE_NAME = "com.flarejune.xposedblacklist"; + private static final String BLACK_LIST_PREF_NAME = "list"; + private static final String PREF_KEY_BLACK_LIST = "blackList"; + public static final String PREF_FILE_PATH = (IS_USING_PROTECTED_STORAGE ? "/data/user_de/0/" : "/data/data") + + BLACK_LIST_PACKAGE_NAME + "/shared_prefs/" + BLACK_LIST_PREF_NAME + ".xml"; + private static final XSharedPreferences PREFERENCES = new XSharedPreferences(new File(PREF_FILE_PATH)); + // always white list. empty string is to make sure blackList does not contain empty packageName + private static final List WHITE_LIST = Arrays.asList(INSTALLER_PACKAGE_NAME, BLACK_LIST_PACKAGE_NAME, ""); + + static { + try { + PREFERENCES.makeWorldReadable(); + } catch (Throwable throwable) { + Utils.logE("error making pref worldReadable", throwable); + } + } + + public static boolean shouldDisableHooks(String packageName) { + return XposedBridge.disableHooks || getBlackList().contains(packageName); + } + + public static Set getBlackList() { + try { + PREFERENCES.reload(); + Set result = PREFERENCES.getStringSet(PREF_KEY_BLACK_LIST, new HashSet()); + if (result != null) result.removeAll(WHITE_LIST); + return result; + } catch (Throwable throwable) { + Utils.logE("error when reading black list", throwable); + return new HashSet<>(); + } + } + + public static void hook(ClassLoader classLoader) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return; + } + try { + XposedHelpers.findAndHookMethod(ContextWrapper.class, "getSharedPreferences", String.class, int.class, new XC_MethodHook() { + @TargetApi(Build.VERSION_CODES.N) + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + try { + String prefName = (String) param.args[0]; + if (!prefName.equals(BLACK_LIST_PREF_NAME)) { + return; + } + Activity activity = (Activity) param.thisObject; + Context context = activity.createDeviceProtectedStorageContext(); + context.moveSharedPreferencesFrom(activity, prefName); + param.setResult(context.getSharedPreferences(prefName, (int) param.args[1])); + } catch (Throwable throwable) { + Utils.logE("error hooking Xposed BlackList", throwable); + } + } + }); + } catch (Throwable throwable) { + Utils.logE("error hooking Xposed BlackList", throwable); + } + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedInstallerHooker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedInstallerHooker.java new file mode 100644 index 00000000..e3b83a77 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/entry/hooker/XposedInstallerHooker.java @@ -0,0 +1,64 @@ +package com.elderdrivers.riru.edxp.sandhook.entry.hooker; + +import com.elderdrivers.riru.edxp.util.Utils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +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; + +import static com.elderdrivers.riru.edxp.config.InstallerChooser.LEGACY_INSTALLER_PACKAGE_NAME; + +public class XposedInstallerHooker { + + public static void hookXposedInstaller(ClassLoader classLoader) { + try { + final String xposedAppClass = LEGACY_INSTALLER_PACKAGE_NAME + ".XposedApp"; + final Class InstallZipUtil = XposedHelpers.findClass(LEGACY_INSTALLER_PACKAGE_NAME + + ".util.InstallZipUtil", classLoader); + XposedHelpers.findAndHookMethod(xposedAppClass, classLoader, "getActiveXposedVersion", + XC_MethodReplacement.returnConstant(XposedBridge.getXposedVersion())); + XposedHelpers.findAndHookMethod(xposedAppClass, classLoader, + "reloadXposedProp", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + Utils.logD("before reloadXposedProp..."); + final String propFieldName = "mXposedProp"; + final Object thisObject = param.thisObject; + if (XposedHelpers.getObjectField(thisObject, propFieldName) != null) { + param.setResult(null); + Utils.logD("reloadXposedProp already done, skip..."); + return; + } + File file = new File("/system/framework/edconfig.jar"); + FileInputStream is = null; + try { + is = new FileInputStream(file); + Object props = XposedHelpers.callStaticMethod(InstallZipUtil, + "parseXposedProp", is); + synchronized (thisObject) { + XposedHelpers.setObjectField(thisObject, propFieldName, props); + } + Utils.logD("reloadXposedProp done..."); + param.setResult(null); + } catch (IOException e) { + Utils.logE("Could not read " + file.getPath(), e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ignored) { + } + } + } + } + }); + } catch (Throwable t) { + Utils.logE("Could not hook Xposed Installer", t); + } + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java new file mode 100644 index 00000000..5ddfdc2a --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java @@ -0,0 +1,132 @@ +package com.elderdrivers.riru.edxp.sandhook.proxy; + +import android.text.TextUtils; + +import com.elderdrivers.riru.edxp.config.ConfigManager; +import com.elderdrivers.riru.edxp.util.ProcessUtils; +import com.elderdrivers.riru.edxp.util.Utils; +import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.sandhook.entry.Router; +import com.elderdrivers.riru.edxp.sandhook.util.PrebuiltMethodsDeopter; + +import de.robv.android.xposed.XposedBridge; + +import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; +import static com.elderdrivers.riru.edxp.Main.isAppNeedHook; + +/** + * 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 = Main.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 = Main.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) { + ConfigManager.setDynamicModulesMode(false); + // 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); + Main.closeFilesBeforeForkNative(); + } + + private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) { + Main.appDataDir = appDataDir; + Main.niceName = niceName; + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + Router.onEnterChildProcess(); + if (!isDynamicModulesMode) { + Main.reopenFilesAfterForkNative(); + } + if (!checkNeedHook(appDataDir, niceName)) { + // if is blacklisted, just stop here + return; + } + Router.prepare(isSystemServer); + PrebuiltMethodsDeopter.deoptBootMethods(); + Router.installBootstrapHooks(isSystemServer); + if (isDynamicModulesMode) { + Router.loadModulesSafely(false); + } + } + + 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 = isAppNeedHook(appDataDir); + } + if (!needHook) { + // clean up the scene + onBlackListed(); + } + return needHook; + } + + private static void onBlackListed() { + XposedBridge.clearLoadedPackages(); + XposedBridge.clearInitPackageResources(); + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java new file mode 100644 index 00000000..b09ff554 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java @@ -0,0 +1,70 @@ +package com.elderdrivers.riru.edxp.sandhook.proxy; + +import com.elderdrivers.riru.edxp.config.ConfigManager; +import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.sandhook.entry.Router; +import com.elderdrivers.riru.edxp.sandhook.util.PrebuiltMethodsDeopter; + +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 + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + // 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); + Main.closeFilesBeforeForkNative(); + } + + 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); + Main.reopenFilesAfterForkNative(); + Router.onEnterChildProcess(); + // load modules for each app process on its forked if dynamic modules mode is on + Router.loadModulesSafely(false); + } + + public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, + long permittedCapabilities, long effectiveCapabilities) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + // 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); + Main.closeFilesBeforeForkNative(); + } + + public static void forkSystemServerPost(int pid) { + // in system_server process + Main.appDataDir = getDataPathPrefix() + "android"; + Main.niceName = "system_server"; + Router.prepare(true); + Main.reopenFilesAfterForkNative(); + Router.onEnterChildProcess(); + // reload module list if dynamic mode is on + Router.loadModulesSafely(false); + } + +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/InlinedMethodCallers.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/InlinedMethodCallers.java new file mode 100644 index 00000000..4ef716e1 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/InlinedMethodCallers.java @@ -0,0 +1,49 @@ +package com.elderdrivers.riru.edxp.sandhook.util; + +import java.util.HashMap; + +/** + * Providing a whitelist of methods which are the callers of the target methods we want to hook. + * Because the target methods are inlined into the callers, we deoptimize the callers to + * run in intercept mode to make target methods hookable. + *

+ * Only for methods which are included in pre-compiled framework codes. + * TODO recompile system apps and priv-apps since their original dex files are available + */ +public class InlinedMethodCallers { + + public static final String KEY_BOOT_IMAGE = "boot_image"; + public static final String KEY_SYSTEM_SERVER = "system_server"; + + /** + * Key should be {@link #KEY_BOOT_IMAGE}, {@link #KEY_SYSTEM_SERVER}, or a package name + * of system apps or priv-apps i.e. com.android.systemui + */ + private static final HashMap CALLERS = new HashMap<>(); + + /** + * format for each row: {className, methodName, methodSig} + */ + private static final String[][] BOOT_IMAGE = { + // callers of Application#attach(Context) + {"android.app.Instrumentation", "newApplication", "(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Context;)Landroid/app/Application;"} + }; + + private static final String[][] SYSTEM_SERVER = {}; + + private static final String[][] SYSTEM_UI = {}; + + static { + CALLERS.put(KEY_BOOT_IMAGE, BOOT_IMAGE); + CALLERS.put(KEY_SYSTEM_SERVER, SYSTEM_SERVER); + CALLERS.put("com.android.systemui", SYSTEM_UI); + } + + public static HashMap getAll() { + return CALLERS; + } + + public static String[][] get(String where) { + return CALLERS.get(where); + } +} diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/PrebuiltMethodsDeopter.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/PrebuiltMethodsDeopter.java new file mode 100644 index 00000000..01f871b2 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/util/PrebuiltMethodsDeopter.java @@ -0,0 +1,41 @@ +package com.elderdrivers.riru.edxp.sandhook.util; + +import com.elderdrivers.riru.edxp.util.Utils; +import com.elderdrivers.riru.edxp.Main; + +import java.util.Arrays; + +import de.robv.android.xposed.XposedHelpers; + +import static com.elderdrivers.riru.edxp.sandhook.util.InlinedMethodCallers.KEY_BOOT_IMAGE; +import static com.elderdrivers.riru.edxp.sandhook.util.InlinedMethodCallers.KEY_SYSTEM_SERVER; + +public class PrebuiltMethodsDeopter { + + public static void deoptMethods(String where, ClassLoader cl) { + String[][] callers = InlinedMethodCallers.get(where); + if (callers == null) { + return; + } + for (String[] caller : callers) { + try { + Object method = Main.findMethodNative( + XposedHelpers.findClass(caller[0], cl), caller[1], caller[2]); + if (method != null) { + Main.deoptMethodNative(method); + } + } catch (Throwable throwable) { + Utils.logE("error when deopting method: " + Arrays.toString(caller), throwable); + } + } + } + + public static void deoptBootMethods() { + // todo check if has been done before + deoptMethods(KEY_BOOT_IMAGE, null); + } + + public static void deoptSystemServerMethods(ClassLoader sysCL) { + deoptMethods(KEY_SYSTEM_SERVER, sysCL); + } +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/XposedCompat.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/XposedCompat.java new file mode 100644 index 00000000..7b494b50 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/XposedCompat.java @@ -0,0 +1,48 @@ +package com.swift.sandhook.xposedcompat; + +import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader; +import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge; +import com.swift.sandhook.xposedcompat.utils.FileUtils; + +import java.io.File; +import java.lang.reflect.Member; + +import de.robv.android.xposed.XposedBridge; + +public class XposedCompat { + + //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 retryWhenCallOriginError = false; + + private static ClassLoader sandHookXposedClassLoader; + + public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) { + SandHookXposedBridge.hookMethod(hookMethod, additionalHookInfo); + } + + public static ClassLoader getSandHookXposedClassLoader(ClassLoader appOriginClassLoader, ClassLoader sandBoxHostClassLoader) { + if (sandHookXposedClassLoader != null) { + return sandHookXposedClassLoader; + } else { + sandHookXposedClassLoader = new ComposeClassLoader(sandBoxHostClassLoader, appOriginClassLoader); + return sandHookXposedClassLoader; + } + } + +// public static boolean clearCache() { +// try { +// FileUtils.delete(cacheDir); +// cacheDir.mkdirs(); +// return true; +// } catch (Throwable throwable) { +// return false; +// } +// } +// +// public static void clearOatCache() { +// SandHookXposedBridge.clearOatFile(); +// } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/classloaders/ComposeClassLoader.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/classloaders/ComposeClassLoader.java new file mode 100644 index 00000000..6d968f6f --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/classloaders/ComposeClassLoader.java @@ -0,0 +1,34 @@ +package com.swift.sandhook.xposedcompat.classloaders; + +/** + * Created by weishu on 17/11/30. + */ + +public class ComposeClassLoader extends ClassLoader { + + private final ClassLoader mAppClassLoader; + public ComposeClassLoader(ClassLoader parent, ClassLoader appClassLoader) { + super(parent); + mAppClassLoader = appClassLoader; + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class clazz = null; + + try { + clazz = mAppClassLoader.loadClass(name); + } catch (ClassNotFoundException e) { + // IGNORE. + } + if (clazz == null) { + clazz = super.loadClass(name, resolve); + } + + if (clazz == null) { + throw new ClassNotFoundException(); + } + + return clazz; + } +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/CallOriginCallBack.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/CallOriginCallBack.java new file mode 100644 index 00000000..65657ef3 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/CallOriginCallBack.java @@ -0,0 +1,5 @@ +package com.swift.sandhook.xposedcompat.hookstub; + +public interface CallOriginCallBack { + long call(long... args) throws Throwable; +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookMethodEntity.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookMethodEntity.java new file mode 100644 index 00000000..1436908c --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookMethodEntity.java @@ -0,0 +1,93 @@ +package com.swift.sandhook.xposedcompat.hookstub; + +import com.swift.sandhook.SandHook; +import com.swift.sandhook.utils.ParamWrapper; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class HookMethodEntity { + + public Member origin; + public Method hook; + public Method backup; + public Class[] parType; + public Class retType; + + public HookMethodEntity(Member origin, Method hook, Method backup) { + this.origin = origin; + this.hook = hook; + this.backup = backup; + } + + public Object[] getArgs(long... addresses) { + if (addresses == null || addresses.length == 0) + return new Object[0]; + if (parType == null || parType.length == 0) + return new Object[0]; + int argStart = 0; + if (!isStatic()) { + argStart = 1; + } + Object[] args = new Object[parType.length]; + for (int i = argStart;i < parType.length + argStart;i++) { + args[i - argStart] = getArg(i - argStart, addresses[i]); + } + return args; + } + + public long[] getArgsAddress(long[] oldAddress, Object... args) { + if (oldAddress == null || oldAddress.length == 0) + return new long[0]; + long[] addresses; + int argStart = 0; + if (!isStatic()) { + argStart = 1; + addresses = new long[oldAddress.length + 1]; + addresses[0] = oldAddress[0]; + } else { + addresses = new long[oldAddress.length]; + } + for (int i = 0;i < parType.length;i++) { + addresses[i + argStart] = ParamWrapper.objectToAddress(parType[i], args[i]); + } + return addresses; + } + + public Object getThis(long address) { + if (isStatic()) + return null; + return SandHook.getObject(address); + } + + public Object getArg(int index, long address) { + return ParamWrapper.addressToObject(parType[index], address); + } + + public Object getResult(long address) { + if (isVoid()) + return null; + return ParamWrapper.addressToObject(retType, address); + } + + public long getResultAddress(Object result) { + if (isVoid()) + return 0; + return ParamWrapper.objectToAddress(retType, result); + } + + public boolean isVoid() { + return retType == null || Void.TYPE.equals(retType); + } + + public boolean isConstructor() { + return origin instanceof Constructor; + } + + public boolean isStatic() { + return Modifier.isStatic(origin.getModifiers()); + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookStubManager.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookStubManager.java new file mode 100644 index 00000000..1b27ed4a --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/HookStubManager.java @@ -0,0 +1,409 @@ +package com.swift.sandhook.xposedcompat.hookstub; + +import android.util.Log; + +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.utils.DexLog; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; + +import static de.robv.android.xposed.XposedBridge.sHookedMethodCallbacks; + +public class HookStubManager { + + + public static int MAX_STUB_ARGS = 0; + + public static int[] stubSizes; + + public static boolean hasStubBackup = false; + + public static AtomicInteger[] curUseStubIndexes; + + public static int ALL_STUB = 0; + + public static Member[] originMethods; + public static HookMethodEntity[] hookMethodEntities; + + private static final Map> hookCallbacks + = sHookedMethodCallbacks; + + static { + Class stubClass = SandHook.is64Bit() ? MethodHookerStubs64.class : MethodHookerStubs32.class; + stubSizes = (int[]) XposedHelpers.getStaticObjectField(stubClass, "stubSizes"); + Boolean hasBackup = (Boolean) XposedHelpers.getStaticObjectField(stubClass, "hasStubBackup"); + hasStubBackup = hasBackup == null ? false : hasBackup; + if (stubSizes != null && stubSizes.length > 0) { + MAX_STUB_ARGS = stubSizes.length - 1; + curUseStubIndexes = new AtomicInteger[MAX_STUB_ARGS + 1]; + for (int i = 0; i < MAX_STUB_ARGS + 1; i++) { + curUseStubIndexes[i] = new AtomicInteger(0); + ALL_STUB += stubSizes[i]; + } + originMethods = new Member[ALL_STUB]; + hookMethodEntities = new HookMethodEntity[ALL_STUB]; + } + } + + + public static HookMethodEntity getHookMethodEntity(Member origin) { + + if (!support()) { + return null; + } + + Class[] parType; + Class retType; + boolean isStatic = Modifier.isStatic(origin.getModifiers()); + + if (origin instanceof Method) { + Method method = (Method) origin; + retType = method.getReturnType(); + parType = method.getParameterTypes(); + } else if (origin instanceof Constructor) { + Constructor constructor = (Constructor) origin; + retType = Void.TYPE; + parType = constructor.getParameterTypes(); + } else { + return null; + } + + if (!ParamWrapper.support(retType)) + return null; + + int needStubArgCount = isStatic ? 0 : 1; + + if (parType != null) { + needStubArgCount += parType.length; + if (needStubArgCount > MAX_STUB_ARGS) + return null; + for (Class par:parType) { + if (!ParamWrapper.support(par)) + return null; + } + } else { + parType = new Class[0]; + } + + synchronized (HookStubManager.class) { + StubMethodsInfo stubMethodInfo = getStubMethodPair(SandHook.is64Bit(), needStubArgCount); + if (stubMethodInfo == null) + return null; + HookMethodEntity entity = new HookMethodEntity(origin, stubMethodInfo.hook, stubMethodInfo.backup); + entity.retType = retType; + entity.parType = parType; + int id = getMethodId(stubMethodInfo.args, stubMethodInfo.index); + originMethods[id] = origin; + hookMethodEntities[id] = entity; + if (hasStubBackup && !tryCompileAndResolveCallOriginMethod(entity.backup, stubMethodInfo.args, stubMethodInfo.index)) { + DexLog.w("internal stub <" + entity.hook.getName() + "> call origin compile failure, skip use internal stub"); + return null; + } else { + return entity; + } + } + } + + public static int getMethodId(int args, int index) { + int id = index; + for (int i = 0;i < args;i++) { + id += stubSizes[i]; + } + return id; + } + + public static String getHookMethodName(int index) { + return "stub_hook_" + index; + } + + public static String getBackupMethodName(int index) { + return "stub_backup_" + index; + } + + public static String getCallOriginClassName(int args, int index) { + return "call_origin_" + args + "_" + index; + } + + + static class StubMethodsInfo { + int args = 0; + int index = 0; + Method hook; + Method backup; + + public StubMethodsInfo(int args, int index, Method hook, Method backup) { + this.args = args; + this.index = index; + this.hook = hook; + this.backup = backup; + } + } + + private static synchronized StubMethodsInfo getStubMethodPair(boolean is64Bit, int stubArgs) { + + stubArgs = getMatchStubArgsCount(stubArgs); + + if (stubArgs < 0) + return null; + + int curUseStubIndex = curUseStubIndexes[stubArgs].getAndIncrement(); + Class[] pars = getFindMethodParTypes(is64Bit, stubArgs); + try { + if (is64Bit) { + Method hook = MethodHookerStubs64.class.getDeclaredMethod(getHookMethodName(curUseStubIndex), pars); + Method backup = hasStubBackup ? MethodHookerStubs64.class.getDeclaredMethod(getBackupMethodName(curUseStubIndex), pars) : BackupMethodStubs.getStubMethod(); + if (hook == null || backup == null) + return null; + return new StubMethodsInfo(stubArgs, curUseStubIndex, hook, backup); + } else { + Method hook = MethodHookerStubs32.class.getDeclaredMethod(getHookMethodName(curUseStubIndex), pars); + Method backup = hasStubBackup ? MethodHookerStubs32.class.getDeclaredMethod(getBackupMethodName(curUseStubIndex), pars) : BackupMethodStubs.getStubMethod(); + if (hook == null || backup == null) + return null; + return new StubMethodsInfo(stubArgs, curUseStubIndex, hook, backup); + } + } catch (Throwable throwable) { + return null; + } + } + + public static Method getCallOriginMethod(int args, int index) { + Class stubClass = SandHook.is64Bit() ? MethodHookerStubs64.class : MethodHookerStubs32.class; + String className = stubClass.getName(); + className += "$"; + className += getCallOriginClassName(args, index); + try { + Class callOriginClass = Class.forName(className, true, stubClass.getClassLoader()); + return callOriginClass.getDeclaredMethod("call", long[].class); + } catch (Throwable e) { + Log.e("HookStubManager", "load call origin class error!", e); + return null; + } + } + + public static boolean tryCompileAndResolveCallOriginMethod(Method backupMethod, int args, int index) { + Method method = getCallOriginMethod(args, index); + if (method != null) { + SandHookMethodResolver.resolveMethod(method, backupMethod); + return SandHook.compileMethod(method); + } else { + return false; + } + } + + public static int getMatchStubArgsCount(int stubArgs) { + for (int i = stubArgs;i <= MAX_STUB_ARGS;i++) { + if (curUseStubIndexes[i].get() < stubSizes[i]) + return i; + } + return -1; + } + + public static Class[] getFindMethodParTypes(boolean is64Bit, int stubArgs) { + if (stubArgs == 0) + return null; + Class[] args = new Class[stubArgs]; + if (is64Bit) { + for (int i = 0;i < stubArgs;i++) { + args[i] = long.class; + } + } else { + for (int i = 0;i < stubArgs;i++) { + args[i] = int.class; + } + } + return args; + } + + public static long hookBridge(int id, CallOriginCallBack callOrigin, long... stubArgs) throws Throwable { + + Member originMethod = originMethods[id]; + HookMethodEntity entity = hookMethodEntities[id]; + + Object thiz = null; + Object[] args = null; + + if (hasArgs(stubArgs)) { + thiz = entity.getThis(stubArgs[0]); + args = entity.getArgs(stubArgs); + } + + if (XposedBridge.disableHooks) { + if (hasStubBackup) { + return callOrigin.call(stubArgs); + } else { + return callOrigin(entity, originMethod, thiz, args); + } + } + + DexLog.printMethodHookIn(originMethod); + + Object[] snapshot = hookCallbacks.get(originMethod).getSnapshot(); + if (snapshot == null || snapshot.length == 0) { + if (hasStubBackup) { + return callOrigin.call(stubArgs); + } else { + return callOrigin(entity, originMethod, thiz, args); + } + } + + XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam(); + + param.method = originMethod; + param.thisObject = thiz; + param.args = args; + + int beforeIdx = 0; + do { + try { + ((XC_MethodHook) snapshot[beforeIdx]).callBeforeHookedMethod(param); + } catch (Throwable t) { + // reset result (ignoring what the unexpectedly exiting callback did) + param.setResult(null); + param.returnEarly = false; + continue; + } + + if (param.returnEarly) { + // skip remaining "before" callbacks and corresponding "after" callbacks + beforeIdx++; + break; + } + } while (++beforeIdx < snapshot.length); + + // call original method if not requested otherwise + if (!param.returnEarly) { + try { + if (hasStubBackup) { + //prepare new args + long[] newArgs = entity.getArgsAddress(stubArgs, param.args); + param.setResult(entity.getResult(callOrigin.call(newArgs))); + } else { + param.setResult(SandHook.callOriginMethod(originMethod, thiz, param.args)); + } + } catch (Throwable e) { + XposedBridge.log(e); + param.setThrowable(e); + } + } + + // call "after method" callbacks + int afterIdx = beforeIdx - 1; + do { + Object lastResult = param.getResult(); + Throwable lastThrowable = param.getThrowable(); + + try { + ((XC_MethodHook) snapshot[afterIdx]).callAfterHookedMethod(param); + } catch (Throwable t) { + XposedBridge.log(t); + if (lastThrowable == null) + param.setResult(lastResult); + else + param.setThrowable(lastThrowable); + } + } while (--afterIdx >= 0); + if (!param.hasThrowable()) { + return entity.getResultAddress(param.getResult()); + } else { + throw param.getThrowable(); + } + } + + public static Object hookBridge(Member origin, Object thiz, Object... args) throws Throwable { + + + if (XposedBridge.disableHooks) { + return SandHook.callOriginMethod(origin, thiz, args); + } + + DexLog.printMethodHookIn(origin); + + Object[] snapshot = hookCallbacks.get(origin).getSnapshot(); + if (snapshot == null || snapshot.length == 0) { + return SandHook.callOriginMethod(origin, thiz, args); + } + + XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam(); + + param.method = origin; + param.thisObject = thiz; + param.args = args; + + int beforeIdx = 0; + do { + try { + ((XC_MethodHook) snapshot[beforeIdx]).callBeforeHookedMethod(param); + } catch (Throwable t) { + // reset result (ignoring what the unexpectedly exiting callback did) + param.setResult(null); + param.returnEarly = false; + continue; + } + + if (param.returnEarly) { + // skip remaining "before" callbacks and corresponding "after" callbacks + beforeIdx++; + break; + } + } while (++beforeIdx < snapshot.length); + + // call original method if not requested otherwise + if (!param.returnEarly) { + try { + param.setResult(SandHook.callOriginMethod(origin, thiz, param.args)); + } catch (Throwable e) { + XposedBridge.log(e); + param.setThrowable(e); + } + } + + // call "after method" callbacks + int afterIdx = beforeIdx - 1; + do { + Object lastResult = param.getResult(); + Throwable lastThrowable = param.getThrowable(); + + try { + ((XC_MethodHook) snapshot[afterIdx]).callAfterHookedMethod(param); + } catch (Throwable t) { + XposedBridge.log(t); + if (lastThrowable == null) + param.setResult(lastResult); + else + param.setThrowable(lastThrowable); + } + } while (--afterIdx >= 0); + if (!param.hasThrowable()) { + return param.getResult(); + } else { + throw param.getThrowable(); + } + } + + public static long callOrigin(HookMethodEntity entity, Member origin, Object thiz, Object[] args) throws Throwable { + Object res = SandHook.callOriginMethod(origin, thiz, args); + return entity.getResultAddress(res); + } + + private static boolean hasArgs(long... args) { + return args != null && args.length > 0; + } + + public static boolean support() { + return MAX_STUB_ARGS > 0 && SandHook.canGetObject() && SandHook.canGetObjectAddress(); + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs32.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs32.java new file mode 100644 index 00000000..573eb4aa --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs32.java @@ -0,0 +1,1413 @@ +package com.swift.sandhook.xposedcompat.hookstub; + +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.getMethodId; +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.hookBridge; + +/** +* this file is auto gen by genhookstubs.py +* it is for sandhook internal hooker & backup methods +**/ +public class MethodHookerStubs32 { + + public static boolean hasStubBackup = false; + public static int[] stubSizes = {10, 20, 30, 30, 30, 30, 30, 20, 10, 10, 5, 5, 3}; + + + //stub of arg size 0, index 0 + public static int stub_hook_0() throws Throwable { + return (int) hookBridge(getMethodId(0, 0), null ); + } + + + //stub of arg size 0, index 1 + public static int stub_hook_1() throws Throwable { + return (int) hookBridge(getMethodId(0, 1), null ); + } + + + //stub of arg size 0, index 2 + public static int stub_hook_2() throws Throwable { + return (int) hookBridge(getMethodId(0, 2), null ); + } + + + //stub of arg size 0, index 3 + public static int stub_hook_3() throws Throwable { + return (int) hookBridge(getMethodId(0, 3), null ); + } + + + //stub of arg size 0, index 4 + public static int stub_hook_4() throws Throwable { + return (int) hookBridge(getMethodId(0, 4), null ); + } + + + //stub of arg size 0, index 5 + public static int stub_hook_5() throws Throwable { + return (int) hookBridge(getMethodId(0, 5), null ); + } + + + //stub of arg size 0, index 6 + public static int stub_hook_6() throws Throwable { + return (int) hookBridge(getMethodId(0, 6), null ); + } + + + //stub of arg size 0, index 7 + public static int stub_hook_7() throws Throwable { + return (int) hookBridge(getMethodId(0, 7), null ); + } + + + //stub of arg size 0, index 8 + public static int stub_hook_8() throws Throwable { + return (int) hookBridge(getMethodId(0, 8), null ); + } + + + //stub of arg size 0, index 9 + public static int stub_hook_9() throws Throwable { + return (int) hookBridge(getMethodId(0, 9), null ); + } + + + //stub of arg size 1, index 0 + public static int stub_hook_0(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 0), null , a0); + } + + + //stub of arg size 1, index 1 + public static int stub_hook_1(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 1), null , a0); + } + + + //stub of arg size 1, index 2 + public static int stub_hook_2(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 2), null , a0); + } + + + //stub of arg size 1, index 3 + public static int stub_hook_3(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 3), null , a0); + } + + + //stub of arg size 1, index 4 + public static int stub_hook_4(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 4), null , a0); + } + + + //stub of arg size 1, index 5 + public static int stub_hook_5(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 5), null , a0); + } + + + //stub of arg size 1, index 6 + public static int stub_hook_6(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 6), null , a0); + } + + + //stub of arg size 1, index 7 + public static int stub_hook_7(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 7), null , a0); + } + + + //stub of arg size 1, index 8 + public static int stub_hook_8(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 8), null , a0); + } + + + //stub of arg size 1, index 9 + public static int stub_hook_9(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 9), null , a0); + } + + + //stub of arg size 1, index 10 + public static int stub_hook_10(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 10), null , a0); + } + + + //stub of arg size 1, index 11 + public static int stub_hook_11(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 11), null , a0); + } + + + //stub of arg size 1, index 12 + public static int stub_hook_12(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 12), null , a0); + } + + + //stub of arg size 1, index 13 + public static int stub_hook_13(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 13), null , a0); + } + + + //stub of arg size 1, index 14 + public static int stub_hook_14(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 14), null , a0); + } + + + //stub of arg size 1, index 15 + public static int stub_hook_15(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 15), null , a0); + } + + + //stub of arg size 1, index 16 + public static int stub_hook_16(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 16), null , a0); + } + + + //stub of arg size 1, index 17 + public static int stub_hook_17(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 17), null , a0); + } + + + //stub of arg size 1, index 18 + public static int stub_hook_18(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 18), null , a0); + } + + + //stub of arg size 1, index 19 + public static int stub_hook_19(int a0) throws Throwable { + return (int) hookBridge(getMethodId(1, 19), null , a0); + } + + + //stub of arg size 2, index 0 + public static int stub_hook_0(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 0), null , a0, a1); + } + + + //stub of arg size 2, index 1 + public static int stub_hook_1(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 1), null , a0, a1); + } + + + //stub of arg size 2, index 2 + public static int stub_hook_2(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 2), null , a0, a1); + } + + + //stub of arg size 2, index 3 + public static int stub_hook_3(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 3), null , a0, a1); + } + + + //stub of arg size 2, index 4 + public static int stub_hook_4(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 4), null , a0, a1); + } + + + //stub of arg size 2, index 5 + public static int stub_hook_5(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 5), null , a0, a1); + } + + + //stub of arg size 2, index 6 + public static int stub_hook_6(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 6), null , a0, a1); + } + + + //stub of arg size 2, index 7 + public static int stub_hook_7(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 7), null , a0, a1); + } + + + //stub of arg size 2, index 8 + public static int stub_hook_8(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 8), null , a0, a1); + } + + + //stub of arg size 2, index 9 + public static int stub_hook_9(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 9), null , a0, a1); + } + + + //stub of arg size 2, index 10 + public static int stub_hook_10(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 10), null , a0, a1); + } + + + //stub of arg size 2, index 11 + public static int stub_hook_11(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 11), null , a0, a1); + } + + + //stub of arg size 2, index 12 + public static int stub_hook_12(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 12), null , a0, a1); + } + + + //stub of arg size 2, index 13 + public static int stub_hook_13(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 13), null , a0, a1); + } + + + //stub of arg size 2, index 14 + public static int stub_hook_14(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 14), null , a0, a1); + } + + + //stub of arg size 2, index 15 + public static int stub_hook_15(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 15), null , a0, a1); + } + + + //stub of arg size 2, index 16 + public static int stub_hook_16(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 16), null , a0, a1); + } + + + //stub of arg size 2, index 17 + public static int stub_hook_17(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 17), null , a0, a1); + } + + + //stub of arg size 2, index 18 + public static int stub_hook_18(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 18), null , a0, a1); + } + + + //stub of arg size 2, index 19 + public static int stub_hook_19(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 19), null , a0, a1); + } + + + //stub of arg size 2, index 20 + public static int stub_hook_20(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 20), null , a0, a1); + } + + + //stub of arg size 2, index 21 + public static int stub_hook_21(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 21), null , a0, a1); + } + + + //stub of arg size 2, index 22 + public static int stub_hook_22(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 22), null , a0, a1); + } + + + //stub of arg size 2, index 23 + public static int stub_hook_23(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 23), null , a0, a1); + } + + + //stub of arg size 2, index 24 + public static int stub_hook_24(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 24), null , a0, a1); + } + + + //stub of arg size 2, index 25 + public static int stub_hook_25(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 25), null , a0, a1); + } + + + //stub of arg size 2, index 26 + public static int stub_hook_26(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 26), null , a0, a1); + } + + + //stub of arg size 2, index 27 + public static int stub_hook_27(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 27), null , a0, a1); + } + + + //stub of arg size 2, index 28 + public static int stub_hook_28(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 28), null , a0, a1); + } + + + //stub of arg size 2, index 29 + public static int stub_hook_29(int a0, int a1) throws Throwable { + return (int) hookBridge(getMethodId(2, 29), null , a0, a1); + } + + + //stub of arg size 3, index 0 + public static int stub_hook_0(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 0), null , a0, a1, a2); + } + + + //stub of arg size 3, index 1 + public static int stub_hook_1(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 1), null , a0, a1, a2); + } + + + //stub of arg size 3, index 2 + public static int stub_hook_2(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 2), null , a0, a1, a2); + } + + + //stub of arg size 3, index 3 + public static int stub_hook_3(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 3), null , a0, a1, a2); + } + + + //stub of arg size 3, index 4 + public static int stub_hook_4(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 4), null , a0, a1, a2); + } + + + //stub of arg size 3, index 5 + public static int stub_hook_5(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 5), null , a0, a1, a2); + } + + + //stub of arg size 3, index 6 + public static int stub_hook_6(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 6), null , a0, a1, a2); + } + + + //stub of arg size 3, index 7 + public static int stub_hook_7(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 7), null , a0, a1, a2); + } + + + //stub of arg size 3, index 8 + public static int stub_hook_8(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 8), null , a0, a1, a2); + } + + + //stub of arg size 3, index 9 + public static int stub_hook_9(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 9), null , a0, a1, a2); + } + + + //stub of arg size 3, index 10 + public static int stub_hook_10(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 10), null , a0, a1, a2); + } + + + //stub of arg size 3, index 11 + public static int stub_hook_11(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 11), null , a0, a1, a2); + } + + + //stub of arg size 3, index 12 + public static int stub_hook_12(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 12), null , a0, a1, a2); + } + + + //stub of arg size 3, index 13 + public static int stub_hook_13(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 13), null , a0, a1, a2); + } + + + //stub of arg size 3, index 14 + public static int stub_hook_14(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 14), null , a0, a1, a2); + } + + + //stub of arg size 3, index 15 + public static int stub_hook_15(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 15), null , a0, a1, a2); + } + + + //stub of arg size 3, index 16 + public static int stub_hook_16(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 16), null , a0, a1, a2); + } + + + //stub of arg size 3, index 17 + public static int stub_hook_17(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 17), null , a0, a1, a2); + } + + + //stub of arg size 3, index 18 + public static int stub_hook_18(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 18), null , a0, a1, a2); + } + + + //stub of arg size 3, index 19 + public static int stub_hook_19(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 19), null , a0, a1, a2); + } + + + //stub of arg size 3, index 20 + public static int stub_hook_20(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 20), null , a0, a1, a2); + } + + + //stub of arg size 3, index 21 + public static int stub_hook_21(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 21), null , a0, a1, a2); + } + + + //stub of arg size 3, index 22 + public static int stub_hook_22(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 22), null , a0, a1, a2); + } + + + //stub of arg size 3, index 23 + public static int stub_hook_23(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 23), null , a0, a1, a2); + } + + + //stub of arg size 3, index 24 + public static int stub_hook_24(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 24), null , a0, a1, a2); + } + + + //stub of arg size 3, index 25 + public static int stub_hook_25(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 25), null , a0, a1, a2); + } + + + //stub of arg size 3, index 26 + public static int stub_hook_26(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 26), null , a0, a1, a2); + } + + + //stub of arg size 3, index 27 + public static int stub_hook_27(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 27), null , a0, a1, a2); + } + + + //stub of arg size 3, index 28 + public static int stub_hook_28(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 28), null , a0, a1, a2); + } + + + //stub of arg size 3, index 29 + public static int stub_hook_29(int a0, int a1, int a2) throws Throwable { + return (int) hookBridge(getMethodId(3, 29), null , a0, a1, a2); + } + + + //stub of arg size 4, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 0), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 1), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 2), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 3), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 4), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 5 + public static int stub_hook_5(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 5), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 6 + public static int stub_hook_6(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 6), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 7 + public static int stub_hook_7(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 7), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 8 + public static int stub_hook_8(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 8), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 9 + public static int stub_hook_9(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 9), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 10 + public static int stub_hook_10(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 10), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 11 + public static int stub_hook_11(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 11), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 12 + public static int stub_hook_12(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 12), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 13 + public static int stub_hook_13(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 13), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 14 + public static int stub_hook_14(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 14), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 15 + public static int stub_hook_15(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 15), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 16 + public static int stub_hook_16(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 16), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 17 + public static int stub_hook_17(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 17), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 18 + public static int stub_hook_18(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 18), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 19 + public static int stub_hook_19(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 19), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 20 + public static int stub_hook_20(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 20), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 21 + public static int stub_hook_21(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 21), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 22 + public static int stub_hook_22(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 22), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 23 + public static int stub_hook_23(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 23), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 24 + public static int stub_hook_24(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 24), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 25 + public static int stub_hook_25(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 25), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 26 + public static int stub_hook_26(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 26), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 27 + public static int stub_hook_27(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 27), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 28 + public static int stub_hook_28(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 28), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 29 + public static int stub_hook_29(int a0, int a1, int a2, int a3) throws Throwable { + return (int) hookBridge(getMethodId(4, 29), null , a0, a1, a2, a3); + } + + + //stub of arg size 5, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 0), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 1), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 2), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 3), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 4), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 5 + public static int stub_hook_5(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 5), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 6 + public static int stub_hook_6(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 6), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 7 + public static int stub_hook_7(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 7), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 8 + public static int stub_hook_8(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 8), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 9 + public static int stub_hook_9(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 9), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 10 + public static int stub_hook_10(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 10), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 11 + public static int stub_hook_11(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 11), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 12 + public static int stub_hook_12(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 12), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 13 + public static int stub_hook_13(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 13), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 14 + public static int stub_hook_14(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 14), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 15 + public static int stub_hook_15(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 15), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 16 + public static int stub_hook_16(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 16), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 17 + public static int stub_hook_17(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 17), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 18 + public static int stub_hook_18(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 18), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 19 + public static int stub_hook_19(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 19), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 20 + public static int stub_hook_20(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 20), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 21 + public static int stub_hook_21(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 21), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 22 + public static int stub_hook_22(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 22), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 23 + public static int stub_hook_23(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 23), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 24 + public static int stub_hook_24(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 24), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 25 + public static int stub_hook_25(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 25), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 26 + public static int stub_hook_26(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 26), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 27 + public static int stub_hook_27(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 27), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 28 + public static int stub_hook_28(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 28), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 29 + public static int stub_hook_29(int a0, int a1, int a2, int a3, int a4) throws Throwable { + return (int) hookBridge(getMethodId(5, 29), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 6, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 0), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 1), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 2), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 3), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 4), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 5 + public static int stub_hook_5(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 5), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 6 + public static int stub_hook_6(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 6), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 7 + public static int stub_hook_7(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 7), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 8 + public static int stub_hook_8(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 8), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 9 + public static int stub_hook_9(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 9), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 10 + public static int stub_hook_10(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 10), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 11 + public static int stub_hook_11(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 11), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 12 + public static int stub_hook_12(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 12), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 13 + public static int stub_hook_13(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 13), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 14 + public static int stub_hook_14(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 14), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 15 + public static int stub_hook_15(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 15), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 16 + public static int stub_hook_16(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 16), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 17 + public static int stub_hook_17(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 17), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 18 + public static int stub_hook_18(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 18), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 19 + public static int stub_hook_19(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 19), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 20 + public static int stub_hook_20(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 20), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 21 + public static int stub_hook_21(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 21), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 22 + public static int stub_hook_22(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 22), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 23 + public static int stub_hook_23(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 23), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 24 + public static int stub_hook_24(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 24), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 25 + public static int stub_hook_25(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 25), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 26 + public static int stub_hook_26(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 26), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 27 + public static int stub_hook_27(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 27), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 28 + public static int stub_hook_28(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 28), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 29 + public static int stub_hook_29(int a0, int a1, int a2, int a3, int a4, int a5) throws Throwable { + return (int) hookBridge(getMethodId(6, 29), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 7, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 0), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 1), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 2), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 3), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 4), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 5 + public static int stub_hook_5(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 5), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 6 + public static int stub_hook_6(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 6), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 7 + public static int stub_hook_7(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 7), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 8 + public static int stub_hook_8(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 8), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 9 + public static int stub_hook_9(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 9), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 10 + public static int stub_hook_10(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 10), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 11 + public static int stub_hook_11(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 11), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 12 + public static int stub_hook_12(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 12), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 13 + public static int stub_hook_13(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 13), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 14 + public static int stub_hook_14(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 14), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 15 + public static int stub_hook_15(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 15), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 16 + public static int stub_hook_16(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 16), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 17 + public static int stub_hook_17(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 17), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 18 + public static int stub_hook_18(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 18), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 19 + public static int stub_hook_19(int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws Throwable { + return (int) hookBridge(getMethodId(7, 19), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 8, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 0), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 1), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 2), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 3), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 4), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 5 + public static int stub_hook_5(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 5), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 6 + public static int stub_hook_6(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 6), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 7 + public static int stub_hook_7(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 7), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 8 + public static int stub_hook_8(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 8), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 9 + public static int stub_hook_9(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) throws Throwable { + return (int) hookBridge(getMethodId(8, 9), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 9, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 3), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 4), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 5 + public static int stub_hook_5(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 5), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 6 + public static int stub_hook_6(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 6), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 7 + public static int stub_hook_7(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 7), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 8 + public static int stub_hook_8(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 8), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 9 + public static int stub_hook_9(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) throws Throwable { + return (int) hookBridge(getMethodId(9, 9), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 10, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) throws Throwable { + return (int) hookBridge(getMethodId(10, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) throws Throwable { + return (int) hookBridge(getMethodId(10, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) throws Throwable { + return (int) hookBridge(getMethodId(10, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) throws Throwable { + return (int) hookBridge(getMethodId(10, 3), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) throws Throwable { + return (int) hookBridge(getMethodId(10, 4), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 11, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) throws Throwable { + return (int) hookBridge(getMethodId(11, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) throws Throwable { + return (int) hookBridge(getMethodId(11, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) throws Throwable { + return (int) hookBridge(getMethodId(11, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 3 + public static int stub_hook_3(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) throws Throwable { + return (int) hookBridge(getMethodId(11, 3), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 4 + public static int stub_hook_4(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) throws Throwable { + return (int) hookBridge(getMethodId(11, 4), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 12, index 0 + public static int stub_hook_0(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11) throws Throwable { + return (int) hookBridge(getMethodId(12, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + + + //stub of arg size 12, index 1 + public static int stub_hook_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11) throws Throwable { + return (int) hookBridge(getMethodId(12, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + + + //stub of arg size 12, index 2 + public static int stub_hook_2(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11) throws Throwable { + return (int) hookBridge(getMethodId(12, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs64.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs64.java new file mode 100644 index 00000000..17887583 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/hookstub/MethodHookerStubs64.java @@ -0,0 +1,1413 @@ +package com.swift.sandhook.xposedcompat.hookstub; + +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.getMethodId; +import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.hookBridge; + +/** +* this file is auto gen by genhookstubs.py +* it is for sandhook internal hooker & backup methods +**/ +public class MethodHookerStubs64 { + + public static boolean hasStubBackup = false; + public static int[] stubSizes = {10, 20, 30, 30, 30, 30, 30, 20, 10, 10, 5, 5, 3}; + + + //stub of arg size 0, index 0 + public static long stub_hook_0() throws Throwable { + return hookBridge(getMethodId(0, 0), null ); + } + + + //stub of arg size 0, index 1 + public static long stub_hook_1() throws Throwable { + return hookBridge(getMethodId(0, 1), null ); + } + + + //stub of arg size 0, index 2 + public static long stub_hook_2() throws Throwable { + return hookBridge(getMethodId(0, 2), null ); + } + + + //stub of arg size 0, index 3 + public static long stub_hook_3() throws Throwable { + return hookBridge(getMethodId(0, 3), null ); + } + + + //stub of arg size 0, index 4 + public static long stub_hook_4() throws Throwable { + return hookBridge(getMethodId(0, 4), null ); + } + + + //stub of arg size 0, index 5 + public static long stub_hook_5() throws Throwable { + return hookBridge(getMethodId(0, 5), null ); + } + + + //stub of arg size 0, index 6 + public static long stub_hook_6() throws Throwable { + return hookBridge(getMethodId(0, 6), null ); + } + + + //stub of arg size 0, index 7 + public static long stub_hook_7() throws Throwable { + return hookBridge(getMethodId(0, 7), null ); + } + + + //stub of arg size 0, index 8 + public static long stub_hook_8() throws Throwable { + return hookBridge(getMethodId(0, 8), null ); + } + + + //stub of arg size 0, index 9 + public static long stub_hook_9() throws Throwable { + return hookBridge(getMethodId(0, 9), null ); + } + + + //stub of arg size 1, index 0 + public static long stub_hook_0(long a0) throws Throwable { + return hookBridge(getMethodId(1, 0), null , a0); + } + + + //stub of arg size 1, index 1 + public static long stub_hook_1(long a0) throws Throwable { + return hookBridge(getMethodId(1, 1), null , a0); + } + + + //stub of arg size 1, index 2 + public static long stub_hook_2(long a0) throws Throwable { + return hookBridge(getMethodId(1, 2), null , a0); + } + + + //stub of arg size 1, index 3 + public static long stub_hook_3(long a0) throws Throwable { + return hookBridge(getMethodId(1, 3), null , a0); + } + + + //stub of arg size 1, index 4 + public static long stub_hook_4(long a0) throws Throwable { + return hookBridge(getMethodId(1, 4), null , a0); + } + + + //stub of arg size 1, index 5 + public static long stub_hook_5(long a0) throws Throwable { + return hookBridge(getMethodId(1, 5), null , a0); + } + + + //stub of arg size 1, index 6 + public static long stub_hook_6(long a0) throws Throwable { + return hookBridge(getMethodId(1, 6), null , a0); + } + + + //stub of arg size 1, index 7 + public static long stub_hook_7(long a0) throws Throwable { + return hookBridge(getMethodId(1, 7), null , a0); + } + + + //stub of arg size 1, index 8 + public static long stub_hook_8(long a0) throws Throwable { + return hookBridge(getMethodId(1, 8), null , a0); + } + + + //stub of arg size 1, index 9 + public static long stub_hook_9(long a0) throws Throwable { + return hookBridge(getMethodId(1, 9), null , a0); + } + + + //stub of arg size 1, index 10 + public static long stub_hook_10(long a0) throws Throwable { + return hookBridge(getMethodId(1, 10), null , a0); + } + + + //stub of arg size 1, index 11 + public static long stub_hook_11(long a0) throws Throwable { + return hookBridge(getMethodId(1, 11), null , a0); + } + + + //stub of arg size 1, index 12 + public static long stub_hook_12(long a0) throws Throwable { + return hookBridge(getMethodId(1, 12), null , a0); + } + + + //stub of arg size 1, index 13 + public static long stub_hook_13(long a0) throws Throwable { + return hookBridge(getMethodId(1, 13), null , a0); + } + + + //stub of arg size 1, index 14 + public static long stub_hook_14(long a0) throws Throwable { + return hookBridge(getMethodId(1, 14), null , a0); + } + + + //stub of arg size 1, index 15 + public static long stub_hook_15(long a0) throws Throwable { + return hookBridge(getMethodId(1, 15), null , a0); + } + + + //stub of arg size 1, index 16 + public static long stub_hook_16(long a0) throws Throwable { + return hookBridge(getMethodId(1, 16), null , a0); + } + + + //stub of arg size 1, index 17 + public static long stub_hook_17(long a0) throws Throwable { + return hookBridge(getMethodId(1, 17), null , a0); + } + + + //stub of arg size 1, index 18 + public static long stub_hook_18(long a0) throws Throwable { + return hookBridge(getMethodId(1, 18), null , a0); + } + + + //stub of arg size 1, index 19 + public static long stub_hook_19(long a0) throws Throwable { + return hookBridge(getMethodId(1, 19), null , a0); + } + + + //stub of arg size 2, index 0 + public static long stub_hook_0(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 0), null , a0, a1); + } + + + //stub of arg size 2, index 1 + public static long stub_hook_1(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 1), null , a0, a1); + } + + + //stub of arg size 2, index 2 + public static long stub_hook_2(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 2), null , a0, a1); + } + + + //stub of arg size 2, index 3 + public static long stub_hook_3(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 3), null , a0, a1); + } + + + //stub of arg size 2, index 4 + public static long stub_hook_4(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 4), null , a0, a1); + } + + + //stub of arg size 2, index 5 + public static long stub_hook_5(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 5), null , a0, a1); + } + + + //stub of arg size 2, index 6 + public static long stub_hook_6(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 6), null , a0, a1); + } + + + //stub of arg size 2, index 7 + public static long stub_hook_7(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 7), null , a0, a1); + } + + + //stub of arg size 2, index 8 + public static long stub_hook_8(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 8), null , a0, a1); + } + + + //stub of arg size 2, index 9 + public static long stub_hook_9(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 9), null , a0, a1); + } + + + //stub of arg size 2, index 10 + public static long stub_hook_10(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 10), null , a0, a1); + } + + + //stub of arg size 2, index 11 + public static long stub_hook_11(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 11), null , a0, a1); + } + + + //stub of arg size 2, index 12 + public static long stub_hook_12(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 12), null , a0, a1); + } + + + //stub of arg size 2, index 13 + public static long stub_hook_13(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 13), null , a0, a1); + } + + + //stub of arg size 2, index 14 + public static long stub_hook_14(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 14), null , a0, a1); + } + + + //stub of arg size 2, index 15 + public static long stub_hook_15(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 15), null , a0, a1); + } + + + //stub of arg size 2, index 16 + public static long stub_hook_16(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 16), null , a0, a1); + } + + + //stub of arg size 2, index 17 + public static long stub_hook_17(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 17), null , a0, a1); + } + + + //stub of arg size 2, index 18 + public static long stub_hook_18(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 18), null , a0, a1); + } + + + //stub of arg size 2, index 19 + public static long stub_hook_19(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 19), null , a0, a1); + } + + + //stub of arg size 2, index 20 + public static long stub_hook_20(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 20), null , a0, a1); + } + + + //stub of arg size 2, index 21 + public static long stub_hook_21(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 21), null , a0, a1); + } + + + //stub of arg size 2, index 22 + public static long stub_hook_22(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 22), null , a0, a1); + } + + + //stub of arg size 2, index 23 + public static long stub_hook_23(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 23), null , a0, a1); + } + + + //stub of arg size 2, index 24 + public static long stub_hook_24(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 24), null , a0, a1); + } + + + //stub of arg size 2, index 25 + public static long stub_hook_25(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 25), null , a0, a1); + } + + + //stub of arg size 2, index 26 + public static long stub_hook_26(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 26), null , a0, a1); + } + + + //stub of arg size 2, index 27 + public static long stub_hook_27(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 27), null , a0, a1); + } + + + //stub of arg size 2, index 28 + public static long stub_hook_28(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 28), null , a0, a1); + } + + + //stub of arg size 2, index 29 + public static long stub_hook_29(long a0, long a1) throws Throwable { + return hookBridge(getMethodId(2, 29), null , a0, a1); + } + + + //stub of arg size 3, index 0 + public static long stub_hook_0(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 0), null , a0, a1, a2); + } + + + //stub of arg size 3, index 1 + public static long stub_hook_1(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 1), null , a0, a1, a2); + } + + + //stub of arg size 3, index 2 + public static long stub_hook_2(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 2), null , a0, a1, a2); + } + + + //stub of arg size 3, index 3 + public static long stub_hook_3(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 3), null , a0, a1, a2); + } + + + //stub of arg size 3, index 4 + public static long stub_hook_4(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 4), null , a0, a1, a2); + } + + + //stub of arg size 3, index 5 + public static long stub_hook_5(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 5), null , a0, a1, a2); + } + + + //stub of arg size 3, index 6 + public static long stub_hook_6(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 6), null , a0, a1, a2); + } + + + //stub of arg size 3, index 7 + public static long stub_hook_7(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 7), null , a0, a1, a2); + } + + + //stub of arg size 3, index 8 + public static long stub_hook_8(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 8), null , a0, a1, a2); + } + + + //stub of arg size 3, index 9 + public static long stub_hook_9(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 9), null , a0, a1, a2); + } + + + //stub of arg size 3, index 10 + public static long stub_hook_10(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 10), null , a0, a1, a2); + } + + + //stub of arg size 3, index 11 + public static long stub_hook_11(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 11), null , a0, a1, a2); + } + + + //stub of arg size 3, index 12 + public static long stub_hook_12(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 12), null , a0, a1, a2); + } + + + //stub of arg size 3, index 13 + public static long stub_hook_13(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 13), null , a0, a1, a2); + } + + + //stub of arg size 3, index 14 + public static long stub_hook_14(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 14), null , a0, a1, a2); + } + + + //stub of arg size 3, index 15 + public static long stub_hook_15(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 15), null , a0, a1, a2); + } + + + //stub of arg size 3, index 16 + public static long stub_hook_16(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 16), null , a0, a1, a2); + } + + + //stub of arg size 3, index 17 + public static long stub_hook_17(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 17), null , a0, a1, a2); + } + + + //stub of arg size 3, index 18 + public static long stub_hook_18(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 18), null , a0, a1, a2); + } + + + //stub of arg size 3, index 19 + public static long stub_hook_19(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 19), null , a0, a1, a2); + } + + + //stub of arg size 3, index 20 + public static long stub_hook_20(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 20), null , a0, a1, a2); + } + + + //stub of arg size 3, index 21 + public static long stub_hook_21(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 21), null , a0, a1, a2); + } + + + //stub of arg size 3, index 22 + public static long stub_hook_22(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 22), null , a0, a1, a2); + } + + + //stub of arg size 3, index 23 + public static long stub_hook_23(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 23), null , a0, a1, a2); + } + + + //stub of arg size 3, index 24 + public static long stub_hook_24(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 24), null , a0, a1, a2); + } + + + //stub of arg size 3, index 25 + public static long stub_hook_25(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 25), null , a0, a1, a2); + } + + + //stub of arg size 3, index 26 + public static long stub_hook_26(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 26), null , a0, a1, a2); + } + + + //stub of arg size 3, index 27 + public static long stub_hook_27(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 27), null , a0, a1, a2); + } + + + //stub of arg size 3, index 28 + public static long stub_hook_28(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 28), null , a0, a1, a2); + } + + + //stub of arg size 3, index 29 + public static long stub_hook_29(long a0, long a1, long a2) throws Throwable { + return hookBridge(getMethodId(3, 29), null , a0, a1, a2); + } + + + //stub of arg size 4, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 0), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 1), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 2), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 3), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 4), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 5 + public static long stub_hook_5(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 5), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 6 + public static long stub_hook_6(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 6), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 7 + public static long stub_hook_7(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 7), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 8 + public static long stub_hook_8(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 8), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 9 + public static long stub_hook_9(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 9), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 10 + public static long stub_hook_10(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 10), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 11 + public static long stub_hook_11(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 11), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 12 + public static long stub_hook_12(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 12), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 13 + public static long stub_hook_13(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 13), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 14 + public static long stub_hook_14(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 14), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 15 + public static long stub_hook_15(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 15), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 16 + public static long stub_hook_16(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 16), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 17 + public static long stub_hook_17(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 17), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 18 + public static long stub_hook_18(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 18), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 19 + public static long stub_hook_19(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 19), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 20 + public static long stub_hook_20(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 20), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 21 + public static long stub_hook_21(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 21), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 22 + public static long stub_hook_22(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 22), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 23 + public static long stub_hook_23(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 23), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 24 + public static long stub_hook_24(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 24), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 25 + public static long stub_hook_25(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 25), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 26 + public static long stub_hook_26(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 26), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 27 + public static long stub_hook_27(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 27), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 28 + public static long stub_hook_28(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 28), null , a0, a1, a2, a3); + } + + + //stub of arg size 4, index 29 + public static long stub_hook_29(long a0, long a1, long a2, long a3) throws Throwable { + return hookBridge(getMethodId(4, 29), null , a0, a1, a2, a3); + } + + + //stub of arg size 5, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 0), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 1), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 2), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 3), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 4), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 5 + public static long stub_hook_5(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 5), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 6 + public static long stub_hook_6(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 6), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 7 + public static long stub_hook_7(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 7), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 8 + public static long stub_hook_8(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 8), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 9 + public static long stub_hook_9(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 9), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 10 + public static long stub_hook_10(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 10), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 11 + public static long stub_hook_11(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 11), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 12 + public static long stub_hook_12(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 12), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 13 + public static long stub_hook_13(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 13), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 14 + public static long stub_hook_14(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 14), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 15 + public static long stub_hook_15(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 15), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 16 + public static long stub_hook_16(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 16), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 17 + public static long stub_hook_17(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 17), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 18 + public static long stub_hook_18(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 18), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 19 + public static long stub_hook_19(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 19), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 20 + public static long stub_hook_20(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 20), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 21 + public static long stub_hook_21(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 21), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 22 + public static long stub_hook_22(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 22), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 23 + public static long stub_hook_23(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 23), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 24 + public static long stub_hook_24(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 24), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 25 + public static long stub_hook_25(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 25), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 26 + public static long stub_hook_26(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 26), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 27 + public static long stub_hook_27(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 27), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 28 + public static long stub_hook_28(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 28), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 5, index 29 + public static long stub_hook_29(long a0, long a1, long a2, long a3, long a4) throws Throwable { + return hookBridge(getMethodId(5, 29), null , a0, a1, a2, a3, a4); + } + + + //stub of arg size 6, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 0), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 1), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 2), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 3), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 4), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 5 + public static long stub_hook_5(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 5), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 6 + public static long stub_hook_6(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 6), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 7 + public static long stub_hook_7(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 7), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 8 + public static long stub_hook_8(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 8), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 9 + public static long stub_hook_9(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 9), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 10 + public static long stub_hook_10(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 10), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 11 + public static long stub_hook_11(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 11), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 12 + public static long stub_hook_12(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 12), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 13 + public static long stub_hook_13(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 13), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 14 + public static long stub_hook_14(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 14), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 15 + public static long stub_hook_15(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 15), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 16 + public static long stub_hook_16(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 16), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 17 + public static long stub_hook_17(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 17), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 18 + public static long stub_hook_18(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 18), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 19 + public static long stub_hook_19(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 19), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 20 + public static long stub_hook_20(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 20), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 21 + public static long stub_hook_21(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 21), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 22 + public static long stub_hook_22(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 22), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 23 + public static long stub_hook_23(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 23), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 24 + public static long stub_hook_24(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 24), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 25 + public static long stub_hook_25(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 25), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 26 + public static long stub_hook_26(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 26), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 27 + public static long stub_hook_27(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 27), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 28 + public static long stub_hook_28(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 28), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 6, index 29 + public static long stub_hook_29(long a0, long a1, long a2, long a3, long a4, long a5) throws Throwable { + return hookBridge(getMethodId(6, 29), null , a0, a1, a2, a3, a4, a5); + } + + + //stub of arg size 7, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 0), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 1), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 2), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 3), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 4), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 5 + public static long stub_hook_5(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 5), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 6 + public static long stub_hook_6(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 6), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 7 + public static long stub_hook_7(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 7), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 8 + public static long stub_hook_8(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 8), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 9 + public static long stub_hook_9(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 9), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 10 + public static long stub_hook_10(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 10), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 11 + public static long stub_hook_11(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 11), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 12 + public static long stub_hook_12(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 12), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 13 + public static long stub_hook_13(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 13), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 14 + public static long stub_hook_14(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 14), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 15 + public static long stub_hook_15(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 15), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 16 + public static long stub_hook_16(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 16), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 17 + public static long stub_hook_17(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 17), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 18 + public static long stub_hook_18(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 18), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 7, index 19 + public static long stub_hook_19(long a0, long a1, long a2, long a3, long a4, long a5, long a6) throws Throwable { + return hookBridge(getMethodId(7, 19), null , a0, a1, a2, a3, a4, a5, a6); + } + + + //stub of arg size 8, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 0), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 1), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 2), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 3), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 4), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 5 + public static long stub_hook_5(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 5), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 6 + public static long stub_hook_6(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 6), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 7 + public static long stub_hook_7(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 7), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 8 + public static long stub_hook_8(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 8), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 8, index 9 + public static long stub_hook_9(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) throws Throwable { + return hookBridge(getMethodId(8, 9), null , a0, a1, a2, a3, a4, a5, a6, a7); + } + + + //stub of arg size 9, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 3), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 4), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 5 + public static long stub_hook_5(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 5), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 6 + public static long stub_hook_6(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 6), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 7 + public static long stub_hook_7(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 7), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 8 + public static long stub_hook_8(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 8), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 9, index 9 + public static long stub_hook_9(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) throws Throwable { + return hookBridge(getMethodId(9, 9), null , a0, a1, a2, a3, a4, a5, a6, a7, a8); + } + + + //stub of arg size 10, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) throws Throwable { + return hookBridge(getMethodId(10, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) throws Throwable { + return hookBridge(getMethodId(10, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) throws Throwable { + return hookBridge(getMethodId(10, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) throws Throwable { + return hookBridge(getMethodId(10, 3), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 10, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) throws Throwable { + return hookBridge(getMethodId(10, 4), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + + //stub of arg size 11, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) throws Throwable { + return hookBridge(getMethodId(11, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) throws Throwable { + return hookBridge(getMethodId(11, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) throws Throwable { + return hookBridge(getMethodId(11, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 3 + public static long stub_hook_3(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) throws Throwable { + return hookBridge(getMethodId(11, 3), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 11, index 4 + public static long stub_hook_4(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) throws Throwable { + return hookBridge(getMethodId(11, 4), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + + + //stub of arg size 12, index 0 + public static long stub_hook_0(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11) throws Throwable { + return hookBridge(getMethodId(12, 0), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + + + //stub of arg size 12, index 1 + public static long stub_hook_1(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11) throws Throwable { + return hookBridge(getMethodId(12, 1), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + + + //stub of arg size 12, index 2 + public static long stub_hook_2(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11) throws Throwable { + return hookBridge(getMethodId(12, 2), null , a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/ErrorCatch.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/ErrorCatch.java new file mode 100644 index 00000000..c6a5a82d --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/ErrorCatch.java @@ -0,0 +1,22 @@ +package com.swift.sandhook.xposedcompat.methodgen; + +import android.util.Log; + +import com.swift.sandhook.SandHook; +import com.swift.sandhook.xposedcompat.XposedCompat; + +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +public class ErrorCatch { + + public static Object callOriginError(Member originMethod, Method backupMethod, Object thiz, Object[] args) throws Throwable { + if (XposedCompat.retryWhenCallOriginError) { + Log.w("SandHook", "method <" + originMethod.toString() + "> use invoke to call origin!"); + return SandHook.callOriginMethod(originMethod, backupMethod, thiz, args); + } else { + return null; + } + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookMaker.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookMaker.java new file mode 100644 index 00000000..ae5c05e7 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookMaker.java @@ -0,0 +1,14 @@ +package com.swift.sandhook.xposedcompat.methodgen; + +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import de.robv.android.xposed.XposedBridge; + +public interface HookMaker { + void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, + ClassLoader appClassLoader, String dexDirPath) throws Exception; + Method getHookMethod(); + Method getBackupMethod(); + Method getCallBackupMethod(); +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMaker.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMaker.java new file mode 100644 index 00000000..4a72537b --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMaker.java @@ -0,0 +1,698 @@ +package com.swift.sandhook.xposedcompat.methodgen; + +import android.text.TextUtils; + +import com.swift.sandhook.SandHook; +import com.swift.sandhook.SandHookMethodResolver; +import com.swift.sandhook.wrapper.HookWrapper; +import com.swift.sandhook.xposedcompat.XposedCompat; +import com.swift.sandhook.xposedcompat.utils.DexLog; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Map; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; +import external.com.android.dx.BinaryOp; +import external.com.android.dx.Code; +import external.com.android.dx.Comparison; +import external.com.android.dx.DexMaker; +import external.com.android.dx.FieldId; +import external.com.android.dx.Label; +import external.com.android.dx.Local; +import external.com.android.dx.MethodId; +import external.com.android.dx.TypeId; + +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.MD5; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoBoxIfNecessary; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoUnboxIfNecessary; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.createResultLocals; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.getObjTypeIdIfPrimitive; + +public class HookerDexMaker implements HookMaker { + + public static final String METHOD_NAME_BACKUP = "backup"; + public static final String METHOD_NAME_HOOK = "hook"; + public static final String METHOD_NAME_CALL_BACKUP = "callBackup"; + public static final String METHOD_NAME_SETUP = "setup"; + public static final String METHOD_NAME_LOG = "printMethodHookIn"; + public static final TypeId objArrayTypeId = TypeId.get(Object[].class); + private static final String CLASS_DESC_PREFIX = "L"; + private static final String CLASS_NAME_PREFIX = "SandHooker"; + private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; + private static final String FIELD_NAME_METHOD = "method"; + private static final String FIELD_NAME_BACKUP_METHOD = "backupMethod"; + private static final String PARAMS_FIELD_NAME_METHOD = "method"; + private static final String PARAMS_FIELD_NAME_THIS_OBJECT = "thisObject"; + private static final String PARAMS_FIELD_NAME_ARGS = "args"; + private static final String CALLBACK_METHOD_NAME_BEFORE = "callBeforeHookedMethod"; + private static final String CALLBACK_METHOD_NAME_AFTER = "callAfterHookedMethod"; + private static final TypeId throwableTypeId = TypeId.get(Throwable.class); + private static final TypeId memberTypeId = TypeId.get(Member.class); + private static final TypeId methodTypeId = TypeId.get(Method.class); + private static final TypeId callbackTypeId = TypeId.get(XC_MethodHook.class); + private static final TypeId hookInfoTypeId + = TypeId.get(XposedBridge.AdditionalHookInfo.class); + private static final TypeId callbacksTypeId + = TypeId.get(XposedBridge.CopyOnWriteSortedSet.class); + private static final TypeId paramTypeId + = TypeId.get(XC_MethodHook.MethodHookParam.class); + private static final MethodId setResultMethodId = + paramTypeId.getMethod(TypeId.VOID, "setResult", TypeId.OBJECT); + private static final MethodId setThrowableMethodId = + paramTypeId.getMethod(TypeId.VOID, "setThrowable", throwableTypeId); + private static final MethodId getResultMethodId = + paramTypeId.getMethod(TypeId.OBJECT, "getResult"); + private static final MethodId getThrowableMethodId = + paramTypeId.getMethod(throwableTypeId, "getThrowable"); + private static final MethodId hasThrowableMethodId = + paramTypeId.getMethod(TypeId.BOOLEAN, "hasThrowable"); + private static final MethodId callAfterCallbackMethodId = + callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_AFTER, paramTypeId); + private static final MethodId callBeforeCallbackMethodId = + callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_BEFORE, paramTypeId); + private static final FieldId returnEarlyFieldId = + paramTypeId.getField(TypeId.BOOLEAN, "returnEarly"); + private static final TypeId xposedBridgeTypeId = TypeId.get(XposedBridge.class); + private static final MethodId logThrowableMethodId = + xposedBridgeTypeId.getMethod(TypeId.VOID, "log", throwableTypeId); + + private FieldId mHookInfoFieldId; + private FieldId mMethodFieldId; + private FieldId mBackupMethodFieldId; + private MethodId mBackupMethodId; + private MethodId mCallBackupMethodId; + private MethodId mHookMethodId; + private MethodId mPrintLogMethodId; + private MethodId mSandHookCallOriginMethodId; + + private TypeId mHookerTypeId; + private TypeId[] mParameterTypeIds; + private Class[] mActualParameterTypes; + private Class mReturnType; + private TypeId mReturnTypeId; + private boolean mIsStatic; + // TODO use this to generate methods + private boolean mHasThrowable; + + private DexMaker mDexMaker; + private Member mMember; + private XposedBridge.AdditionalHookInfo mHookInfo; + private ClassLoader mAppClassLoader; + private Class mHookClass; + private Method mHookMethod; + private Method mBackupMethod; + private Method mCallBackupMethod; + private String mDexDirPath; + + private static TypeId[] getParameterTypeIds(Class[] parameterTypes, boolean isStatic) { + int parameterSize = parameterTypes.length; + int targetParameterSize = isStatic ? parameterSize : parameterSize + 1; + TypeId[] parameterTypeIds = new TypeId[targetParameterSize]; + int offset = 0; + if (!isStatic) { + parameterTypeIds[0] = TypeId.OBJECT; + offset = 1; + } + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypeIds[i + offset] = TypeId.get(parameterTypes[i]); + } + return parameterTypeIds; + } + + private static Class[] getParameterTypes(Class[] parameterTypes, boolean isStatic) { + if (isStatic) { + return parameterTypes; + } + int parameterSize = parameterTypes.length; + int targetParameterSize = parameterSize + 1; + Class[] newParameterTypes = new Class[targetParameterSize]; + int offset = 1; + newParameterTypes[0] = Object.class; + System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length); + return newParameterTypes; + } + + public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, + ClassLoader appClassLoader, String dexDirPath) throws Exception { + if (member instanceof Method) { + Method method = (Method) member; + mIsStatic = Modifier.isStatic(method.getModifiers()); + mReturnType = method.getReturnType(); + if (mReturnType.equals(Void.class) || mReturnType.equals(void.class) + || mReturnType.isPrimitive()) { + mReturnTypeId = TypeId.get(mReturnType); + } else { + // all others fallback to plain Object for convenience + mReturnType = Object.class; + mReturnTypeId = TypeId.OBJECT; + } + mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic); + mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic); + mHasThrowable = method.getExceptionTypes().length > 0; + } else if (member instanceof Constructor) { + Constructor constructor = (Constructor) member; + mIsStatic = false; + mReturnType = void.class; + mReturnTypeId = TypeId.VOID; + mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic); + mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic); + mHasThrowable = constructor.getExceptionTypes().length > 0; + } else if (member.getDeclaringClass().isInterface()) { + throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString()); + } else if (Modifier.isAbstract(member.getModifiers())) { + throw new IllegalArgumentException("Cannot hook abstract methods: " + member.toString()); + } else { + throw new IllegalArgumentException("Only methods and constructors can be hooked: " + member.toString()); + } + mMember = member; + mHookInfo = hookInfo; + mDexDirPath = dexDirPath; + if (appClassLoader == null + || appClassLoader.getClass().getName().equals("java.lang.BootClassLoader")) { + mAppClassLoader = this.getClass().getClassLoader(); + } else { + mAppClassLoader = appClassLoader; + } + + mDexMaker = new DexMaker(); + // Generate a Hooker class. + String className = getClassName(mMember); + String dexName = className + ".jar"; + + HookWrapper.HookEntity hookEntity = null; + //try load cache first + try { + ClassLoader loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName); + if (loader != null) { + hookEntity = loadHookerClass(loader, className); + } + } catch (Throwable throwable) {} + + //do generate + if (hookEntity == null) { + hookEntity = doMake(className, dexName); + } + SandHook.hook(hookEntity); + } + + private HookWrapper.HookEntity doMake(String className, String dexName) throws Exception { + mHookerTypeId = TypeId.get(CLASS_DESC_PREFIX + className + ";"); + mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT); + generateFields(); + generateSetupMethod(); + if (XposedCompat.retryWhenCallOriginError) { + generateBackupAndCallOriginCheckMethod(); + } else { + generateBackupMethod(); + } + generateCallBackupMethod(); + generateHookMethod(); + + ClassLoader loader; + if (TextUtils.isEmpty(mDexDirPath)) { + throw new IllegalArgumentException("dexDirPath should not be empty!!!"); + } + // Create the dex file and load it. + loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName); + return loadHookerClass(loader, className); + } + + private HookWrapper.HookEntity loadHookerClass(ClassLoader loader, String className) throws Exception { + mHookClass = loader.loadClass(className); + // Execute our newly-generated code in-process. + mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); + mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); + mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); + SandHook.resolveStaticMethod(mCallBackupMethod); + SandHookMethodResolver.resolveMethod(mCallBackupMethod, mBackupMethod); + SandHook.compileMethod(mCallBackupMethod); + mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, Method.class, XposedBridge.AdditionalHookInfo.class).invoke(null, mMember, mBackupMethod, mHookInfo); + return new HookWrapper.HookEntity(mMember, mHookMethod, mBackupMethod); + } + + private String getClassName(Member originMethod) { + return CLASS_NAME_PREFIX + "_" + MD5(originMethod.toString()); + } + + public Method getHookMethod() { + return mHookMethod; + } + + public Method getBackupMethod() { + return mBackupMethod; + } + + public Method getCallBackupMethod() { + return mCallBackupMethod; + } + + public Class getHookClass() { + return mHookClass; + } + + private void generateFields() { + mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO); + mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD); + mBackupMethodFieldId = mHookerTypeId.getField(methodTypeId, FIELD_NAME_BACKUP_METHOD); + mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null); + mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null); + mDexMaker.declare(mBackupMethodFieldId, Modifier.STATIC, null); + } + + private void generateSetupMethod() { + MethodId setupMethodId = mHookerTypeId.getMethod( + TypeId.VOID, METHOD_NAME_SETUP, memberTypeId, methodTypeId, hookInfoTypeId); + Code code = mDexMaker.declare(setupMethodId, Modifier.PUBLIC | Modifier.STATIC); + // init logic + // get parameters + Local method = code.getParameter(0, memberTypeId); + Local backupMethod = code.getParameter(1, methodTypeId); + Local hookInfo = code.getParameter(2, hookInfoTypeId); + // save params to static + code.sput(mMethodFieldId, method); + code.sput(mBackupMethodFieldId, backupMethod); + code.sput(mHookInfoFieldId, hookInfo); + code.returnVoid(); + } + + private void generateBackupMethod() { + mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds); + Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + + Local method = code.newLocal(memberTypeId); + + Map resultLocals = createResultLocals(code); + MethodId errLogMethod = TypeId.get(DexLog.class).getMethod(TypeId.get(Void.TYPE), "printCallOriginError", memberTypeId); + + + //very very important!!!!!!!!!!! + //add a try cache block avoid inline + Label tryCatchBlock = new Label(); + + code.addCatchClause(throwableTypeId, tryCatchBlock); + code.sget(mMethodFieldId, method); + code.invokeStatic(errLogMethod, null, method); + // start of try + code.mark(tryCatchBlock); + + // do nothing + if (mReturnTypeId.equals(TypeId.VOID)) { + code.returnVoid(); + } else { + // we have limited the returnType to primitives or Object, so this should be safe + code.returnValue(resultLocals.get(mReturnTypeId)); + } + } + + private void generateBackupAndCallOriginCheckMethod() { + mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds); + mSandHookCallOriginMethodId = TypeId.get(ErrorCatch.class).getMethod(TypeId.get(Object.class), "callOriginError", memberTypeId, methodTypeId, TypeId.get(Object.class), TypeId.get(Object[].class)); + MethodId errLogMethod = TypeId.get(DexLog.class).getMethod(TypeId.get(Void.TYPE), "printCallOriginError", methodTypeId); + + Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + + Local method = code.newLocal(memberTypeId); + Local backupMethod = code.newLocal(methodTypeId); + Local thisObject = code.newLocal(TypeId.OBJECT); + Local args = code.newLocal(objArrayTypeId); + Local actualParamSize = code.newLocal(TypeId.INT); + Local argIndex = code.newLocal(TypeId.INT); + Local resultObj = code.newLocal(TypeId.OBJECT); + Label tryCatchBlock = new Label(); + + Local[] allArgsLocals = createParameterLocals(code); + Map resultLocals = createResultLocals(code); + + + + //very very important!!!!!!!!!!! + //add a try cache block avoid inline + + + // start of try + code.addCatchClause(throwableTypeId, tryCatchBlock); + code.sget(mMethodFieldId, method); + code.invokeStatic(errLogMethod, null, method); + //call origin by invoke + code.loadConstant(args, null); + code.loadConstant(argIndex, 0); + code.sget(mBackupMethodFieldId, backupMethod); + int paramsSize = mParameterTypeIds.length; + int offset = 0; + // thisObject + if (mIsStatic) { + // thisObject = null + code.loadConstant(thisObject, null); + } else { + // thisObject = args[0] + offset = 1; + code.move(thisObject, allArgsLocals[0]); + } + +// offset = mIsStatic ? 0 : 1; +// for (int i = offset; i < allArgsLocals.length; i++) { +// code.loadConstant(argIndex, i - offset); +// code.aget(resultObj, args, argIndex); +// autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true); +// } + + // actual args (exclude thisObject if this is not a static method) + code.loadConstant(actualParamSize, paramsSize - offset); + code.newArray(args, actualParamSize); + for (int i = offset; i < paramsSize; i++) { + Local parameter = allArgsLocals[i]; + // save parameter to resultObj as Object + autoBoxIfNecessary(code, resultObj, parameter); + code.loadConstant(argIndex, i - offset); + // save Object to args + code.aput(args, argIndex, resultObj); + } + + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mSandHookCallOriginMethodId, null, method, backupMethod, thisObject, args); + code.returnVoid(); + } else { + code.invokeStatic(mSandHookCallOriginMethodId, resultObj, method, backupMethod, thisObject, args); + TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId); + Local matchObjLocal = resultLocals.get(objTypeId); + code.cast(matchObjLocal, resultObj); + // have to use matching typed Object(Integer, Double ...) to do unboxing + Local toReturn = resultLocals.get(mReturnTypeId); + autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true); + code.returnValue(toReturn); + } + + code.mark(tryCatchBlock); + // do nothing + if (mReturnTypeId.equals(TypeId.VOID)) { + code.returnVoid(); + } else { + // we have limited the returnType to primitives or Object, so this should be safe + code.returnValue(resultLocals.get(mReturnTypeId)); + } + } + + private void generateCallBackupMethod() { + mCallBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_CALL_BACKUP, mParameterTypeIds); + Code code = mDexMaker.declare(mCallBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + // just call backup and return its result + + Local localOrigin = code.newLocal(memberTypeId); + Local localBackup = code.newLocal(methodTypeId); + Local[] allArgsLocals = createParameterLocals(code); + Map resultLocals = createResultLocals(code); + + + code.sget(mMethodFieldId, localOrigin); + code.sget(mBackupMethodFieldId, localBackup); + + MethodId methodId = TypeId.get(SandHook.class).getMethod(TypeId.get(Void.TYPE), "ensureBackupMethod", memberTypeId, methodTypeId); + code.invokeStatic(methodId, null, localOrigin, localBackup); + + + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mBackupMethodId, null, allArgsLocals); + code.returnVoid(); + } else { + Local result = resultLocals.get(mReturnTypeId); + code.invokeStatic(mBackupMethodId, result, allArgsLocals); + code.returnValue(result); + } + } + + private void generateHookMethod() { + mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds); + mPrintLogMethodId = TypeId.get(DexLog.class).getMethod(TypeId.get(Void.TYPE), METHOD_NAME_LOG, TypeId.get(Member.class)); + Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC); + + // code starts + + // prepare common labels + Label noHookReturn = new Label(); + Label incrementAndCheckBefore = new Label(); + Label tryBeforeCatch = new Label(); + Label noExceptionBefore = new Label(); + Label checkAndCallBackup = new Label(); + Label beginCallBefore = new Label(); + Label beginCallAfter = new Label(); + Label tryOrigCatch = new Label(); + Label noExceptionOrig = new Label(); + Label tryAfterCatch = new Label(); + Label decrementAndCheckAfter = new Label(); + Label noBackupThrowable = new Label(); + Label throwThrowable = new Label(); + // prepare locals + Local disableHooks = code.newLocal(TypeId.BOOLEAN); + Local hookInfo = code.newLocal(hookInfoTypeId); + Local callbacks = code.newLocal(callbacksTypeId); + Local snapshot = code.newLocal(objArrayTypeId); + Local snapshotLen = code.newLocal(TypeId.INT); + Local callbackObj = code.newLocal(TypeId.OBJECT); + Local callback = code.newLocal(callbackTypeId); + + Local resultObj = code.newLocal(TypeId.OBJECT); // as a temp Local + Local one = code.newLocal(TypeId.INT); + Local nullObj = code.newLocal(TypeId.OBJECT); + Local throwable = code.newLocal(throwableTypeId); + + Local param = code.newLocal(paramTypeId); + Local method = code.newLocal(memberTypeId); + Local thisObject = code.newLocal(TypeId.OBJECT); + Local args = code.newLocal(objArrayTypeId); + Local returnEarly = code.newLocal(TypeId.BOOLEAN); + + Local actualParamSize = code.newLocal(TypeId.INT); + Local argIndex = code.newLocal(TypeId.INT); + + Local beforeIdx = code.newLocal(TypeId.INT); + Local lastResult = code.newLocal(TypeId.OBJECT); + Local lastThrowable = code.newLocal(throwableTypeId); + Local hasThrowable = code.newLocal(TypeId.BOOLEAN); + + Local[] allArgsLocals = createParameterLocals(code); + + Map resultLocals = createResultLocals(code); + + code.loadConstant(args, null); + code.loadConstant(argIndex, 0); + code.loadConstant(one, 1); + code.loadConstant(snapshotLen, 0); + code.loadConstant(nullObj, null); + + code.sget(mMethodFieldId, method); + //print log + code.invokeStatic(mPrintLogMethodId, null, method); + + // check XposedBridge.disableHooks flag + + FieldId disableHooksField = + xposedBridgeTypeId.getField(TypeId.BOOLEAN, "disableHooks"); + code.sget(disableHooksField, disableHooks); + // disableHooks == true => no hooking + code.compareZ(Comparison.NE, noHookReturn, disableHooks); + + // check callbacks length + code.sget(mHookInfoFieldId, hookInfo); + code.iget(hookInfoTypeId.getField(callbacksTypeId, "callbacks"), callbacks, hookInfo); + code.invokeVirtual(callbacksTypeId.getMethod(objArrayTypeId, "getSnapshot"), snapshot, callbacks); + code.arrayLength(snapshotLen, snapshot); + // snapshotLen == 0 => no hooking + code.compareZ(Comparison.EQ, noHookReturn, snapshotLen); + + // start hooking + + // prepare hooking locals + int paramsSize = mParameterTypeIds.length; + int offset = 0; + // thisObject + if (mIsStatic) { + // thisObject = null + code.loadConstant(thisObject, null); + } else { + // thisObject = args[0] + offset = 1; + code.move(thisObject, allArgsLocals[0]); + } + // actual args (exclude thisObject if this is not a static method) + code.loadConstant(actualParamSize, paramsSize - offset); + code.newArray(args, actualParamSize); + for (int i = offset; i < paramsSize; i++) { + Local parameter = allArgsLocals[i]; + // save parameter to resultObj as Object + autoBoxIfNecessary(code, resultObj, parameter); + code.loadConstant(argIndex, i - offset); + // save Object to args + code.aput(args, argIndex, resultObj); + } + // create param + code.newInstance(param, paramTypeId.getConstructor()); + // set method, thisObject, args + code.iput(paramTypeId.getField(memberTypeId, PARAMS_FIELD_NAME_METHOD), param, method); + code.iput(paramTypeId.getField(TypeId.OBJECT, PARAMS_FIELD_NAME_THIS_OBJECT), param, thisObject); + code.iput(paramTypeId.getField(objArrayTypeId, PARAMS_FIELD_NAME_ARGS), param, args); + + // call beforeCallbacks + code.loadConstant(beforeIdx, 0); + + code.mark(beginCallBefore); + // start of try + code.addCatchClause(throwableTypeId, tryBeforeCatch); + + code.aget(callbackObj, snapshot, beforeIdx); + code.cast(callback, callbackObj); + code.invokeVirtual(callBeforeCallbackMethodId, null, callback, param); + code.jump(noExceptionBefore); + + // end of try + code.removeCatchClause(throwableTypeId); + + // start of catch + code.mark(tryBeforeCatch); + code.moveException(throwable); + code.invokeStatic(logThrowableMethodId, null, throwable); + code.invokeVirtual(setResultMethodId, null, param, nullObj); + code.loadConstant(returnEarly, false); + code.iput(returnEarlyFieldId, param, returnEarly); + code.jump(incrementAndCheckBefore); + + // no exception when calling beforeCallbacks + code.mark(noExceptionBefore); + code.iget(returnEarlyFieldId, returnEarly, param); + // if returnEarly == false, continue + code.compareZ(Comparison.EQ, incrementAndCheckBefore, returnEarly); + // returnEarly == true, break + code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one); + code.jump(checkAndCallBackup); + + // increment and check to continue + code.mark(incrementAndCheckBefore); + code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one); + code.compare(Comparison.LT, beginCallBefore, beforeIdx, snapshotLen); + + // check and call backup + code.mark(checkAndCallBackup); + code.iget(returnEarlyFieldId, returnEarly, param); + // if returnEarly == true, go to call afterCallbacks directly + code.compareZ(Comparison.NE, noExceptionOrig, returnEarly); + // try to call backup + // try start + code.addCatchClause(throwableTypeId, tryOrigCatch); + // we have to load args[] to paramLocals + // because args[] may be changed in beforeHookedMethod + // should consider first param is thisObj if hooked method is not static + offset = mIsStatic ? 0 : 1; + for (int i = offset; i < allArgsLocals.length; i++) { + code.loadConstant(argIndex, i - offset); + code.aget(resultObj, args, argIndex); + autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true); + } + // get pre-created Local with a matching typeId + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mBackupMethodId, null, allArgsLocals); + // TODO maybe keep preset result to do some magic? + code.invokeVirtual(setResultMethodId, null, param, nullObj); + } else { + Local returnedResult = resultLocals.get(mReturnTypeId); + code.invokeStatic(mBackupMethodId, returnedResult, allArgsLocals); + // save returnedResult to resultObj as a Object + autoBoxIfNecessary(code, resultObj, returnedResult); + // save resultObj to param + code.invokeVirtual(setResultMethodId, null, param, resultObj); + } + // go to call afterCallbacks + code.jump(noExceptionOrig); + // try end + code.removeCatchClause(throwableTypeId); + // catch + code.mark(tryOrigCatch); + code.moveException(throwable); + // exception occurred when calling backup, save throwable to param + code.invokeVirtual(setThrowableMethodId, null, param, throwable); + + code.mark(noExceptionOrig); + code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one); + + // call afterCallbacks + code.mark(beginCallAfter); + // save results of backup calling + code.invokeVirtual(getResultMethodId, lastResult, param); + code.invokeVirtual(getThrowableMethodId, lastThrowable, param); + // try start + code.addCatchClause(throwableTypeId, tryAfterCatch); + code.aget(callbackObj, snapshot, beforeIdx); + code.cast(callback, callbackObj); + code.invokeVirtual(callAfterCallbackMethodId, null, callback, param); + // all good, just continue + code.jump(decrementAndCheckAfter); + // try end + code.removeCatchClause(throwableTypeId); + // catch + code.mark(tryAfterCatch); + code.moveException(throwable); + code.invokeStatic(logThrowableMethodId, null, throwable); + // if lastThrowable == null, go to recover lastResult + code.compareZ(Comparison.EQ, noBackupThrowable, lastThrowable); + // lastThrowable != null, recover lastThrowable + code.invokeVirtual(setThrowableMethodId, null, param, lastThrowable); + // continue + code.jump(decrementAndCheckAfter); + code.mark(noBackupThrowable); + // recover lastResult and continue + code.invokeVirtual(setResultMethodId, null, param, lastResult); + // decrement and check continue + code.mark(decrementAndCheckAfter); + code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one); + code.compareZ(Comparison.GE, beginCallAfter, beforeIdx); + + // callbacks end + // return + code.invokeVirtual(hasThrowableMethodId, hasThrowable, param); + // if hasThrowable, throw the throwable and return + code.compareZ(Comparison.NE, throwThrowable, hasThrowable); + // return getResult + if (mReturnTypeId.equals(TypeId.VOID)) { + code.returnVoid(); + } else { + // getResult always return an Object, so save to resultObj + code.invokeVirtual(getResultMethodId, resultObj, param); + // have to unbox it if returnType is primitive + // casting Object + TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId); + Local matchObjLocal = resultLocals.get(objTypeId); + code.cast(matchObjLocal, resultObj); + // have to use matching typed Object(Integer, Double ...) to do unboxing + Local toReturn = resultLocals.get(mReturnTypeId); + autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true); + // return + code.returnValue(toReturn); + } + // throw throwable + code.mark(throwThrowable); + code.invokeVirtual(getThrowableMethodId, throwable, param); + code.throwValue(throwable); + + // call backup and return + code.mark(noHookReturn); + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mBackupMethodId, null, allArgsLocals); + code.returnVoid(); + } else { + Local result = resultLocals.get(mReturnTypeId); + code.invokeStatic(mBackupMethodId, result, allArgsLocals); + code.returnValue(result); + } + } + + private Local[] createParameterLocals(Code code) { + Local[] paramLocals = new Local[mParameterTypeIds.length]; + for (int i = 0; i < mParameterTypeIds.length; i++) { + paramLocals[i] = code.getParameter(i, mParameterTypeIds[i]); + } + return paramLocals; + } +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMakerNew.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMakerNew.java new file mode 100644 index 00000000..ebd99af4 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMakerNew.java @@ -0,0 +1,298 @@ +package com.swift.sandhook.xposedcompat.methodgen; + +import android.text.TextUtils; + +import com.swift.sandhook.SandHook; +import com.swift.sandhook.wrapper.HookWrapper; +import com.swift.sandhook.xposedcompat.hookstub.HookStubManager; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Map; + +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; +import external.com.android.dx.Code; +import external.com.android.dx.DexMaker; +import external.com.android.dx.FieldId; +import external.com.android.dx.Local; +import external.com.android.dx.MethodId; +import external.com.android.dx.TypeId; + +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.MD5; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoBoxIfNecessary; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoUnboxIfNecessary; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.createResultLocals; +import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.getObjTypeIdIfPrimitive; + +public class HookerDexMakerNew implements HookMaker { + + public static final String METHOD_NAME_BACKUP = "backup"; + public static final String METHOD_NAME_HOOK = "hook"; + public static final TypeId objArrayTypeId = TypeId.get(Object[].class); + private static final String CLASS_DESC_PREFIX = "L"; + private static final String CLASS_NAME_PREFIX = "SandHookerN"; + private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; + private static final String FIELD_NAME_METHOD = "method"; + private static final String FIELD_NAME_BACKUP_METHOD = "backupMethod"; + private static final TypeId memberTypeId = TypeId.get(Member.class); + private static final TypeId methodTypeId = TypeId.get(Method.class); + private static final TypeId hookInfoTypeId + = TypeId.get(XposedBridge.AdditionalHookInfo.class); + + + private FieldId mHookInfoFieldId; + private FieldId mMethodFieldId; + private FieldId mBackupMethodFieldId; + private MethodId mHookMethodId; + private MethodId mBackupMethodId; + private MethodId mSandHookBridgeMethodId; + + private TypeId mHookerTypeId; + private TypeId[] mParameterTypeIds; + private Class[] mActualParameterTypes; + private Class mReturnType; + private TypeId mReturnTypeId; + private boolean mIsStatic; + // TODO use this to generate methods + private boolean mHasThrowable; + + private DexMaker mDexMaker; + private Member mMember; + private XposedBridge.AdditionalHookInfo mHookInfo; + private ClassLoader mAppClassLoader; + private Class mHookClass; + private Method mHookMethod; + private Method mBackupMethod; + private String mDexDirPath; + + private static TypeId[] getParameterTypeIds(Class[] parameterTypes, boolean isStatic) { + int parameterSize = parameterTypes.length; + int targetParameterSize = isStatic ? parameterSize : parameterSize + 1; + TypeId[] parameterTypeIds = new TypeId[targetParameterSize]; + int offset = 0; + if (!isStatic) { + parameterTypeIds[0] = TypeId.OBJECT; + offset = 1; + } + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypeIds[i + offset] = TypeId.get(parameterTypes[i]); + } + return parameterTypeIds; + } + + private static Class[] getParameterTypes(Class[] parameterTypes, boolean isStatic) { + if (isStatic) { + return parameterTypes; + } + int parameterSize = parameterTypes.length; + int targetParameterSize = parameterSize + 1; + Class[] newParameterTypes = new Class[targetParameterSize]; + int offset = 1; + newParameterTypes[0] = Object.class; + System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length); + return newParameterTypes; + } + + public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, + ClassLoader appClassLoader, String dexDirPath) throws Exception { + if (member instanceof Method) { + Method method = (Method) member; + mIsStatic = Modifier.isStatic(method.getModifiers()); + mReturnType = method.getReturnType(); + if (mReturnType.equals(Void.class) || mReturnType.equals(void.class) + || mReturnType.isPrimitive()) { + mReturnTypeId = TypeId.get(mReturnType); + } else { + // all others fallback to plain Object for convenience + mReturnType = Object.class; + mReturnTypeId = TypeId.OBJECT; + } + mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic); + mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic); + mHasThrowable = method.getExceptionTypes().length > 0; + } else if (member instanceof Constructor) { + Constructor constructor = (Constructor) member; + mIsStatic = false; + mReturnType = void.class; + mReturnTypeId = TypeId.VOID; + mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic); + mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic); + mHasThrowable = constructor.getExceptionTypes().length > 0; + } else if (member.getDeclaringClass().isInterface()) { + throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString()); + } else if (Modifier.isAbstract(member.getModifiers())) { + throw new IllegalArgumentException("Cannot hook abstract methods: " + member.toString()); + } else { + throw new IllegalArgumentException("Only methods and constructors can be hooked: " + member.toString()); + } + mMember = member; + mHookInfo = hookInfo; + mDexDirPath = dexDirPath; + if (appClassLoader == null + || appClassLoader.getClass().getName().equals("java.lang.BootClassLoader")) { + mAppClassLoader = this.getClass().getClassLoader(); + } else { + mAppClassLoader = appClassLoader; + } + + mDexMaker = new DexMaker(); + // Generate a Hooker class. + String className = getClassName(mMember); + String dexName = className + ".jar"; + + HookWrapper.HookEntity hookEntity = null; + //try load cache first + try { + ClassLoader loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName); + if (loader != null) { + hookEntity = loadHookerClass(loader, className); + } + } catch (Throwable throwable) {} + + //do generate + if (hookEntity == null) { + hookEntity = doMake(className, dexName); + } + SandHook.hook(hookEntity); + } + + private HookWrapper.HookEntity doMake(String className, String dexName) throws Exception { + mHookerTypeId = TypeId.get(CLASS_DESC_PREFIX + className + ";"); + mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT); + generateFields(); + generateHookMethod(); + generateBackupMethod(); + + ClassLoader loader; + if (TextUtils.isEmpty(mDexDirPath)) { + throw new IllegalArgumentException("dexDirPath should not be empty!!!"); + } + // Create the dex file and load it. + loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName); + return loadHookerClass(loader, className); + } + + private HookWrapper.HookEntity loadHookerClass(ClassLoader loader, String className) throws Exception { + mHookClass = loader.loadClass(className); + // Execute our newly-generated code in-process. + mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); + mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP); + setup(mHookClass); + return new HookWrapper.HookEntity(mMember, mHookMethod, mBackupMethod, false); + } + + private void setup(Class mHookClass) { + XposedHelpers.setStaticObjectField(mHookClass, FIELD_NAME_METHOD, mMember); + XposedHelpers.setStaticObjectField(mHookClass, FIELD_NAME_BACKUP_METHOD, mBackupMethod); + XposedHelpers.setStaticObjectField(mHookClass, FIELD_NAME_HOOK_INFO, mHookInfo); + } + + private String getClassName(Member originMethod) { + return CLASS_NAME_PREFIX + "_" + MD5(originMethod.toString()); + } + + public Method getHookMethod() { + return mHookMethod; + } + + public Method getBackupMethod() { + return mBackupMethod; + } + + public Method getCallBackupMethod() { + return mBackupMethod; + } + + public Class getHookClass() { + return mHookClass; + } + + private void generateFields() { + mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO); + mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD); + mBackupMethodFieldId = mHookerTypeId.getField(methodTypeId, FIELD_NAME_BACKUP_METHOD); + mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null); + mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null); + mDexMaker.declare(mBackupMethodFieldId, Modifier.STATIC, null); + } + + private void generateBackupMethod() { + mBackupMethodId = mHookerTypeId.getMethod(TypeId.VOID, METHOD_NAME_BACKUP); + Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + code.returnVoid(); + } + + private void generateHookMethod() { + mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds); + mSandHookBridgeMethodId = TypeId.get(HookStubManager.class).getMethod(TypeId.get(Object.class), "hookBridge", memberTypeId, TypeId.get(Object.class), TypeId.get(Object[].class)); + + Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC); + + Local method = code.newLocal(memberTypeId); + // Local backupMethod = code.newLocal(methodTypeId); + Local thisObject = code.newLocal(TypeId.OBJECT); + Local args = code.newLocal(objArrayTypeId); + Local actualParamSize = code.newLocal(TypeId.INT); + Local argIndex = code.newLocal(TypeId.INT); + Local resultObj = code.newLocal(TypeId.OBJECT); + + Local[] allArgsLocals = createParameterLocals(code); + Map resultLocals = createResultLocals(code); + + + code.sget(mMethodFieldId, method); + code.loadConstant(args, null); + code.loadConstant(argIndex, 0); +// code.sget(mBackupMethodFieldId, backupMethod); + int paramsSize = mParameterTypeIds.length; + int offset = 0; + // thisObject + if (mIsStatic) { + // thisObject = null + code.loadConstant(thisObject, null); + } else { + // thisObject = args[0] + offset = 1; + code.move(thisObject, allArgsLocals[0]); + } + + // actual args (exclude thisObject if this is not a static method) + code.loadConstant(actualParamSize, paramsSize - offset); + code.newArray(args, actualParamSize); + for (int i = offset; i < paramsSize; i++) { + Local parameter = allArgsLocals[i]; + // save parameter to resultObj as Object + autoBoxIfNecessary(code, resultObj, parameter); + code.loadConstant(argIndex, i - offset); + // save Object to args + code.aput(args, argIndex, resultObj); + } + + if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeStatic(mSandHookBridgeMethodId, null, method, thisObject, args); + code.returnVoid(); + } else { + code.invokeStatic(mSandHookBridgeMethodId, resultObj, method, thisObject, args); + TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId); + Local matchObjLocal = resultLocals.get(objTypeId); + code.cast(matchObjLocal, resultObj); + // have to use matching typed Object(Integer, Double ...) to do unboxing + Local toReturn = resultLocals.get(mReturnTypeId); + autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true); + code.returnValue(toReturn); + } + + } + + private Local[] createParameterLocals(Code code) { + Local[] paramLocals = new Local[mParameterTypeIds.length]; + for (int i = 0; i < mParameterTypeIds.length; i++) { + paramLocals[i] = code.getParameter(i, mParameterTypeIds[i]); + } + return paramLocals; + } +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/SandHookXposedBridge.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/SandHookXposedBridge.java new file mode 100644 index 00000000..22227e44 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/methodgen/SandHookXposedBridge.java @@ -0,0 +1,161 @@ +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; +import com.swift.sandhook.xposedcompat.XposedCompat; +import com.swift.sandhook.xposedcompat.hookstub.HookMethodEntity; +import com.swift.sandhook.xposedcompat.hookstub.HookStubManager; +import com.swift.sandhook.xposedcompat.utils.DexLog; +import com.swift.sandhook.xposedcompat.utils.FileUtils; + +import java.io.File; +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.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 hookedInfo = new HashMap<>(); + private static HookMaker hookMaker = XposedCompat.useNewDexMaker ? new HookerDexMakerNew() : new HookerDexMaker(); + private static final AtomicBoolean dexPathInited = new AtomicBoolean(false); + private static File dexDir; + + public static Map entityMap = new HashMap<>(); + + public static void onForkPost() { + dexPathInited.set(false); + } + + public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) { + + if (!checkMember(hookMethod)) { + return; + } + + if (hookedInfo.containsKey(hookMethod) || entityMap.containsKey(hookMethod)) { + DexLog.w("already hook method:" + hookMethod.toString()); + return; + } + + try { + setupDexCachePath(); + Trace.beginSection("SandHook-Xposed"); + long timeStart = System.currentTimeMillis(); + HookMethodEntity stub = null; + if (XposedCompat.useInternalStub) { + stub = HookStubManager.getHookMethodEntity(hookMethod); + } + if (stub != null) { + SandHook.hook(new HookWrapper.HookEntity(hookMethod, stub.hook, stub.backup, false)); + entityMap.put(hookMethod, stub); + } else { + hookMaker.start(hookMethod, additionalHookInfo, + null, 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")); + Trace.endSection(); + } catch (Exception e) { + DexLog.e("error occur when hook method <" + hookMethod.toString() + ">", e); + } + } + + private static void setupDexCachePath() { + // using file based DexClassLoader + if (!dexPathInited.compareAndSet(false, true)) { + 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(); + } 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) { + return true; + } else if (member instanceof Constructor) { + return true; + } else if (member.getDeclaringClass().isInterface()) { + DexLog.e("Cannot hook interfaces: " + member.toString()); + return false; + } else if (Modifier.isAbstract(member.getModifiers())) { + DexLog.e("Cannot hook abstract methods: " + member.toString()); + return false; + } else { + DexLog.e("Only methods and constructors can be hooked: " + member.toString()); + return false; + } + } + + 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); + } + } + + public static void setLibPath() { + if (Process.is64Bit()) { + SandHookConfig.libSandHookPath = "/system/lib64/libsandhook.edxp.so"; + } else { + SandHookConfig.libSandHookPath = "/system/lib/libsandhook.edxp.so"; + } + } +} + + diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ApplicationUtils.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ApplicationUtils.java new file mode 100644 index 00000000..205d7c1f --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ApplicationUtils.java @@ -0,0 +1,34 @@ +package com.swift.sandhook.xposedcompat.utils; + +import android.app.Application; + +import java.lang.reflect.Method; + +public class ApplicationUtils { + + private static Class classActivityThread; + private static Method currentApplicationMethod; + + static Application application; + + public static Application currentApplication() { + if (application != null) + return application; + if (currentApplicationMethod == null) { + try { + classActivityThread = Class.forName("android.app.ActivityThread"); + currentApplicationMethod = classActivityThread.getDeclaredMethod("currentApplication"); + } catch (Exception e) { + e.printStackTrace(); + } + } + if (currentApplicationMethod == null) + return null; + try { + application = (Application) currentApplicationMethod.invoke(null); + } catch (Exception e) { + } + return application; + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ClassLoaderUtils.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ClassLoaderUtils.java new file mode 100644 index 00000000..4acfa841 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ClassLoaderUtils.java @@ -0,0 +1,106 @@ +package com.swift.sandhook.xposedcompat.utils; + +import android.os.Build; +import android.util.ArrayMap; + +import com.elderdrivers.riru.edxp.sandhook.BuildConfig; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import dalvik.system.PathClassLoader; + +public class ClassLoaderUtils { + + public static final String DEXPATH = "/system/framework/edxposed.dex:/system/framework/eddalvikdx.dex:/system/framework/eddexmaker.dex"; + + public static void replaceParentClassLoader(ClassLoader appClassLoader) { + if (appClassLoader == null) { + DexLog.e("appClassLoader is null, you might be kidding me?"); + return; + } + try { + ClassLoader curCL = ClassLoaderUtils.class.getClassLoader(); + ClassLoader parent = appClassLoader; + ClassLoader lastChild = appClassLoader; + while (parent != null) { + ClassLoader tmp = parent.getParent(); + if (tmp == curCL) { + DexLog.d("replacing has been done before, skip."); + return; + } + if (tmp == null) { + DexLog.d("before replacing =========================================>"); + dumpClassLoaders(appClassLoader); + Field parentField = ClassLoader.class.getDeclaredField("parent"); + parentField.setAccessible(true); + parentField.set(curCL, parent); + parentField.set(lastChild, curCL); + DexLog.d("after replacing ==========================================>"); + dumpClassLoaders(appClassLoader); + } + lastChild = parent; + parent = tmp; + } + } catch (Throwable throwable) { + DexLog.e("error when replacing class loader.", throwable); + } + } + + private static void dumpClassLoaders(ClassLoader classLoader) { + if (BuildConfig.DEBUG) { + while (classLoader != null) { + DexLog.d(classLoader + " =>"); + classLoader = classLoader.getParent(); + } + } + } + + public static List getAppClassLoader() { + List cacheLoaders = new ArrayList<>(0); + try { + DexLog.d("start getting app classloader"); + Class appLoadersClass = Class.forName("android.app.ApplicationLoaders"); + Field loadersField = appLoadersClass.getDeclaredField("gApplicationLoaders"); + loadersField.setAccessible(true); + Object loaders = loadersField.get(null); + Field mLoaderMapField = loaders.getClass().getDeclaredField("mLoaders"); + mLoaderMapField.setAccessible(true); + ArrayMap mLoaderMap = (ArrayMap) mLoaderMapField.get(loaders); + DexLog.d("mLoaders size = " + mLoaderMap.size()); + cacheLoaders = new ArrayList<>(mLoaderMap.values()); + } catch (Exception ex) { + DexLog.e("error get app class loader.", ex); + } + return cacheLoaders; + } + + private static HashSet classLoaders = new HashSet<>(); + + public static boolean addPathToClassLoader(ClassLoader classLoader) { + if (!(classLoader instanceof PathClassLoader)) { + DexLog.w(classLoader + " is not a BaseDexClassLoader!!!"); + return false; + } + if (classLoaders.contains(classLoader)) { + DexLog.d(classLoader + " has been hooked before"); + return true; + } + try { + PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + //baseDexClassLoader.addDexPath(DEXPATH); + } else { + DexUtils.injectDexAtFirst(DEXPATH, baseDexClassLoader); + } + classLoaders.add(classLoader); + return true; + } catch (Throwable throwable) { + DexLog.e("error when addPath to ClassLoader: " + classLoader, throwable); + } + return false; + } + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexLog.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexLog.java new file mode 100644 index 00000000..cda8f193 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexLog.java @@ -0,0 +1,51 @@ +package com.swift.sandhook.xposedcompat.utils; + +import android.util.Log; + +import java.lang.reflect.Member; + + +public class DexLog { + + public static final String TAG = "SandXposed-dexmaker"; + + public static boolean DEBUG = true; + + 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) { + return Log.d(TAG, s); + } + + public static void printMethodHookIn(Member member) { + if (DEBUG && member != null) { + Log.d("SandHook-Xposed", "method <" + member.toString() + "> hook in"); + } + } + + public static void printCallOriginError(Member member) { + if (member != null) { + Log.e("SandHook-Xposed", "method <" + member.toString() + "> call origin error!"); + } + } + + 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); + } + + +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexMakerUtils.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexMakerUtils.java new file mode 100644 index 00000000..ad9142df --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexMakerUtils.java @@ -0,0 +1,224 @@ +package com.swift.sandhook.xposedcompat.utils; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +import external.com.android.dx.Code; +import external.com.android.dx.Local; +import external.com.android.dx.TypeId; + +public class DexMakerUtils { + + + private static volatile Method addInstMethod, specMethod; + + public static void autoBoxIfNecessary(Code code, Local target, Local source) { + String boxMethod = "valueOf"; + TypeId boxTypeId; + TypeId typeId = source.getType(); + if (typeId.equals(TypeId.BOOLEAN)) { + boxTypeId = TypeId.get(Boolean.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.BOOLEAN), target, source); + } else if (typeId.equals(TypeId.BYTE)) { + boxTypeId = TypeId.get(Byte.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.BYTE), target, source); + } else if (typeId.equals(TypeId.CHAR)) { + boxTypeId = TypeId.get(Character.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.CHAR), target, source); + } else if (typeId.equals(TypeId.DOUBLE)) { + boxTypeId = TypeId.get(Double.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.DOUBLE), target, source); + } else if (typeId.equals(TypeId.FLOAT)) { + boxTypeId = TypeId.get(Float.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.FLOAT), target, source); + } else if (typeId.equals(TypeId.INT)) { + boxTypeId = TypeId.get(Integer.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.INT), target, source); + } else if (typeId.equals(TypeId.LONG)) { + boxTypeId = TypeId.get(Long.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.LONG), target, source); + } else if (typeId.equals(TypeId.SHORT)) { + boxTypeId = TypeId.get(Short.class); + code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.SHORT), target, source); + } else if (typeId.equals(TypeId.VOID)) { + code.loadConstant(target, null); + } else { + code.move(target, source); + } + } + + public static void autoUnboxIfNecessary(Code code, Local target, Local source, + Map tmpLocals, boolean castObj) { + String unboxMethod; + TypeId typeId = target.getType(); + TypeId boxTypeId; + if (typeId.equals(TypeId.BOOLEAN)) { + unboxMethod = "booleanValue"; + boxTypeId = TypeId.get("Ljava/lang/Boolean;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.BOOLEAN, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.BYTE)) { + unboxMethod = "byteValue"; + boxTypeId = TypeId.get("Ljava/lang/Byte;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.BYTE, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.CHAR)) { + unboxMethod = "charValue"; + boxTypeId = TypeId.get("Ljava/lang/Character;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.CHAR, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.DOUBLE)) { + unboxMethod = "doubleValue"; + boxTypeId = TypeId.get("Ljava/lang/Double;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.DOUBLE, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.FLOAT)) { + unboxMethod = "floatValue"; + boxTypeId = TypeId.get("Ljava/lang/Float;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.FLOAT, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.INT)) { + unboxMethod = "intValue"; + boxTypeId = TypeId.get("Ljava/lang/Integer;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.INT, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.LONG)) { + unboxMethod = "longValue"; + boxTypeId = TypeId.get("Ljava/lang/Long;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.LONG, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.SHORT)) { + unboxMethod = "shortValue"; + boxTypeId = TypeId.get("Ljava/lang/Short;"); + Local boxTypedLocal = tmpLocals.get(boxTypeId); + code.cast(boxTypedLocal, source); + code.invokeVirtual(boxTypeId.getMethod(TypeId.SHORT, unboxMethod), target, boxTypedLocal); + } else if (typeId.equals(TypeId.VOID)) { + code.loadConstant(target, null); + } else if (castObj) { + code.cast(target, source); + } else { + code.move(target, source); + } + } + + public static Map createResultLocals(Code code) { + HashMap resultMap = new HashMap<>(); + Local booleanLocal = code.newLocal(TypeId.BOOLEAN); + Local byteLocal = code.newLocal(TypeId.BYTE); + Local charLocal = code.newLocal(TypeId.CHAR); + Local doubleLocal = code.newLocal(TypeId.DOUBLE); + Local floatLocal = code.newLocal(TypeId.FLOAT); + Local intLocal = code.newLocal(TypeId.INT); + Local longLocal = code.newLocal(TypeId.LONG); + Local shortLocal = code.newLocal(TypeId.SHORT); + Local voidLocal = code.newLocal(TypeId.VOID); + Local objectLocal = code.newLocal(TypeId.OBJECT); + + Local booleanObjLocal = code.newLocal(TypeId.get("Ljava/lang/Boolean;")); + Local byteObjLocal = code.newLocal(TypeId.get("Ljava/lang/Byte;")); + Local charObjLocal = code.newLocal(TypeId.get("Ljava/lang/Character;")); + Local doubleObjLocal = code.newLocal(TypeId.get("Ljava/lang/Double;")); + Local floatObjLocal = code.newLocal(TypeId.get("Ljava/lang/Float;")); + Local intObjLocal = code.newLocal(TypeId.get("Ljava/lang/Integer;")); + Local longObjLocal = code.newLocal(TypeId.get("Ljava/lang/Long;")); + Local shortObjLocal = code.newLocal(TypeId.get("Ljava/lang/Short;")); + Local voidObjLocal = code.newLocal(TypeId.get("Ljava/lang/Void;")); + + // backup need initialized locals + code.loadConstant(booleanLocal, false); + code.loadConstant(byteLocal, (byte) 0); + code.loadConstant(charLocal, '\0'); + code.loadConstant(doubleLocal,0.0); + code.loadConstant(floatLocal,0.0f); + code.loadConstant(intLocal, 0); + code.loadConstant(longLocal, 0L); + code.loadConstant(shortLocal, (short) 0); + code.loadConstant(voidLocal, null); + code.loadConstant(objectLocal, null); + // all to null + code.loadConstant(booleanObjLocal, null); + code.loadConstant(byteObjLocal, null); + code.loadConstant(charObjLocal, null); + code.loadConstant(doubleObjLocal, null); + code.loadConstant(floatObjLocal, null); + code.loadConstant(intObjLocal, null); + code.loadConstant(longObjLocal, null); + code.loadConstant(shortObjLocal, null); + code.loadConstant(voidObjLocal, null); + // package all + resultMap.put(TypeId.BOOLEAN, booleanLocal); + resultMap.put(TypeId.BYTE, byteLocal); + resultMap.put(TypeId.CHAR, charLocal); + resultMap.put(TypeId.DOUBLE, doubleLocal); + resultMap.put(TypeId.FLOAT, floatLocal); + resultMap.put(TypeId.INT, intLocal); + resultMap.put(TypeId.LONG, longLocal); + resultMap.put(TypeId.SHORT, shortLocal); + resultMap.put(TypeId.VOID, voidLocal); + resultMap.put(TypeId.OBJECT, objectLocal); + + resultMap.put(TypeId.get("Ljava/lang/Boolean;"), booleanObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Byte;"), byteObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Character;"), charObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Double;"), doubleObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Float;"), floatObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Integer;"), intObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Long;"), longObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Short;"), shortObjLocal); + resultMap.put(TypeId.get("Ljava/lang/Void;"), voidObjLocal); + + return resultMap; + } + + public static TypeId getObjTypeIdIfPrimitive(TypeId typeId) { + if (typeId.equals(TypeId.BOOLEAN)) { + return TypeId.get("Ljava/lang/Boolean;"); + } else if (typeId.equals(TypeId.BYTE)) { + return TypeId.get("Ljava/lang/Byte;"); + } else if (typeId.equals(TypeId.CHAR)) { + return TypeId.get("Ljava/lang/Character;"); + } else if (typeId.equals(TypeId.DOUBLE)) { + return TypeId.get("Ljava/lang/Double;"); + } else if (typeId.equals(TypeId.FLOAT)) { + return TypeId.get("Ljava/lang/Float;"); + } else if (typeId.equals(TypeId.INT)) { + return TypeId.get("Ljava/lang/Integer;"); + } else if (typeId.equals(TypeId.LONG)) { + return TypeId.get("Ljava/lang/Long;"); + } else if (typeId.equals(TypeId.SHORT)) { + return TypeId.get("Ljava/lang/Short;"); + } else if (typeId.equals(TypeId.VOID)) { + return TypeId.get("Ljava/lang/Void;"); + } else { + return typeId; + } + } + + public static void returnRightValue(Code code, Class returnType, Map resultLocals) { + String unboxMethod; + TypeId boxTypeId; + code.returnValue(resultLocals.get(returnType)); + } + + public static String MD5(String source) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + messageDigest.update(source.getBytes()); + return new BigInteger(1, messageDigest.digest()).toString(32); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return source; + } +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexUtils.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexUtils.java new file mode 100644 index 00000000..55bd02f4 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/DexUtils.java @@ -0,0 +1,66 @@ +package com.swift.sandhook.xposedcompat.utils; + +import android.annotation.TargetApi; +import android.os.Build; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; + +import dalvik.system.BaseDexClassLoader; +import dalvik.system.DexClassLoader; + +/** + * For 6.0 only. + */ +@TargetApi(Build.VERSION_CODES.M) +public class DexUtils { + + public static void injectDexAtFirst(String dexPath, BaseDexClassLoader classLoader) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + DexClassLoader dexClassLoader = new DexClassLoader(dexPath, null, dexPath, classLoader); + Object baseDexElements = getDexElements(getPathList(classLoader)); + Object newDexElements = getDexElements(getPathList(dexClassLoader)); + Object allDexElements = combineArray(newDexElements, baseDexElements); + Object pathList = getPathList(classLoader); + setField(pathList, pathList.getClass(), "dexElements", allDexElements); + } + + private static Object getDexElements(Object paramObject) + throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException { + return getField(paramObject, paramObject.getClass(), "dexElements"); + } + + private static Object getPathList(Object baseDexClassLoader) + throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList"); + } + + private static Object combineArray(Object firstArray, Object secondArray) { + Class localClass = firstArray.getClass().getComponentType(); + int firstArrayLength = Array.getLength(firstArray); + int allLength = firstArrayLength + Array.getLength(secondArray); + Object result = Array.newInstance(localClass, allLength); + for (int k = 0; k < allLength; ++k) { + if (k < firstArrayLength) { + Array.set(result, k, Array.get(firstArray, k)); + } else { + Array.set(result, k, Array.get(secondArray, k - firstArrayLength)); + } + } + return result; + } + + public static Object getField(Object obj, Class cl, String field) + throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + Field localField = cl.getDeclaredField(field); + localField.setAccessible(true); + return localField.get(obj); + } + + public static void setField(Object obj, Class cl, String field, Object value) + throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + Field localField = cl.getDeclaredField(field); + localField.setAccessible(true); + localField.set(obj, value); + } + +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/FileUtils.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/FileUtils.java new file mode 100644 index 00000000..c3c7acc1 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/FileUtils.java @@ -0,0 +1,77 @@ +package com.swift.sandhook.xposedcompat.utils; + +import android.os.Build; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +public class FileUtils { + + public static final boolean IS_USING_PROTECTED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + + /** + * Delete a file or a directory and its children. + * + * @param file The directory to delete. + * @throws IOException Exception when problem occurs during deleting the directory. + */ + public static void delete(File file) throws IOException { + + for (File childFile : file.listFiles()) { + + if (childFile.isDirectory()) { + delete(childFile); + } else { + if (!childFile.delete()) { + throw new IOException(); + } + } + } + + if (!file.delete()) { + throw new IOException(); + } + } + + public static String readLine(File file) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + return reader.readLine(); + } catch (Throwable throwable) { + return ""; + } + } + + public static void writeLine(File file, String line) { + try { + file.createNewFile(); + } catch (IOException ex) { + } + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + writer.write(line); + writer.flush(); + } catch (Throwable throwable) { + DexLog.e("error writing line to file " + file + ": " + throwable.getMessage()); + } + } + + public static String getPackageName(String dataDir) { + if (TextUtils.isEmpty(dataDir)) { + DexLog.e("getPackageName using empty dataDir"); + return ""; + } + int lastIndex = dataDir.lastIndexOf("/"); + if (lastIndex < 0) { + return dataDir; + } + return dataDir.substring(lastIndex + 1); + } + + public static String getDataPathPrefix() { + return IS_USING_PROTECTED_STORAGE ? "/data/user_de/0/" : "/data/data/"; + } +} diff --git a/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ProcessUtils.java b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ProcessUtils.java new file mode 100644 index 00000000..2adcf4f3 --- /dev/null +++ b/edxp-sandhook/src/main/java/com/swift/sandhook/xposedcompat/utils/ProcessUtils.java @@ -0,0 +1,64 @@ +package com.swift.sandhook.xposedcompat.utils; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by swift_gan on 2017/11/23. + */ + +public class ProcessUtils { + + private static volatile String processName = null; + + public static String getProcessName(Context context) { + if (!TextUtils.isEmpty(processName)) + return processName; + processName = doGetProcessName(context); + return processName; + } + + private static String doGetProcessName(Context context) { + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List runningApps = am.getRunningAppProcesses(); + if (runningApps == null) { + return null; + } + for (ActivityManager.RunningAppProcessInfo proInfo : runningApps) { + if (proInfo.pid == android.os.Process.myPid()) { + if (proInfo.processName != null) { + return proInfo.processName; + } + } + } + return context.getPackageName(); + } + + public static boolean isMainProcess(Context context) { + String processName = getProcessName(context); + String pkgName = context.getPackageName(); + if (!TextUtils.isEmpty(processName) && !TextUtils.equals(processName, pkgName)) { + return false; + } else { + return true; + } + } + + public static List findActivitiesForPackage(Context context, String packageName) { + final PackageManager packageManager = context.getPackageManager(); + + final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + mainIntent.setPackage(packageName); + + final List apps = packageManager.queryIntentActivities(mainIntent, 0); + return apps != null ? apps : new ArrayList(); + } +} diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/Main.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java similarity index 99% rename from edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/Main.java rename to edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java index f855d593..d6bb5997 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/Main.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -1,4 +1,4 @@ -package com.elderdrivers.riru.edxp.yahfa; +package com.elderdrivers.riru.edxp; import android.annotation.SuppressLint; import android.os.Build; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java index 3a27654f..352f0c05 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java @@ -2,7 +2,7 @@ package com.elderdrivers.riru.edxp.yahfa.config; import com.elderdrivers.riru.edxp.config.EdXpConfig; import com.elderdrivers.riru.edxp.config.InstallerChooser; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.yahfa.entry.hooker.XposedBlackListHooker; public class YahfaEdxpConfig implements EdXpConfig { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java index c573f441..2f0709ae 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java @@ -1,11 +1,10 @@ package com.elderdrivers.riru.edxp.yahfa.config; import com.elderdrivers.riru.edxp.hook.HookProvider; -import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter; import com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils; import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge; +import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import de.robv.android.xposed.XposedBridge; @@ -17,7 +16,7 @@ public class YahfaHookProvider implements HookProvider { } @Override - public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable { return DynamicBridge.invokeOriginalMethod(method, thisObject, args); } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java index bbf7d68a..13da190a 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java @@ -1,6 +1,6 @@ package com.elderdrivers.riru.edxp.yahfa.core; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.yahfa.entry.hooker.OnePlusWorkAroundHooker; import com.elderdrivers.riru.edxp.util.Utils; @@ -14,8 +14,8 @@ import java.util.Set; import de.robv.android.xposed.XposedHelpers; -import static com.elderdrivers.riru.edxp.yahfa.Main.backupAndHookNative; -import static com.elderdrivers.riru.edxp.yahfa.Main.findMethodNative; +import static com.elderdrivers.riru.edxp.Main.backupAndHookNative; +import static com.elderdrivers.riru.edxp.Main.findMethodNative; public class HookMain { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java index 9b2d496b..d8f616b5 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java @@ -2,7 +2,7 @@ package com.elderdrivers.riru.edxp.yahfa.core; import android.os.Build; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.util.Utils; import java.lang.reflect.Field; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java index 5b56caf3..7ca85d47 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java @@ -3,9 +3,8 @@ package com.elderdrivers.riru.edxp.yahfa.dexmaker; import android.app.AndroidAppHelper; import android.os.Build; import android.text.TextUtils; -import android.util.Log; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.yahfa.core.HookMain; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java index a6307f7b..b998a29e 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java @@ -1,6 +1,6 @@ package com.elderdrivers.riru.edxp.yahfa.dexmaker; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import java.io.File; import java.lang.reflect.Constructor; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java index 3b281da5..56695e55 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java @@ -4,7 +4,7 @@ import android.annotation.TargetApi; import android.os.Build; import android.text.TextUtils; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.yahfa.core.HookMain; import java.io.File; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/HandleBindAppHooker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/HandleBindAppHooker.java index 2d3050e7..12d7ef2c 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/HandleBindAppHooker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/HandleBindAppHooker.java @@ -8,7 +8,7 @@ import android.content.res.CompatibilityInfo; import com.elderdrivers.riru.common.KeepMembers; import com.elderdrivers.riru.edxp.util.Utils; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import de.robv.android.xposed.XposedBridge; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/OnePlusWorkAroundHooker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/OnePlusWorkAroundHooker.java index 87cda6b2..f3261f9d 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/OnePlusWorkAroundHooker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/OnePlusWorkAroundHooker.java @@ -1,7 +1,7 @@ package com.elderdrivers.riru.edxp.yahfa.entry.hooker; import com.elderdrivers.riru.common.KeepMembers; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import de.robv.android.xposed.XposedBridge; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java index dd59ee82..ba9842d9 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java @@ -2,7 +2,7 @@ package com.elderdrivers.riru.edxp.yahfa.proxy; import android.text.TextUtils; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter; @@ -11,7 +11,7 @@ import com.elderdrivers.riru.edxp.util.Utils; import de.robv.android.xposed.XposedBridge; -import static com.elderdrivers.riru.edxp.yahfa.Main.isAppNeedHook; +import static com.elderdrivers.riru.edxp.Main.isAppNeedHook; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; /** diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java index 0df49cd6..9c1d3dba 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java @@ -1,6 +1,6 @@ package com.elderdrivers.riru.edxp.yahfa.proxy; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter; import com.elderdrivers.riru.edxp.yahfa.entry.Router; diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java index 86041628..ecd8813d 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java @@ -1,7 +1,7 @@ package com.elderdrivers.riru.edxp.yahfa.util; import com.elderdrivers.riru.edxp.util.Utils; -import com.elderdrivers.riru.edxp.yahfa.Main; +import com.elderdrivers.riru.edxp.Main; import java.util.Arrays; diff --git a/settings.gradle b/settings.gradle index 2b7924b5..c96662e1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-yahfa', ':edxp-common' \ No newline at end of file +include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook' \ No newline at end of file diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java index f2f553f0..671bd828 100644 --- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java +++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java @@ -9,7 +9,7 @@ public interface HookProvider { void hookMethod(Member method, XposedBridge.AdditionalHookInfo additionalInfo); - Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; + Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable; Member findMethodNative(Member hookMethod); diff --git a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java index 25abdc04..92d9fc2b 100644 --- a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -60,7 +60,7 @@ public final class XposedBridge { private static final Object[] EMPTY_ARRAY = new Object[0]; // built-in handlers - private static final Map> sHookedMethodCallbacks = new HashMap<>(); + public static final Map> sHookedMethodCallbacks = new HashMap<>(); public static final CopyOnWriteSortedSet sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>(); /*package*/ static final CopyOnWriteSortedSet sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>(); @@ -402,7 +402,7 @@ public final class XposedBridge { Class[] parameterTypes, Class returnType, Object thisObject, Object[] args) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + throws Throwable { return EdXpConfigGlobal.getHookProvider().invokeOriginalMethod(method, thisObject, args); } @@ -432,7 +432,7 @@ public final class XposedBridge { * if an exception was thrown by the invoked method */ public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) - throws NullPointerException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + throws Throwable { if (args == null) { args = EMPTY_ARRAY; }