Optimize dexmaker for yahfa

This reverts commit c84c09b2d5.
This commit is contained in:
LoveSy 2021-01-28 00:26:23 +08:00
parent 0e2783f8e5
commit d3a5b282cb
7 changed files with 182 additions and 370 deletions

View File

@ -29,8 +29,8 @@ namespace art {
const char *thisDesc = GetDescriptor(thiz, &storage1); const char *thisDesc = GetDescriptor(thiz, &storage1);
const char *thatDesc = GetDescriptor(that, &storage2); const char *thatDesc = GetDescriptor(that, &storage2);
// Note: these identifiers should be consistent with those in Java layer // Note: these identifiers should be consistent with those in Java layer
if (strstr(thisDesc, "EdHooker_") != nullptr if (strstr(thisDesc, "LspHooker_") != nullptr
|| strstr(thatDesc, "EdHooker_") != nullptr || strstr(thatDesc, "LspHooker_") != nullptr
|| strstr(thisDesc, "com/elderdrivers/riru/") != nullptr || strstr(thisDesc, "com/elderdrivers/riru/") != nullptr
|| strstr(thatDesc, "com/elderdrivers/riru/") != nullptr) { || strstr(thatDesc, "com/elderdrivers/riru/") != nullptr) {
return true; return true;

View File

@ -41,9 +41,9 @@ public class NormalProxy extends BaseProxy {
mRouter.initResourcesHook(); mRouter.initResourcesHook();
mRouter.prepare(isSystem); mRouter.prepare(isSystem);
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
mRouter.installBootstrapHooks(isSystem);
ConfigManager.appDataDir = appDataDir; ConfigManager.appDataDir = appDataDir;
ConfigManager.niceName = niceName; ConfigManager.niceName = niceName;
mRouter.installBootstrapHooks(isSystem);
XposedInit.prefsBasePath = ConfigManager.getPrefsPath(""); XposedInit.prefsBasePath = ConfigManager.getPrefsPath("");
mRouter.onEnterChildProcess(); mRouter.onEnterChildProcess();
Utils.logI("Loading modules for " + niceName); Utils.logI("Loading modules for " + niceName);

View File

@ -72,6 +72,6 @@ public class FileUtils {
} }
public static String getDataPathPrefix() { public static String getDataPathPrefix() {
return ConfigManager.getDataPathPrefix(); return ConfigManager.getDataPathPrefix() + "/";
} }
} }

View File

@ -13,6 +13,7 @@ import external.com.android.dx.Code;
import external.com.android.dx.Local; import external.com.android.dx.Local;
import external.com.android.dx.TypeId; import external.com.android.dx.TypeId;
@SuppressWarnings({"rawtypes", "unchecked"})
public class DexMakerUtils { public class DexMakerUtils {
public static boolean canCache = true; public static boolean canCache = true;
@ -215,12 +216,6 @@ public class DexMakerUtils {
} }
} }
public static void returnRightValue(Code code, Class<?> returnType, Map<Class, Local> resultLocals) {
String unboxMethod;
TypeId<?> boxTypeId;
code.returnValue(resultLocals.get(returnType));
}
public static String getSha1Hex(String text) { public static String getSha1Hex(String text) {
final MessageDigest digest; final MessageDigest digest;
try { try {

View File

@ -1,6 +1,9 @@
package com.elderdrivers.riru.edxp.yahfa.dexmaker; package com.elderdrivers.riru.edxp.yahfa.dexmaker;
import com.elderdrivers.riru.edxp.config.ConfigManager;
import java.io.File;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member; import java.lang.reflect.Member;
@ -9,11 +12,12 @@ import java.lang.reflect.Modifier;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import de.robv.android.xposed.LspHooker;
import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedBridge;
public final class DynamicBridge { public final class DynamicBridge {
private static final HashMap<Member, Method> hookedInfo = new HashMap<>(); private static final HashMap<Member, LspHooker> hookedInfo = new HashMap<>();
private static final HookerDexMaker dexMaker = new HookerDexMaker(); private static final HookerDexMaker dexMaker = new HookerDexMaker();
private static final AtomicBoolean dexPathInited = new AtomicBoolean(false); private static final AtomicBoolean dexPathInited = new AtomicBoolean(false);
@ -40,7 +44,7 @@ public final class DynamicBridge {
try { try {
dexMaker.start(hookMethod, additionalHookInfo, dexMaker.start(hookMethod, additionalHookInfo,
hookMethod.getDeclaringClass().getClassLoader()); hookMethod.getDeclaringClass().getClassLoader());
hookedInfo.put(hookMethod, dexMaker.getCallBackupMethod()); hookedInfo.put(hookMethod, dexMaker.getHooker());
} catch (Exception e) { } catch (Exception e) {
DexLog.e("error occur when generating dex.", e); DexLog.e("error occur when generating dex.", e);
} }
@ -66,25 +70,16 @@ public final class DynamicBridge {
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
Method callBackup = hookedInfo.get(method); LspHooker hooker = hookedInfo.get(method);
if (callBackup == null) { if (hooker == null) {
throw new IllegalStateException("method not hooked, cannot call original method."); throw new IllegalStateException("method not hooked, cannot call original method.");
} }
if (!Modifier.isStatic(callBackup.getModifiers())) { try {
throw new IllegalStateException("original method is not static, something must be wrong!"); return hooker.callBackup(thisObject, args);
} } catch (IllegalAccessException e) {
callBackup.setAccessible(true); throw e;
if (args == null) { } catch (Throwable e) {
args = new Object[0]; throw new InvocationTargetException(e);
}
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;
System.arraycopy(args, 0, newArgs, 1, argsSize);
return callBackup.invoke(null, newArgs);
} }
} }
} }

View File

@ -5,7 +5,6 @@ import android.os.Build;
import com.elderdrivers.riru.edxp.BuildConfig; import com.elderdrivers.riru.edxp.BuildConfig;
import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.ConfigManager;
import com.elderdrivers.riru.edxp.core.Yahfa;
import com.elderdrivers.riru.edxp.core.yahfa.HookMain; import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
import com.elderdrivers.riru.edxp.util.ProxyClassLoader; import com.elderdrivers.riru.edxp.util.ProxyClassLoader;
@ -16,17 +15,13 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import dalvik.system.InMemoryDexClassLoader; import dalvik.system.InMemoryDexClassLoader;
import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.LspHooker;
import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedBridge;
import external.com.android.dx.BinaryOp;
import external.com.android.dx.Code; import external.com.android.dx.Code;
import external.com.android.dx.Comparison;
import external.com.android.dx.DexMaker; import external.com.android.dx.DexMaker;
import external.com.android.dx.FieldId; import external.com.android.dx.FieldId;
import external.com.android.dx.Label;
import external.com.android.dx.Local; import external.com.android.dx.Local;
import external.com.android.dx.MethodId; import external.com.android.dx.MethodId;
import external.com.android.dx.TypeId; import external.com.android.dx.TypeId;
@ -37,82 +32,35 @@ import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.canCache;
import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.createResultLocals; import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.createResultLocals;
import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.getObjTypeIdIfPrimitive; import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.getObjTypeIdIfPrimitive;
@SuppressWarnings("rawtypes")
public class HookerDexMaker { public class HookerDexMaker {
public static final String METHOD_NAME_BACKUP = "backup"; public static final String METHOD_NAME_BACKUP = "backup";
public static final String METHOD_NAME_HOOK = "hook"; 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_SETUP = "setup";
public static final TypeId<Object[]> objArrayTypeId = TypeId.get(Object[].class); public static final TypeId<Object[]> objArrayTypeId = TypeId.get(Object[].class);
private static final String CLASS_DESC_PREFIX = "L"; private static final String CLASS_DESC_PREFIX = "L";
/** /**
* Note: this identifier is used in native codes to pass class access verification. * 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 CLASS_NAME_PREFIX = "LspHooker_";
private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; private static final String FIELD_NAME_HOOKER = "hooker";
private static final String FIELD_NAME_METHOD = "method"; private static final TypeId<LspHooker> hookerTypeId = TypeId.get(LspHooker.class);
private static final String PARAMS_FIELD_NAME_METHOD = "method"; private static final MethodId<LspHooker, Object> handleHookedMethodMethodId =
private static final String PARAMS_FIELD_NAME_THIS_OBJECT = "thisObject"; hookerTypeId.getMethod(TypeId.OBJECT, "handleHookedMethod", objArrayTypeId);
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<Throwable> throwableTypeId = TypeId.get(Throwable.class);
private static final TypeId<Member> memberTypeId = TypeId.get(Member.class);
private static final TypeId<XC_MethodHook> callbackTypeId = TypeId.get(XC_MethodHook.class);
private static final TypeId<XposedBridge.AdditionalHookInfo> hookInfoTypeId
= TypeId.get(XposedBridge.AdditionalHookInfo.class);
private static final TypeId<XposedBridge.CopyOnWriteSortedSet> callbacksTypeId
= TypeId.get(XposedBridge.CopyOnWriteSortedSet.class);
private static final TypeId<XC_MethodHook.MethodHookParam> paramTypeId
= TypeId.get(XC_MethodHook.MethodHookParam.class);
private static final MethodId<XC_MethodHook.MethodHookParam, Void> setResultMethodId =
paramTypeId.getMethod(TypeId.VOID, "setResult", TypeId.OBJECT);
private static final MethodId<XC_MethodHook.MethodHookParam, Void> setThrowableMethodId =
paramTypeId.getMethod(TypeId.VOID, "setThrowable", throwableTypeId);
private static final MethodId<XC_MethodHook.MethodHookParam, Object> getResultMethodId =
paramTypeId.getMethod(TypeId.OBJECT, "getResult");
private static final MethodId<XC_MethodHook.MethodHookParam, Throwable> getThrowableMethodId =
paramTypeId.getMethod(throwableTypeId, "getThrowable");
private static final MethodId<XC_MethodHook.MethodHookParam, Boolean> hasThrowableMethodId =
paramTypeId.getMethod(TypeId.BOOLEAN, "hasThrowable");
private static final MethodId<XC_MethodHook, Void> callAfterCallbackMethodId =
callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_AFTER, paramTypeId);
private static final MethodId<XC_MethodHook, Void> callBeforeCallbackMethodId =
callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_BEFORE, paramTypeId);
private static final FieldId<XC_MethodHook.MethodHookParam, Boolean> returnEarlyFieldId =
paramTypeId.getField(TypeId.BOOLEAN, "returnEarly");
private static final TypeId<XposedBridge> xposedBridgeTypeId = TypeId.get(XposedBridge.class);
private static final MethodId<XposedBridge, Void> logThrowableMethodId =
xposedBridgeTypeId.getMethod(TypeId.VOID, "log", throwableTypeId);
private static final MethodId<XposedBridge, Void> logStrMethodId =
xposedBridgeTypeId.getMethod(TypeId.VOID, "log", TypeId.STRING);
private static AtomicLong sClassNameSuffix = new AtomicLong(1); private FieldId<?, LspHooker> mHookerFieldId;
private FieldId<?, XposedBridge.AdditionalHookInfo> mHookInfoFieldId;
private FieldId<?, Member> mMethodFieldId;
private MethodId<?, ?> mBackupMethodId;
private MethodId<?, ?> mCallBackupMethodId;
private MethodId<?, ?> mHookMethodId;
private TypeId<?> mHookerTypeId; private TypeId<?> mHookerTypeId;
private TypeId<?>[] mParameterTypeIds; private TypeId<?>[] mParameterTypeIds;
private Class<?>[] mActualParameterTypes; private Class<?>[] mActualParameterTypes;
private Class<?> mReturnType;
private TypeId<?> mReturnTypeId; private TypeId<?> mReturnTypeId;
private boolean mIsStatic;
// TODO use this to generate methods
private boolean mHasThrowable;
private DexMaker mDexMaker; private DexMaker mDexMaker;
private Member mMember; private Member mMember;
private XposedBridge.AdditionalHookInfo mHookInfo; private XposedBridge.AdditionalHookInfo mHookInfo;
private ClassLoader mAppClassLoader; private ClassLoader mAppClassLoader;
private Class<?> mHookClass; private LspHooker mHooker;
private Method mHookMethod;
private Method mBackupMethod;
private Method mCallBackupMethod;
private static TypeId<?>[] getParameterTypeIds(Class<?>[] parameterTypes, boolean isStatic) { private static TypeId<?>[] getParameterTypeIds(Class<?>[] parameterTypes, boolean isStatic) {
int parameterSize = parameterTypes.length; int parameterSize = parameterTypes.length;
@ -144,29 +92,27 @@ public class HookerDexMaker {
public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo,
ClassLoader appClassLoader) throws Exception { ClassLoader appClassLoader) throws Exception {
Class<?> returnType;
boolean isStatic;
if (member instanceof Method) { if (member instanceof Method) {
Method method = (Method) member; Method method = (Method) member;
mIsStatic = Modifier.isStatic(method.getModifiers()); isStatic = Modifier.isStatic(method.getModifiers());
mReturnType = method.getReturnType(); returnType = method.getReturnType();
if (mReturnType.equals(Void.class) || mReturnType.equals(void.class) if (returnType.equals(Void.class) || returnType.equals(void.class)
|| mReturnType.isPrimitive()) { || returnType.isPrimitive()) {
mReturnTypeId = TypeId.get(mReturnType); mReturnTypeId = TypeId.get(returnType);
} else { } else {
// all others fallback to plain Object for convenience // all others fallback to plain Object for convenience
mReturnType = Object.class;
mReturnTypeId = TypeId.OBJECT; mReturnTypeId = TypeId.OBJECT;
} }
mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic); mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), isStatic);
mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic); mActualParameterTypes = getParameterTypes(method.getParameterTypes(), isStatic);
mHasThrowable = method.getExceptionTypes().length > 0;
} else if (member instanceof Constructor) { } else if (member instanceof Constructor) {
Constructor constructor = (Constructor) member; Constructor constructor = (Constructor) member;
mIsStatic = false; isStatic = false;
mReturnType = void.class;
mReturnTypeId = TypeId.VOID; mReturnTypeId = TypeId.VOID;
mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic); mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), isStatic);
mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic); mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), isStatic);
mHasThrowable = constructor.getExceptionTypes().length > 0;
} else if (member.getDeclaringClass().isInterface()) { } else if (member.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString()); throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString());
} else if (Modifier.isAbstract(member.getModifiers())) { } else if (Modifier.isAbstract(member.getModifiers())) {
@ -186,6 +132,7 @@ public class HookerDexMaker {
doMake(member.getDeclaringClass().getName()); doMake(member.getDeclaringClass().getName());
} }
@SuppressWarnings("ResultOfMethodCallIgnored")
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
private void doMake(String hookedClassName) throws Exception { private void doMake(String hookedClassName) throws Exception {
mDexMaker = new DexMaker(); mDexMaker = new DexMaker();
@ -225,15 +172,13 @@ public class HookerDexMaker {
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader); loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
} }
mHookClass = Class.forName(className.replace("/", "."), true, loader); Class<?> hookClass = Class.forName(className.replace("/", "."), true, loader);
// Execute our newly-generated code in-process. // Execute our newly-generated code in-process.
mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class) Method backupMethod = hookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes);
.invoke(null, mMember, mHookInfo); mHooker = new LspHooker(mHookInfo, mMember, backupMethod);
mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); hookClass.getMethod(METHOD_NAME_SETUP, LspHooker.class).invoke(null, mHooker);
mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); Method hookMethod = hookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes);
mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); HookMain.backupAndHook(mMember, hookMethod, backupMethod);
Yahfa.setMethodNonCompilable(mCallBackupMethod);
HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod);
} }
private void doGenerate(String className) { private void doGenerate(String className) {
@ -244,49 +189,30 @@ public class HookerDexMaker {
generateSetupMethod(); generateSetupMethod();
generateBackupMethod(); generateBackupMethod();
generateHookMethod(); generateHookMethod();
generateCallBackupMethod();
} }
public Method getHookMethod() { public LspHooker getHooker() {return mHooker;}
return mHookMethod;
}
public Method getBackupMethod() {
return mBackupMethod;
}
public Method getCallBackupMethod() {
return mCallBackupMethod;
}
public Class getHookClass() {
return mHookClass;
}
private void generateFields() { private void generateFields() {
mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO); mHookerFieldId = mHookerTypeId.getField(hookerTypeId, FIELD_NAME_HOOKER);
mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD); mDexMaker.declare(mHookerFieldId, Modifier.STATIC, null);
mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null);
mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null);
} }
private void generateSetupMethod() { private void generateSetupMethod() {
MethodId<?, Void> setupMethodId = mHookerTypeId.getMethod( MethodId<?, Void> setupMethodId = mHookerTypeId.getMethod(
TypeId.VOID, METHOD_NAME_SETUP, memberTypeId, hookInfoTypeId); TypeId.VOID, METHOD_NAME_SETUP, hookerTypeId);
Code code = mDexMaker.declare(setupMethodId, Modifier.PUBLIC | Modifier.STATIC); Code code = mDexMaker.declare(setupMethodId, Modifier.PUBLIC | Modifier.STATIC);
// init logic // init logic
// get parameters // get parameters
Local<Member> method = code.getParameter(0, memberTypeId); Local<LspHooker> hooker = code.getParameter(0, hookerTypeId);
Local<XposedBridge.AdditionalHookInfo> hookInfo = code.getParameter(1, hookInfoTypeId);
// save params to static // save params to static
code.sput(mMethodFieldId, method); code.sput(mHookerFieldId, hooker);
code.sput(mHookInfoFieldId, hookInfo);
code.returnVoid(); code.returnVoid();
} }
private void generateBackupMethod() { private void generateBackupMethod() {
mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds); MethodId<?, ?> backupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds);
Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); Code code = mDexMaker.declare(backupMethodId, Modifier.PUBLIC | Modifier.STATIC);
Map<TypeId, Local> resultLocals = createResultLocals(code); Map<TypeId, Local> resultLocals = createResultLocals(code);
// do nothing // do nothing
if (mReturnTypeId.equals(TypeId.VOID)) { if (mReturnTypeId.equals(TypeId.VOID)) {
@ -297,253 +223,48 @@ public class HookerDexMaker {
} }
} }
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<TypeId, Local> 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() { private void generateHookMethod() {
mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds); MethodId<?, ?> hookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds);
Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC); Code code = mDexMaker.declare(hookMethodId, Modifier.PUBLIC | Modifier.STATIC);
// code starts // 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 // prepare locals
Local<Boolean> disableHooks = code.newLocal(TypeId.BOOLEAN); Local<LspHooker> hooker = code.newLocal(hookerTypeId);
Local<XposedBridge.AdditionalHookInfo> hookInfo = code.newLocal(hookInfoTypeId);
Local<XposedBridge.CopyOnWriteSortedSet> callbacks = code.newLocal(callbacksTypeId);
Local<Object[]> snapshot = code.newLocal(objArrayTypeId);
Local<Integer> snapshotLen = code.newLocal(TypeId.INT);
Local<Object> callbackObj = code.newLocal(TypeId.OBJECT);
Local<XC_MethodHook> callback = code.newLocal(callbackTypeId);
Local<Object> resultObj = code.newLocal(TypeId.OBJECT); // as a temp Local Local<Object> resultObj = code.newLocal(TypeId.OBJECT); // as a temp Local
Local<Integer> one = code.newLocal(TypeId.INT);
Local<Object> nullObj = code.newLocal(TypeId.OBJECT);
Local<Throwable> throwable = code.newLocal(throwableTypeId);
Local<XC_MethodHook.MethodHookParam> param = code.newLocal(paramTypeId);
Local<Member> method = code.newLocal(memberTypeId);
Local<Object> thisObject = code.newLocal(TypeId.OBJECT);
Local<Object[]> args = code.newLocal(objArrayTypeId); Local<Object[]> args = code.newLocal(objArrayTypeId);
Local<Boolean> returnEarly = code.newLocal(TypeId.BOOLEAN);
Local<Integer> actualParamSize = code.newLocal(TypeId.INT); Local<Integer> actualParamSize = code.newLocal(TypeId.INT);
Local<Integer> argIndex = code.newLocal(TypeId.INT); Local<Integer> argIndex = code.newLocal(TypeId.INT);
Local<Integer> beforeIdx = code.newLocal(TypeId.INT);
Local<Object> lastResult = code.newLocal(TypeId.OBJECT);
Local<Throwable> lastThrowable = code.newLocal(throwableTypeId);
Local<Boolean> hasThrowable = code.newLocal(TypeId.BOOLEAN);
Local[] allArgsLocals = createParameterLocals(code); Local[] allArgsLocals = createParameterLocals(code);
Map<TypeId, Local> resultLocals = createResultLocals(code); Map<TypeId, Local> resultLocals = createResultLocals(code);
code.loadConstant(args, null); code.loadConstant(args, null);
code.loadConstant(argIndex, 0); code.loadConstant(argIndex, 0);
code.loadConstant(one, 1);
code.loadConstant(snapshotLen, 0);
code.loadConstant(nullObj, null);
// check XposedBridge.disableHooks flag code.sget(mHookerFieldId, hooker);
FieldId<XposedBridge, Boolean> 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 // start hooking
// prepare hooking locals // prepare hooking locals
int paramsSize = mParameterTypeIds.length; int paramsSize = mParameterTypeIds.length;
int offset = 0; code.loadConstant(actualParamSize, paramsSize);
// 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); code.newArray(args, actualParamSize);
for (int i = offset; i < paramsSize; i++) { for (int i = 0; i < paramsSize; i++) {
Local parameter = allArgsLocals[i]; Local parameter = allArgsLocals[i];
// save parameter to resultObj as Object // save parameter to resultObj as Object
autoBoxIfNecessary(code, resultObj, parameter); autoBoxIfNecessary(code, resultObj, parameter);
code.loadConstant(argIndex, i - offset); code.loadConstant(argIndex, i);
// save Object to args // save Object to args
code.aput(args, argIndex, resultObj); code.aput(args, argIndex, resultObj);
} }
// create param // handleHookedMethod
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)) { if (mReturnTypeId.equals(TypeId.VOID)) {
code.invokeVirtual(handleHookedMethodMethodId, null, hooker, args);
code.returnVoid(); code.returnVoid();
} else { } else {
// getResult always return an Object, so save to resultObj // hooker always return an Object, so save to resultObj
code.invokeVirtual(getResultMethodId, resultObj, param); code.invokeVirtual(handleHookedMethodMethodId, resultObj, hooker, args);
// have to unbox it if returnType is primitive // have to unbox it if returnType is primitive
// casting Object // casting Object
TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId); TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId);
@ -555,21 +276,6 @@ public class HookerDexMaker {
// return // return
code.returnValue(toReturn); 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) { private Local[] createParameterLocals(Code code) {

View File

@ -0,0 +1,116 @@
package de.robv.android.xposed;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static de.robv.android.xposed.XposedBridge.disableHooks;
public class LspHooker {
private final XposedBridge.AdditionalHookInfo additionalInfo;
private final Member method;
private final Method backup;
public LspHooker(XposedBridge.AdditionalHookInfo info, Member origin, Method backup) {
this.additionalInfo = info;
this.method = origin;
this.backup = backup;
}
public Object callBackup(Object thisObject, Object[] args) throws Throwable {
try {
if (args == null) {
args = new Object[0];
}
if (Modifier.isStatic(method.getModifiers())) {
return backup.invoke(null, args);
} else {
Object[] newArgs = new Object[args.length + 1];
newArgs[0] = thisObject;
System.arraycopy(args, 0, newArgs, 1, args.length);
return backup.invoke(null, newArgs);
}
} catch (InvocationTargetException ite) {
throw ite.getCause();
}
}
@SuppressWarnings({"unused", "RedundantSuppression"})
public Object handleHookedMethod(Object[] args) throws Throwable {
Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
final int callbacksLength = callbacksSnapshot.length;
if (disableHooks || callbacksLength == 0) {
try {
return backup.invoke(null, args);
} catch (InvocationTargetException ite) {
throw ite.getCause();
}
}
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
param.method = method;
if (Modifier.isStatic(method.getModifiers())) {
param.thisObject = null;
param.args = args;
} else {
param.thisObject = args[0];
param.args = new Object[args.length - 1];
System.arraycopy(args, 1, param.args, 0, args.length - 1);
}
// call "before method" callbacks
int beforeIdx = 0;
do {
try {
((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
} catch (Throwable t) {
XposedBridge.log(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 < callbacksLength);
// call original method if not requested otherwise
if (!param.returnEarly) {
param.setResult(callBackup(param.thisObject, param.args));
}
// call "after method" callbacks
int afterIdx = beforeIdx - 1;
do {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable();
try {
((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);
} catch (Throwable t) {
XposedBridge.log(t);
// reset to last result (ignoring what the unexpectedly exiting callback did)
if (lastThrowable == null)
param.setResult(lastResult);
else
param.setThrowable(lastThrowable);
}
} while (--afterIdx >= 0);
// return
if (param.hasThrowable())
throw param.getThrowable();
else
return param.getResult();
}
}