Introduce Whale hook for Android

This commit is contained in:
solohsu 2019-03-20 10:29:51 +08:00
parent e858ebd70b
commit d0b1d7c52b
43 changed files with 1956 additions and 14 deletions

View File

@ -69,6 +69,27 @@ afterEvaluate {
}
pushTask.dependsOn(zipTask)
zipTask = task("zipWhale${nameCapped}", type: Exec, dependsOn: ":edxp-whale:makeAndCopy${nameCapped}") {
workingDir '..'
commandLine 'sh', 'build.sh', \
project.name, \
"Whale-${project.version}-${nameLowered}", \
"${project.extensions['module_name']}"
}
pushTask = task("pushWhale${nameCapped}", type: Exec) {
workingDir 'release'
def commands = ["adb", "push", "magisk-${project.extensions['module_name']}-Whale" +
"-${project.version}-${nameLowered}.zip", "/sdcard/"]
if (OperatingSystem.current().isWindows()) {
commandLine 'cmd', '/c', commands.join(" ")
} else {
commandLine commands
}
}
pushTask.dependsOn(zipTask)
}
}

View File

@ -5,10 +5,10 @@
#if defined(__LP64__)
static constexpr const char *kLibArtPath = "/system/lib64/libart.so";
static constexpr const char *kLibWhalePath = "/system/lib64/libwhale.so";
static constexpr const char *kLibWhalePath = "/system/lib64/libwhale.edxp.so";
#else
static constexpr const char *kLibArtPath = "/system/lib/libart.so";
static constexpr const char *kLibWhalePath = "/system/lib/libwhale.so";
static constexpr const char *kLibWhalePath = "/system/lib/libwhale.edxp.so";
#endif
#define XHOOK_REGISTER(NAME) \

View File

@ -1 +1,2 @@
libsandhook.edxp.so
libwhale.edxp.so

View File

@ -2,12 +2,10 @@ 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;
@ -19,7 +17,7 @@ public class SandHookProvider implements HookProvider {
}
@Override
public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable {
public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable {
return SandHookXposedBridge.invokeOriginalMethod(method, thisObject, args);
}
@ -32,4 +30,9 @@ public class SandHookProvider implements HookProvider {
public void deoptMethods(String packageName, ClassLoader classLoader) {
PrebuiltMethodsDeopter.deoptMethods(packageName, classLoader);
}
@Override
public long getMethodId(Member member) {
return 0;
}
}

1
edxp-whale/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

61
edxp-whale/build.gradle Normal file
View File

@ -0,0 +1,61 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.elderdrivers.riru.edxp.whale"
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')
}
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"
}
}
}

Binary file not shown.

33
edxp-whale/proguard-rules.pro vendored Normal file
View File

@ -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.lody.** {*;}

View File

@ -0,0 +1 @@
<manifest package="com.elderdrivers.riru.edxp.whale" />

View File

@ -0,0 +1,146 @@
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.BuildConfig;
import com.elderdrivers.riru.edxp.config.InstallerChooser;
import com.elderdrivers.riru.edxp.yahfa.core.HookMethodResolver;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import com.elderdrivers.riru.edxp.yahfa.proxy.BlackWhiteListProxy;
import com.elderdrivers.riru.edxp.yahfa.proxy.NormalProxy;
import com.elderdrivers.riru.edxp.util.Utils;
import com.lody.whale.WhaleRuntime;
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());
try {
WhaleRuntime.getMethodSlot(null);
} catch (Throwable throwable) {}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// 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);
}

View File

@ -0,0 +1,23 @@
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.Main;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.XposedBlackListHooker;
public class WhaleEdxpConfig 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();
}
}

View File

@ -0,0 +1,37 @@
package com.elderdrivers.riru.edxp.yahfa.config;
import com.elderdrivers.riru.edxp.hook.HookProvider;
import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter;
import com.lody.whale.WhaleRuntime;
import java.lang.reflect.Member;
import de.robv.android.xposed.XposedBridge;
public class WhaleHookProvider implements HookProvider {
@Override
public void hookMethod(Member method, XposedBridge.AdditionalHookInfo additionalInfo) {
WhaleRuntime.hookMethodNative(method.getDeclaringClass(), method, additionalInfo);
}
@Override
public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable {
return WhaleRuntime.invokeOriginalMethodNative(methodId, thisObject, args);
}
@Override
public Member findMethodNative(Member hookMethod) {
return hookMethod;
}
@Override
public void deoptMethods(String packageName, ClassLoader classLoader) {
PrebuiltMethodsDeopter.deoptMethods(packageName, classLoader);
}
@Override
public long getMethodId(Member member) {
return WhaleRuntime.getMethodSlot(member);
}
}

View File

@ -0,0 +1,186 @@
package com.elderdrivers.riru.edxp.yahfa.core;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.util.Utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import de.robv.android.xposed.XposedHelpers;
import static com.elderdrivers.riru.edxp.Main.backupAndHookNative;
import static com.elderdrivers.riru.edxp.Main.findMethodNative;
public class HookMain {
private static Set<String> hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName());
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) {
try {
Class<?> hookInfoClass = Class.forName(hookInfoClassName, true, patchClassLoader);
String[] hookItemNames = (String[]) hookInfoClass.getField("hookItemNames").get(null);
for (String hookItemName : hookItemNames) {
doHookItemDefault(patchClassLoader, hookItemName, originClassLoader);
}
} catch (Throwable e) {
Utils.logE("error when hooking all in: " + hookInfoClassName, e);
}
}
private static void doHookItemDefault(ClassLoader patchClassLoader, String hookItemName, ClassLoader originClassLoader) {
try {
Utils.logD("Start hooking with item " + hookItemName);
Class<?> hookItem = Class.forName(hookItemName, true, patchClassLoader);
String className = (String) hookItem.getField("className").get(null);
String methodName = (String) hookItem.getField("methodName").get(null);
String methodSig = (String) hookItem.getField("methodSig").get(null);
if (className == null || className.equals("")) {
Utils.logW("No target class. Skipping...");
return;
}
Class<?> clazz = null;
try {
clazz = Class.forName(className, true, originClassLoader);
} catch (ClassNotFoundException cnfe) {
Utils.logE(className + " not found in " + originClassLoader);
return;
}
if (Modifier.isAbstract(clazz.getModifiers())) {
Utils.logW("Hook may fail for abstract class: " + className);
}
Method hook = null;
Method backup = null;
for (Method method : hookItem.getDeclaredMethods()) {
if (method.getName().equals("hook") && Modifier.isStatic(method.getModifiers())) {
hook = method;
} else if (method.getName().equals("backup") && Modifier.isStatic(method.getModifiers())) {
backup = method;
}
}
if (hook == null) {
Utils.logE("Cannot find hook for " + methodName);
return;
}
findAndBackupAndHook(clazz, methodName, methodSig, hook, backup);
} catch (Throwable e) {
if (!hookItemWhiteList.contains(hookItemName)) {
Utils.logE("error when hooking " + hookItemName, e);
}
}
}
public static void findAndHook(Class targetClass, String methodName, String methodSig, Method hook) {
hook(findMethod(targetClass, methodName, methodSig), hook);
}
public static void findAndBackupAndHook(Class targetClass, String methodName, String methodSig,
Method hook, Method backup) {
backupAndHook(findMethod(targetClass, methodName, methodSig), hook, backup);
}
public static void hook(Object target, Method hook) {
backupAndHook(target, hook, null);
}
public static void backupAndHook(Object target, Method hook, Method backup) {
Utils.logD(String.format("target=%s, hook=%s, backup=%s", target, hook, backup));
if (target == null) {
throw new IllegalArgumentException("null target method");
}
if (hook == null) {
throw new IllegalArgumentException("null hook method");
}
if (!Modifier.isStatic(hook.getModifiers())) {
throw new IllegalArgumentException("Hook must be a static method: " + hook);
}
checkCompatibleMethods(target, hook, "Original", "Hook");
if (backup != null) {
if (!Modifier.isStatic(backup.getModifiers())) {
throw new IllegalArgumentException("Backup must be a static method: " + backup);
}
// backup is just a placeholder and the constraint could be less strict
checkCompatibleMethods(target, backup, "Original", "Backup");
}
if (backup != null) {
HookMethodResolver.resolveMethod(hook, backup);
}
// make sure GC completed before hook
Thread currentThread = Thread.currentThread();
int lastGcType = 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<Class<?>> originalParams;
if (original instanceof Method) {
originalParams = new ArrayList<>(Arrays.asList(((Method) original).getParameterTypes()));
} else if (original instanceof Constructor) {
originalParams = new ArrayList<>(Arrays.asList(((Constructor<?>) original).getParameterTypes()));
} else {
throw new IllegalArgumentException("Type of target method is wrong");
}
ArrayList<Class<?>> replacementParams = new ArrayList<>(Arrays.asList(replacement.getParameterTypes()));
if (original instanceof Method
&& !Modifier.isStatic(((Method) original).getModifiers())) {
originalParams.add(0, ((Method) original).getDeclaringClass());
} else if (original instanceof Constructor) {
originalParams.add(0, ((Constructor<?>) original).getDeclaringClass());
}
if (!Modifier.isStatic(replacement.getModifiers())) {
replacementParams.add(0, replacement.getDeclaringClass());
}
if (original instanceof Method
&& !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) {
throw new IllegalArgumentException("Incompatible return types. " + originalName + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType());
} else if (original instanceof Constructor) {
if (replacement.getReturnType().equals(Void.class)) {
throw new IllegalArgumentException("Incompatible return types. " + "<init>" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType());
}
}
if (originalParams.size() != replacementParams.size()) {
throw new IllegalArgumentException("Number of arguments don't match. " + originalName + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size());
}
for (int i = 0; i < originalParams.size(); i++) {
if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) {
throw new IllegalArgumentException("Incompatible argument #" + i + ": " + originalName + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i));
}
}
}
}

View File

@ -0,0 +1,155 @@
package com.elderdrivers.riru.edxp.yahfa.core;
import android.os.Build;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.util.Utils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* create by Swift Gan on 14/01/2019
* To ensure method in resolved cache
*/
public class HookMethodResolver {
public static Class artMethodClass;
public static Field resolvedMethodsField;
public static Field dexCacheField;
public static Field dexMethodIndexField;
public static Field artMethodField;
public static boolean canResolvedInJava = false;
public static boolean isArtMethod = false;
public static long resolvedMethodsAddress = 0;
public static int dexMethodIndex = 0;
public static Method testMethod;
public static Object testArtMethod;
public static void init() {
checkSupport();
}
private static void checkSupport() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
isArtMethod = false;
canResolvedInJava = false;
return;
}
testMethod = HookMethodResolver.class.getDeclaredMethod("init");
artMethodField = getField(Method.class, "artMethod");
testArtMethod = artMethodField.get(testMethod);
if (hasJavaArtMethod() && testArtMethod.getClass() == artMethodClass) {
checkSupportForArtMethod();
isArtMethod = true;
} else if (testArtMethod instanceof Long) {
checkSupportForArtMethodId();
isArtMethod = false;
} else {
canResolvedInJava = false;
}
} catch (Throwable throwable) {
Utils.logE("error when checkSupport", throwable);
}
}
// may 5.0
private static void checkSupportForArtMethod() throws Exception {
dexMethodIndexField = getField(artMethodClass, "dexMethodIndex");
dexCacheField = getField(Class.class, "dexCache");
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
if (resolvedMethodsField.get(dexCache) instanceof Object[]) {
canResolvedInJava = true;
}
}
// may 6.0
private static void checkSupportForArtMethodId() throws Exception {
dexMethodIndexField = getField(Method.class, "dexMethodIndex");
dexMethodIndex = (int) dexMethodIndexField.get(testMethod);
dexCacheField = getField(Class.class, "dexCache");
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
Object resolvedMethods = resolvedMethodsField.get(dexCache);
if (resolvedMethods instanceof Long) {
canResolvedInJava = false;
resolvedMethodsAddress = (long) resolvedMethods;
} else if (resolvedMethods instanceof long[]) {
canResolvedInJava = true;
}
}
public static void resolveMethod(Method hook, Method backup) {
if (canResolvedInJava && artMethodField != null) {
// in java
try {
resolveInJava(hook, backup);
} catch (Exception e) {
// in native
resolveInNative(hook, backup);
}
} else {
// in native
resolveInNative(hook, backup);
}
}
private static void resolveInJava(Method hook, Method backup) throws Exception {
Object dexCache = dexCacheField.get(hook.getDeclaringClass());
if (isArtMethod) {
Object artMethod = artMethodField.get(backup);
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
Object resolvedMethods = resolvedMethodsField.get(dexCache);
((Object[])resolvedMethods)[dexMethodIndex] = artMethod;
} else {
int dexMethodIndex = (int) dexMethodIndexField.get(backup);
Object resolvedMethods = resolvedMethodsField.get(dexCache);
long artMethod = (long) artMethodField.get(backup);
((long[])resolvedMethods)[dexMethodIndex] = artMethod;
}
}
private static void resolveInNative(Method hook, Method backup) {
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;
}
}
}

View File

@ -0,0 +1,37 @@
package com.elderdrivers.riru.edxp.yahfa.dexmaker;
import android.util.Log;
import com.elderdrivers.riru.edxp.BuildConfig;
public class DexLog {
public static final String TAG = "EdXposed-dexmaker";
public static int v(String s) {
return Log.v(TAG, s);
}
public static int i(String s) {
return Log.i(TAG, s);
}
public static int d(String s) {
if (BuildConfig.DEBUG) {
return Log.d(TAG, s);
}
return 0;
}
public static int w(String s) {
return Log.w(TAG, s);
}
public static int e(String s) {
return Log.e(TAG, s);
}
public static int e(String s, Throwable t) {
return Log.e(TAG, s, t);
}
}

View File

@ -0,0 +1,94 @@
package com.elderdrivers.riru.edxp.yahfa.dexmaker;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
public class MethodInfo {
public String className;
public String classDesc;
public String methodName;
public String methodSig;
public Method method;
public Constructor constructor;
public boolean isConstructor;
public ClassLoader classLoader;
public MethodInfo(Member member) {
if (member instanceof Method) {
method = (Method) member;
isConstructor = false;
classLoader = member.getDeclaringClass().getClassLoader();
generateMethodInfo();
} else if (member instanceof Constructor) {
constructor = (Constructor) member;
isConstructor = true;
classLoader = member.getDeclaringClass().getClassLoader();
generateConstructorInfo();
} else {
throw new IllegalArgumentException("member should be Method or Constructor");
}
}
private void generateConstructorInfo() {
methodName = "<init>";
className = constructor.getDeclaringClass().getName();
generateCommonInfo(constructor.getParameterTypes(), void.class);
}
private void generateMethodInfo() {
methodName = method.getName();
className = method.getDeclaringClass().getName();
generateCommonInfo(method.getParameterTypes(), method.getReturnType());
}
private void generateCommonInfo(Class[] parameterTypes, Class returnType) {
classDesc = "L" + className.replace(".", "/") + ";";
StringBuilder builder = new StringBuilder();
builder.append("(");
for (Class parameterType : parameterTypes) {
builder.append(getDescStr(parameterType));
}
builder.append(")");
builder.append(getDescStr(returnType));
methodSig = builder.toString();
}
public Class getClassForSure() {
try {
// TODO does initialize make sense?
return Class.forName(className, true, classLoader);
} catch (Throwable throwable) {
DexLog.e("error when getClassForSure", throwable);
return null;
}
}
public static String getDescStr(Class clazz) {
if (clazz.equals(boolean.class)) {
return "Z";
} else if (clazz.equals(byte.class)) {
return "B";
} else if (clazz.equals(char.class)) {
return "C";
} else if (clazz.equals(double.class)) {
return "D";
} else if (clazz.equals(float.class)) {
return "F";
} else if (clazz.equals(int.class)) {
return "I";
} else if (clazz.equals(long.class)) {
return "J";
} else if (clazz.equals(short.class)) {
return "S";
} else if (clazz.equals(void.class)) {
return "V";
} else {
String prefix = clazz.isArray() ? "" : "L";
String suffix = clazz.isArray() ? "" : ";";
return prefix + clazz.getName().replace(".", "/") + suffix;
}
}
}

View File

@ -0,0 +1,122 @@
package com.elderdrivers.riru.edxp.yahfa.entry;
import android.app.AndroidAppHelper;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
import com.elderdrivers.riru.edxp.util.Utils;
import com.elderdrivers.riru.edxp.yahfa.config.WhaleHookProvider;
import com.elderdrivers.riru.edxp.yahfa.config.WhaleEdxpConfig;
import com.elderdrivers.riru.edxp.yahfa.core.HookMain;
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.AppBootstrapHookInfo;
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.SysBootstrapHookInfo;
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.SysInnerHookInfo;
import com.elderdrivers.riru.edxp.yahfa.entry.bootstrap.WorkAroundHookInfo;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.SystemMainHooker;
import java.util.concurrent.atomic.AtomicBoolean;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedInit;
public class Router {
public volatile static boolean forkCompleted = false;
private static volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
public static void 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;
}
public static void logD(String prefix) {
Utils.logD(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
AndroidAppHelper.currentProcessName()));
}
public static void logE(String prefix, Throwable throwable) {
Utils.logE(String.format("%s: pkg=%s, prc=%s", prefix, AndroidAppHelper.currentPackageName(),
AndroidAppHelper.currentProcessName()), throwable);
}
public static void injectConfig() {
EdXpConfigGlobal.sConfig = new WhaleEdxpConfig();
EdXpConfigGlobal.sHookProvider = new WhaleHookProvider();
}
}

View File

@ -0,0 +1,14 @@
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.OnePlusWorkAroundHooker;
public class AppBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {
HandleBindAppHooker.class.getName(),
LoadedApkConstructorHooker.class.getName(),
OnePlusWorkAroundHooker.class.getName()
};
}

View File

@ -0,0 +1,16 @@
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.HandleBindAppHooker;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.LoadedApkConstructorHooker;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.OnePlusWorkAroundHooker;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.SystemMainHooker;
public class SysBootstrapHookInfo implements KeepMembers {
public static String[] hookItemNames = {
HandleBindAppHooker.class.getName(),
SystemMainHooker.class.getName(),
LoadedApkConstructorHooker.class.getName(),
OnePlusWorkAroundHooker.class.getName()
};
}

View File

@ -0,0 +1,10 @@
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.StartBootstrapServicesHooker;
public class SysInnerHookInfo implements KeepMembers {
public static String[] hookItemNames = {
StartBootstrapServicesHooker.class.getName()
};
}

View File

@ -0,0 +1,10 @@
package com.elderdrivers.riru.edxp.yahfa.entry.bootstrap;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.entry.hooker.OnePlusWorkAroundHooker;
public class WorkAroundHookInfo implements KeepMembers {
public static String[] hookItemNames = {
OnePlusWorkAroundHooker.class.getName()
};
}

View File

@ -0,0 +1,89 @@
package com.elderdrivers.riru.edxp.yahfa.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.yahfa.entry.Router;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import static com.elderdrivers.riru.edxp.config.InstallerChooser.INSTALLER_PACKAGE_NAME;
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
import static com.elderdrivers.riru.edxp.yahfa.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) {
}
}

View File

@ -0,0 +1,98 @@
package com.elderdrivers.riru.edxp.yahfa.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.yahfa.entry.Router;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
// when a package is loaded for an existing process, trigger the callbacks as well
// ed: remove resources related hooking
public class LoadedApkConstructorHooker implements KeepMembers {
public static String className = "android.app.LoadedApk";
public static String methodName = "<init>";
public static String methodSig = "(Landroid/app/ActivityThread;" +
"Landroid/content/pm/ApplicationInfo;" +
"Landroid/content/res/CompatibilityInfo;" +
"Ljava/lang/ClassLoader;ZZZ)V";
public static void hook(Object thiz, ActivityThread activityThread,
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation,
boolean includeCode, boolean registerPackage) {
if (XposedBlackListHooker.shouldDisableHooks("")) {
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
includeCode, registerPackage);
return;
}
Router.logD("LoadedApk#<init> starts");
backup(thiz, activityThread, aInfo, compatInfo, baseLoader, securityViolation,
includeCode, registerPackage);
try {
LoadedApk loadedApk = (LoadedApk) thiz;
String packageName = loadedApk.getPackageName();
Object mAppDir = XposedHelpers.getObjectField(thiz, "mAppDir");
Router.logD("LoadedApk#<init> ends: " + mAppDir);
if (XposedBlackListHooker.shouldDisableHooks(packageName)) {
return;
}
if (packageName.equals("android")) {
Router.logD("LoadedApk#<init> is android, skip: " + mAppDir);
return;
}
// mIncludeCode checking should go ahead of loadedPackagesInProcess added checking
if (!XposedHelpers.getBooleanField(loadedApk, "mIncludeCode")) {
Router.logD("LoadedApk#<init> mIncludeCode == false: " + mAppDir);
return;
}
if (!XposedInit.loadedPackagesInProcess.add(packageName)) {
Router.logD("LoadedApk#<init> has been loaded before, skip: " + mAppDir);
return;
}
// OnePlus magic...
if (Log.getStackTraceString(new Throwable()).
contains("android.app.ActivityThread$ApplicationThread.schedulePreload")) {
Router.logD("LoadedApk#<init> maybe oneplus's custom opt, skip");
return;
}
replaceParentClassLoader(loadedApk.getClassLoader());
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
lpparam.packageName = packageName;
lpparam.processName = AndroidAppHelper.currentProcessName();
lpparam.classLoader = loadedApk.getClassLoader();
lpparam.appInfo = loadedApk.getApplicationInfo();
lpparam.isFirstApplication = false;
XC_LoadPackage.callAll(lpparam);
} catch (Throwable t) {
Router.logE("error when hooking LoadedApk.<init>", t);
}
}
public static void backup(Object thiz, ActivityThread activityThread,
ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation,
boolean includeCode, boolean registerPackage) {
}
}

View File

@ -0,0 +1,41 @@
package com.elderdrivers.riru.edxp.yahfa.entry.hooker;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import de.robv.android.xposed.XposedBridge;
/**
* On OnePlus stock roms (Android Pie), {@link dalvik.system.BaseDexClassLoader#findClass(String)}
* will open /dev/binder to communicate with PackageManagerService to check whether
* current package name inCompatConfigList, which is an OnePlus OEM feature enabled only when
* system prop "persist.sys.oem.region" set to "CN".(detail of related source code:
* https://gist.github.com/solohsu/ecc07141759958fc096ba0781fac0a5f)
* If we invoke intZygoteCallbacks in
* {@link Main#forkAndSpecializePre}, where in zygote process,
* we would get a chance to invoke findclass, leaving fd of /dev/binder open in zygote process,
* which is not allowed because /dev/binder is not in predefined whitelist here:
* http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/jni/fd_utils.cpp#35
* So we just hook BaseDexClassLoader#inCompatConfigList to return false to prevent
* open of /dev/binder and we haven't found side effects yet.
* Other roms might share the same problems but not reported too.
*/
public class OnePlusWorkAroundHooker implements KeepMembers {
public static String className = "dalvik.system.BaseDexClassLoader";
public static String methodName = "inCompatConfigList";
public static String methodSig = "(ILjava/lang/String;)Z";
public static boolean hook(int type, String packageName) {
if (XposedBridge.disableHooks || Router.forkCompleted) {
return backup(type, packageName);
}
Router.logD("BaseDexClassLoader#inCompatConfigList() starts");
return false;
}
public static boolean backup(int type, String packageName) {
return false;
}
}

View File

@ -0,0 +1,66 @@
package com.elderdrivers.riru.edxp.yahfa.entry.hooker;
import android.os.Build;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import static com.elderdrivers.riru.edxp.util.ClassLoaderUtils.replaceParentClassLoader;
import static com.elderdrivers.riru.edxp.util.Utils.logD;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class StartBootstrapServicesHooker implements KeepMembers {
public static String className = "com.android.server.SystemServer";
public static String methodName = "startBootstrapServices";
public static String methodSig = "()V";
public static void hook(Object systemServer) {
if (XposedBridge.disableHooks) {
backup(systemServer);
return;
}
logD("SystemServer#startBootstrapServices() starts");
try {
XposedInit.loadedPackagesInProcess.add("android");
replaceParentClassLoader(SystemMainHooker.systemServerCL);
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
lpparam.packageName = "android";
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
lpparam.classLoader = SystemMainHooker.systemServerCL;
lpparam.appInfo = null;
lpparam.isFirstApplication = true;
XC_LoadPackage.callAll(lpparam);
// Huawei
try {
findAndHookMethod("com.android.server.pm.HwPackageManagerService", SystemMainHooker.systemServerCL, "isOdexMode", XC_MethodReplacement.returnConstant(false));
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
}
try {
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
findAndHookMethod(className, SystemMainHooker.systemServerCL, "dexEntryExists", String.class, XC_MethodReplacement.returnConstant(true));
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {
}
} catch (Throwable t) {
Router.logE("error when hooking startBootstrapServices", t);
} finally {
backup(systemServer);
}
}
public static void backup(Object systemServer) {
}
}

View File

@ -0,0 +1,43 @@
package com.elderdrivers.riru.edxp.yahfa.entry.hooker;
import android.app.ActivityThread;
import com.elderdrivers.riru.common.KeepMembers;
import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import de.robv.android.xposed.XposedBridge;
// system_server initialization
// ed: only support sdk >= 21 for now
public class SystemMainHooker implements KeepMembers {
public static String className = "android.app.ActivityThread";
public static String methodName = "systemMain";
public static String methodSig = "()Landroid/app/ActivityThread;";
public static ClassLoader systemServerCL;
public static ActivityThread hook() {
if (XposedBridge.disableHooks) {
return backup();
}
Router.logD("ActivityThread#systemMain() starts");
ActivityThread activityThread = backup();
try {
// get system_server classLoader
systemServerCL = Thread.currentThread().getContextClassLoader();
// deopt methods in SYSTEMSERVERCLASSPATH
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
Router.startSystemServerHook();
} catch (Throwable t) {
Router.logE("error when hooking systemMain", t);
}
return activityThread;
}
public static ActivityThread backup() {
return null;
}
}

View File

@ -0,0 +1,87 @@
package com.elderdrivers.riru.edxp.yahfa.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<String> 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<String> getBlackList() {
try {
PREFERENCES.reload();
Set<String> result = PREFERENCES.getStringSet(PREF_KEY_BLACK_LIST, new HashSet<String>());
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);
}
}
}

View File

@ -0,0 +1,64 @@
package com.elderdrivers.riru.edxp.yahfa.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);
}
}
}

View File

@ -0,0 +1,132 @@
package com.elderdrivers.riru.edxp.yahfa.proxy;
import android.text.TextUtils;
import com.elderdrivers.riru.edxp.Main;
import com.elderdrivers.riru.edxp.config.ConfigManager;
import com.elderdrivers.riru.edxp.yahfa.entry.Router;
import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter;
import com.elderdrivers.riru.edxp.util.ProcessUtils;
import com.elderdrivers.riru.edxp.util.Utils;
import de.robv.android.xposed.XposedBridge;
import static com.elderdrivers.riru.edxp.Main.isAppNeedHook;
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
/**
* 1. Non dynamic mode
* - system_server is whitelisted
* * for all child processes of main zygote
* What've been done in main zygote pre-forking system_server
* 1) non dynamic flag set (no need to reset)
* 2) boot image methods deopted (no need to redo)
* 3) startSystemServer flag set to true (need to reset)
* 4) workaround hooks installed (need to redo)
* 5) module list loaded and initZygote called (no need to redo)
* 6) close all fds (no need to redo because of 5))
* * for all child processes of secondary zygote
* 1) do the same things pre-forking first child process
* - system_server is blacklisted:
* * for all child processes of both main zygote and secondary zygote
* 1) do the same things pre-forking first child process
* 2. Dynamic mode:
* to be continued
*/
public class BlackWhiteListProxy {
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo,
String niceName, int[] fdsToClose, int[] fdsToIgnore,
boolean startChildZygote, String instructionSet,
String appDataDir) {
final boolean isDynamicModulesMode = 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();
}
}

View File

@ -0,0 +1,70 @@
package com.elderdrivers.riru.edxp.yahfa.proxy;
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;
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);
}
}

View File

@ -0,0 +1,49 @@
package com.elderdrivers.riru.edxp.yahfa.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.
* <p>
* 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<String, String[][]> 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<String, String[][]> getAll() {
return CALLERS;
}
public static String[][] get(String where) {
return CALLERS.get(where);
}
}

View File

@ -0,0 +1,41 @@
package com.elderdrivers.riru.edxp.yahfa.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.yahfa.util.InlinedMethodCallers.KEY_BOOT_IMAGE;
import static com.elderdrivers.riru.edxp.yahfa.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);
}
}

View File

@ -0,0 +1,105 @@
package com.lody.whale;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.HashMap;
/**
* @author Lody
*/
class VMHelper {
// Holds a mapping from Java type names to native type codes.
private static final HashMap<Class<?>, String> PRIMITIVE_TO_SIGNATURE;
static {
PRIMITIVE_TO_SIGNATURE = new HashMap<>(9);
PRIMITIVE_TO_SIGNATURE.put(byte.class, "B");
PRIMITIVE_TO_SIGNATURE.put(char.class, "C");
PRIMITIVE_TO_SIGNATURE.put(short.class, "S");
PRIMITIVE_TO_SIGNATURE.put(int.class, "I");
PRIMITIVE_TO_SIGNATURE.put(long.class, "J");
PRIMITIVE_TO_SIGNATURE.put(float.class, "F");
PRIMITIVE_TO_SIGNATURE.put(double.class, "D");
PRIMITIVE_TO_SIGNATURE.put(void.class, "V");
PRIMITIVE_TO_SIGNATURE.put(boolean.class, "Z");
}
/**
* Returns the internal name of {@code clazz} (also known as the
* descriptor).
*/
private static String getSignature(final Class<?> clazz) {
final String primitiveSignature = PRIMITIVE_TO_SIGNATURE.get(clazz);
if (primitiveSignature != null) {
return primitiveSignature;
} else if (clazz.isArray()) {
return "[" + getSignature(clazz.getComponentType());
} else {
return "L" + clazz.getName().replace('.', '/') + ";";
}
}
/**
* Returns the native type codes of {@code clazz}.
*/
private static String getShortyType(final Class<?> clazz) {
final String primitiveSignature = PRIMITIVE_TO_SIGNATURE.get(clazz);
if (primitiveSignature != null) {
return primitiveSignature;
}
return "L";
}
// @SuppressWarnings("ConstantConditions")
private static String getSignature(final Class<?> retType,
final Class<?>[] parameterTypes) {
final StringBuilder result = new StringBuilder();
result.append('(');
for (final Class<?> parameterType : parameterTypes) {
result.append(getSignature(parameterType));
}
result.append(")");
result.append(getSignature(retType));
return result.toString();
}
private static String getShorty(final Class<?> retType,
final Class<?>[] parameterTypes) {
final StringBuilder result = new StringBuilder();
result.append(getShortyType(retType));
for (final Class<?> parameterType : parameterTypes) {
result.append(getShortyType(parameterType));
}
return result.toString();
}
static String getSignature(final Member m) {
if (m instanceof Method) {
final Method md = (Method) m;
return getSignature(md.getReturnType(), md.getParameterTypes());
}
if (m instanceof Constructor) {
final Constructor<?> c = (Constructor<?>) m;
return getSignature(void.class, c.getParameterTypes());
}
return null;
}
static String getShorty(final Member m) {
if (m instanceof Method) {
final Method md = (Method) m;
return getShorty(md.getReturnType(), md.getParameterTypes());
}
if (m instanceof Constructor) {
final Constructor<?> c = (Constructor<?>) m;
return getShorty(void.class, c.getParameterTypes());
}
return null;
}
}

View File

@ -0,0 +1,74 @@
package com.lody.whale;
import android.os.Build;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedBridge;
/**
* @author Lody
* <p>
* NOTICE: Do not move or rename any methods in this class.
*/
public class WhaleRuntime {
static {
System.loadLibrary("whale.edxp");
}
private static String getShorty(Member member) {
return VMHelper.getShorty(member);
}
public static long[] countInstancesOfClasses(Class[] classes, boolean assignable) {
if (Build.VERSION.SDK_INT < 27) {
throw new UnsupportedOperationException("Not support countInstancesOfClasses on your device yet.");
}
try {
Class<?> clazz = Class.forName("dalvik.system.VMDebug");
Method method = clazz.getDeclaredMethod("countInstancesOfClasses", Class[].class, boolean.class);
return (long[]) method.invoke(null, classes, assignable);
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
public static Object[][] getInstancesOfClasses(Class[] classes, boolean assignable) {
if (Build.VERSION.SDK_INT < 28) {
throw new UnsupportedOperationException("Not support getInstancesOfClasses on your device yet.");
}
try {
Class<?> clazz = Class.forName("dalvik.system.VMDebug");
Method method = clazz.getDeclaredMethod("getInstancesOfClasses", Class[].class, boolean.class);
return (Object[][]) method.invoke(null, classes, assignable);
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
public static Object handleHookedMethod(Member member, long slot, Object additionInfo, Object thisObject, Object[] args) throws Throwable {
return XposedBridge.handleHookedMethod(member, slot, additionInfo, thisObject, args);
}
public static native Object invokeOriginalMethodNative(long slot, Object thisObject, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
public static native long getMethodSlot(Member member) throws IllegalArgumentException;
public static native long hookMethodNative(Class<?> declClass, Member method, Object additionInfo);
public static native void setObjectClassNative(Object object, Class<?> parent);
public static native Object cloneToSubclassNative(Object object, Class<?> subClass);
public static native void removeFinalFlagNative(Class<?> cl);
public static native void enforceDisableHiddenAPIPolicy();
private static native void reserved0();
private static native void reserved1();
}

View File

@ -16,7 +16,7 @@ public class YahfaHookProvider implements HookProvider {
}
@Override
public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable {
public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable {
return DynamicBridge.invokeOriginalMethod(method, thisObject, args);
}
@ -29,4 +29,9 @@ public class YahfaHookProvider implements HookProvider {
public void deoptMethods(String packageName, ClassLoader classLoader) {
PrebuiltMethodsDeopter.deoptMethods(packageName, classLoader);
}
@Override
public long getMethodId(Member member) {
return 0;
}
}

View File

@ -1 +1 @@
include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook'
include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook', ':edxp-whale'

View File

@ -54,7 +54,7 @@ public class EdXpConfigGlobal {
}
@Override
public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return null;
}
@ -68,5 +68,10 @@ public class EdXpConfigGlobal {
public void deoptMethods(String packageName, ClassLoader classLoader) {
}
@Override
public long getMethodId(Member member) {
return 0;
}
};
}

View File

@ -1,6 +1,5 @@
package com.elderdrivers.riru.edxp.hook;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import de.robv.android.xposed.XposedBridge;
@ -9,9 +8,11 @@ public interface HookProvider {
void hookMethod(Member method, XposedBridge.AdditionalHookInfo additionalInfo);
Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable;
Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable;
Member findMethodNative(Member hookMethod);
void deoptMethods(String packageName, ClassLoader classLoader);
long getMethodId(Member member);
}

View File

@ -260,7 +260,7 @@ public final class XposedBridge {
/**
* This method is called as a replacement for hooked methods.
*/
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
public static Object handleHookedMethod(Member method, long originalMethodId, Object additionalInfoObj,
Object thisObject, Object[] args) throws Throwable {
AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;
@ -398,12 +398,12 @@ public final class XposedBridge {
EdXpConfigGlobal.getHookProvider().hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
}
private static Object invokeOriginalMethodNative(Member method, int methodId,
private static Object invokeOriginalMethodNative(Member method, long methodId,
Class<?>[] parameterTypes,
Class<?> returnType,
Object thisObject, Object[] args)
throws Throwable {
return EdXpConfigGlobal.getHookProvider().invokeOriginalMethod(method, thisObject, args);
return EdXpConfigGlobal.getHookProvider().invokeOriginalMethod(method, methodId, thisObject, args);
}
/**
@ -452,7 +452,8 @@ public final class XposedBridge {
throw new IllegalArgumentException("method must be of type Method or Constructor");
}
return invokeOriginalMethodNative(method, 0, parameterTypes, returnType, thisObject, args);
long methodId = EdXpConfigGlobal.getHookProvider().getMethodId(method);
return invokeOriginalMethodNative(method, methodId, parameterTypes, returnType, thisObject, args);
}
/*package*/ static void setObjectClass(Object obj, Class<?> clazz) {