diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h b/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h index 38f17863..8c0995d3 100644 --- a/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h +++ b/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h @@ -29,8 +29,8 @@ namespace art { const char *thisDesc = GetDescriptor(thiz, &storage1); const char *thatDesc = GetDescriptor(that, &storage2); // Note: these identifiers should be consistent with those in Java layer - if (strstr(thisDesc, "EdHooker_") != nullptr - || strstr(thatDesc, "EdHooker_") != nullptr + if (strstr(thisDesc, "LspHooker_") != nullptr + || strstr(thatDesc, "LspHooker_") != nullptr || strstr(thisDesc, "com/elderdrivers/riru/") != nullptr || strstr(thatDesc, "com/elderdrivers/riru/") != nullptr) { return true; diff --git a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/proxy/NormalProxy.java b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/proxy/NormalProxy.java index 107778d9..ab1d3771 100644 --- a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/proxy/NormalProxy.java +++ b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/proxy/NormalProxy.java @@ -41,9 +41,9 @@ public class NormalProxy extends BaseProxy { mRouter.initResourcesHook(); mRouter.prepare(isSystem); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote - mRouter.installBootstrapHooks(isSystem); ConfigManager.appDataDir = appDataDir; ConfigManager.niceName = niceName; + mRouter.installBootstrapHooks(isSystem); XposedInit.prefsBasePath = ConfigManager.getPrefsPath(""); mRouter.onEnterChildProcess(); Utils.logI("Loading modules for " + niceName); diff --git a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/util/FileUtils.java b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/util/FileUtils.java index 968f73bb..e1bd0d48 100644 --- a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/util/FileUtils.java +++ b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/util/FileUtils.java @@ -72,6 +72,6 @@ public class FileUtils { } public static String getDataPathPrefix() { - return ConfigManager.getDataPathPrefix(); + return ConfigManager.getDataPathPrefix() + "/"; } } diff --git a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java index df9b36bb..d977e854 100644 --- a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java +++ b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java @@ -13,6 +13,7 @@ import external.com.android.dx.Code; import external.com.android.dx.Local; import external.com.android.dx.TypeId; +@SuppressWarnings({"rawtypes", "unchecked"}) public class DexMakerUtils { public static boolean canCache = true; @@ -215,12 +216,6 @@ public class DexMakerUtils { } } - public static void returnRightValue(Code code, Class returnType, Map resultLocals) { - String unboxMethod; - TypeId boxTypeId; - code.returnValue(resultLocals.get(returnType)); - } - public static String getSha1Hex(String text) { final MessageDigest digest; try { diff --git a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java index 0dfa3234..0ab52658 100644 --- a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java +++ b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java @@ -1,6 +1,9 @@ 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.InvocationTargetException; import java.lang.reflect.Member; @@ -9,11 +12,12 @@ import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; +import de.robv.android.xposed.LspHooker; import de.robv.android.xposed.XposedBridge; public final class DynamicBridge { - private static final HashMap hookedInfo = new HashMap<>(); + private static final HashMap hookedInfo = new HashMap<>(); private static final HookerDexMaker dexMaker = new HookerDexMaker(); private static final AtomicBoolean dexPathInited = new AtomicBoolean(false); @@ -40,7 +44,7 @@ public final class DynamicBridge { try { dexMaker.start(hookMethod, additionalHookInfo, hookMethod.getDeclaringClass().getClassLoader()); - hookedInfo.put(hookMethod, dexMaker.getCallBackupMethod()); + hookedInfo.put(hookMethod, dexMaker.getHooker()); } catch (Exception 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) throws InvocationTargetException, IllegalAccessException { - Method callBackup = hookedInfo.get(method); - if (callBackup == null) { + LspHooker hooker = hookedInfo.get(method); + if (hooker == null) { throw new IllegalStateException("method not hooked, cannot call original method."); } - if (!Modifier.isStatic(callBackup.getModifiers())) { - throw new IllegalStateException("original method is not static, something must be wrong!"); - } - callBackup.setAccessible(true); - if (args == null) { - args = new Object[0]; - } - final int argsSize = args.length; - if (Modifier.isStatic(method.getModifiers())) { - return callBackup.invoke(null, args); - } else { - Object[] newArgs = new Object[argsSize + 1]; - newArgs[0] = thisObject; - System.arraycopy(args, 0, newArgs, 1, argsSize); - return callBackup.invoke(null, newArgs); + try { + return hooker.callBackup(thisObject, args); + } catch (IllegalAccessException e) { + throw e; + } catch (Throwable e) { + throw new InvocationTargetException(e); } } } diff --git a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java index 63aa89de..7cf5cf9c 100644 --- a/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java +++ b/edxp-core/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java @@ -5,7 +5,6 @@ import android.os.Build; import com.elderdrivers.riru.edxp.BuildConfig; 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.util.ProxyClassLoader; @@ -16,17 +15,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; import dalvik.system.InMemoryDexClassLoader; -import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.LspHooker; import de.robv.android.xposed.XposedBridge; -import external.com.android.dx.BinaryOp; import external.com.android.dx.Code; -import external.com.android.dx.Comparison; import external.com.android.dx.DexMaker; import external.com.android.dx.FieldId; -import external.com.android.dx.Label; import external.com.android.dx.Local; import external.com.android.dx.MethodId; import external.com.android.dx.TypeId; @@ -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.getObjTypeIdIfPrimitive; +@SuppressWarnings("rawtypes") public class HookerDexMaker { public static final String METHOD_NAME_BACKUP = "backup"; public static final String METHOD_NAME_HOOK = "hook"; - public static final String METHOD_NAME_CALL_BACKUP = "callBackup"; public static final String METHOD_NAME_SETUP = "setup"; public static final TypeId objArrayTypeId = TypeId.get(Object[].class); private static final String CLASS_DESC_PREFIX = "L"; /** * Note: this identifier is used in native codes to pass class access verification. */ - private static final String CLASS_NAME_PREFIX = "EdHooker_"; - private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; - private static final String FIELD_NAME_METHOD = "method"; - private static final String PARAMS_FIELD_NAME_METHOD = "method"; - private static final String PARAMS_FIELD_NAME_THIS_OBJECT = "thisObject"; - private static final String PARAMS_FIELD_NAME_ARGS = "args"; - private static final String CALLBACK_METHOD_NAME_BEFORE = "callBeforeHookedMethod"; - private static final String CALLBACK_METHOD_NAME_AFTER = "callAfterHookedMethod"; - private static final String PARAMS_METHOD_NAME_IS_EARLY_RETURN = "isEarlyReturn"; - private static final TypeId throwableTypeId = TypeId.get(Throwable.class); - private static final TypeId memberTypeId = TypeId.get(Member.class); - private static final TypeId callbackTypeId = TypeId.get(XC_MethodHook.class); - private static final TypeId hookInfoTypeId - = TypeId.get(XposedBridge.AdditionalHookInfo.class); - private static final TypeId callbacksTypeId - = TypeId.get(XposedBridge.CopyOnWriteSortedSet.class); - private static final TypeId paramTypeId - = TypeId.get(XC_MethodHook.MethodHookParam.class); - private static final MethodId setResultMethodId = - paramTypeId.getMethod(TypeId.VOID, "setResult", TypeId.OBJECT); - private static final MethodId setThrowableMethodId = - paramTypeId.getMethod(TypeId.VOID, "setThrowable", throwableTypeId); - private static final MethodId getResultMethodId = - paramTypeId.getMethod(TypeId.OBJECT, "getResult"); - private static final MethodId getThrowableMethodId = - paramTypeId.getMethod(throwableTypeId, "getThrowable"); - private static final MethodId hasThrowableMethodId = - paramTypeId.getMethod(TypeId.BOOLEAN, "hasThrowable"); - private static final MethodId callAfterCallbackMethodId = - callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_AFTER, paramTypeId); - private static final MethodId callBeforeCallbackMethodId = - callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_BEFORE, paramTypeId); - private static final FieldId returnEarlyFieldId = - paramTypeId.getField(TypeId.BOOLEAN, "returnEarly"); - private static final TypeId xposedBridgeTypeId = TypeId.get(XposedBridge.class); - private static final MethodId logThrowableMethodId = - xposedBridgeTypeId.getMethod(TypeId.VOID, "log", throwableTypeId); - private static final MethodId logStrMethodId = - xposedBridgeTypeId.getMethod(TypeId.VOID, "log", TypeId.STRING); + private static final String CLASS_NAME_PREFIX = "LspHooker_"; + private static final String FIELD_NAME_HOOKER = "hooker"; + private static final TypeId hookerTypeId = TypeId.get(LspHooker.class); + private static final MethodId handleHookedMethodMethodId = + hookerTypeId.getMethod(TypeId.OBJECT, "handleHookedMethod", objArrayTypeId); - private static AtomicLong sClassNameSuffix = new AtomicLong(1); - - private FieldId mHookInfoFieldId; - private FieldId mMethodFieldId; - private MethodId mBackupMethodId; - private MethodId mCallBackupMethodId; - private MethodId mHookMethodId; + private FieldId mHookerFieldId; private TypeId mHookerTypeId; private TypeId[] mParameterTypeIds; private Class[] mActualParameterTypes; - private Class mReturnType; private TypeId mReturnTypeId; - private boolean mIsStatic; - // TODO use this to generate methods - private boolean mHasThrowable; private DexMaker mDexMaker; private Member mMember; private XposedBridge.AdditionalHookInfo mHookInfo; private ClassLoader mAppClassLoader; - private Class mHookClass; - private Method mHookMethod; - private Method mBackupMethod; - private Method mCallBackupMethod; + private LspHooker mHooker; private static TypeId[] getParameterTypeIds(Class[] parameterTypes, boolean isStatic) { int parameterSize = parameterTypes.length; @@ -144,29 +92,27 @@ public class HookerDexMaker { public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, ClassLoader appClassLoader) throws Exception { + Class returnType; + boolean isStatic; if (member instanceof Method) { Method method = (Method) member; - mIsStatic = Modifier.isStatic(method.getModifiers()); - mReturnType = method.getReturnType(); - if (mReturnType.equals(Void.class) || mReturnType.equals(void.class) - || mReturnType.isPrimitive()) { - mReturnTypeId = TypeId.get(mReturnType); + isStatic = Modifier.isStatic(method.getModifiers()); + returnType = method.getReturnType(); + if (returnType.equals(Void.class) || returnType.equals(void.class) + || returnType.isPrimitive()) { + mReturnTypeId = TypeId.get(returnType); } else { // all others fallback to plain Object for convenience - mReturnType = Object.class; mReturnTypeId = TypeId.OBJECT; } - mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic); - mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic); - mHasThrowable = method.getExceptionTypes().length > 0; + mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), isStatic); + mActualParameterTypes = getParameterTypes(method.getParameterTypes(), isStatic); } else if (member instanceof Constructor) { Constructor constructor = (Constructor) member; - mIsStatic = false; - mReturnType = void.class; + isStatic = false; mReturnTypeId = TypeId.VOID; - mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic); - mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic); - mHasThrowable = constructor.getExceptionTypes().length > 0; + mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), isStatic); + mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), isStatic); } else if (member.getDeclaringClass().isInterface()) { throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString()); } else if (Modifier.isAbstract(member.getModifiers())) { @@ -186,6 +132,7 @@ public class HookerDexMaker { doMake(member.getDeclaringClass().getName()); } + @SuppressWarnings("ResultOfMethodCallIgnored") @TargetApi(Build.VERSION_CODES.O) private void doMake(String hookedClassName) throws Exception { mDexMaker = new DexMaker(); @@ -225,15 +172,13 @@ public class HookerDexMaker { 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. - mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class) - .invoke(null, mMember, mHookInfo); - mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); - mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); - mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); - Yahfa.setMethodNonCompilable(mCallBackupMethod); - HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod); + Method backupMethod = hookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); + mHooker = new LspHooker(mHookInfo, mMember, backupMethod); + hookClass.getMethod(METHOD_NAME_SETUP, LspHooker.class).invoke(null, mHooker); + Method hookMethod = hookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); + HookMain.backupAndHook(mMember, hookMethod, backupMethod); } private void doGenerate(String className) { @@ -244,49 +189,30 @@ public class HookerDexMaker { generateSetupMethod(); generateBackupMethod(); generateHookMethod(); - generateCallBackupMethod(); } - public Method getHookMethod() { - return mHookMethod; - } - - public Method getBackupMethod() { - return mBackupMethod; - } - - public Method getCallBackupMethod() { - return mCallBackupMethod; - } - - public Class getHookClass() { - return mHookClass; - } + public LspHooker getHooker() {return mHooker;} private void generateFields() { - mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO); - mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD); - mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null); - mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null); + mHookerFieldId = mHookerTypeId.getField(hookerTypeId, FIELD_NAME_HOOKER); + mDexMaker.declare(mHookerFieldId, Modifier.STATIC, null); } private void generateSetupMethod() { MethodId 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); // init logic // get parameters - Local method = code.getParameter(0, memberTypeId); - Local hookInfo = code.getParameter(1, hookInfoTypeId); + Local hooker = code.getParameter(0, hookerTypeId); // save params to static - code.sput(mMethodFieldId, method); - code.sput(mHookInfoFieldId, hookInfo); + code.sput(mHookerFieldId, hooker); code.returnVoid(); } private void generateBackupMethod() { - mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds); - Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC); + MethodId backupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds); + Code code = mDexMaker.declare(backupMethodId, Modifier.PUBLIC | Modifier.STATIC); Map resultLocals = createResultLocals(code); // do nothing 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 resultLocals = createResultLocals(code); - if (mReturnTypeId.equals(TypeId.VOID)) { - code.invokeStatic(mBackupMethodId, null, allArgsLocals); - code.returnVoid(); - } else { - Local result = resultLocals.get(mReturnTypeId); - code.invokeStatic(mBackupMethodId, result, allArgsLocals); - code.returnValue(result); - } - } - private void generateHookMethod() { - mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds); - Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC); + MethodId hookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds); + Code code = mDexMaker.declare(hookMethodId, Modifier.PUBLIC | Modifier.STATIC); // code starts - - // prepare common labels - Label noHookReturn = new Label(); - Label incrementAndCheckBefore = new Label(); - Label tryBeforeCatch = new Label(); - Label noExceptionBefore = new Label(); - Label checkAndCallBackup = new Label(); - Label beginCallBefore = new Label(); - Label beginCallAfter = new Label(); - Label tryOrigCatch = new Label(); - Label noExceptionOrig = new Label(); - Label tryAfterCatch = new Label(); - Label decrementAndCheckAfter = new Label(); - Label noBackupThrowable = new Label(); - Label throwThrowable = new Label(); // prepare locals - Local disableHooks = code.newLocal(TypeId.BOOLEAN); - Local hookInfo = code.newLocal(hookInfoTypeId); - Local callbacks = code.newLocal(callbacksTypeId); - Local snapshot = code.newLocal(objArrayTypeId); - Local snapshotLen = code.newLocal(TypeId.INT); - Local callbackObj = code.newLocal(TypeId.OBJECT); - Local callback = code.newLocal(callbackTypeId); - + Local hooker = code.newLocal(hookerTypeId); Local resultObj = code.newLocal(TypeId.OBJECT); // as a temp Local - Local one = code.newLocal(TypeId.INT); - Local nullObj = code.newLocal(TypeId.OBJECT); - Local throwable = code.newLocal(throwableTypeId); - - Local param = code.newLocal(paramTypeId); - Local method = code.newLocal(memberTypeId); - Local thisObject = code.newLocal(TypeId.OBJECT); Local args = code.newLocal(objArrayTypeId); - Local returnEarly = code.newLocal(TypeId.BOOLEAN); Local actualParamSize = code.newLocal(TypeId.INT); Local argIndex = code.newLocal(TypeId.INT); - Local beforeIdx = code.newLocal(TypeId.INT); - Local lastResult = code.newLocal(TypeId.OBJECT); - Local lastThrowable = code.newLocal(throwableTypeId); - Local hasThrowable = code.newLocal(TypeId.BOOLEAN); - Local[] allArgsLocals = createParameterLocals(code); Map resultLocals = createResultLocals(code); code.loadConstant(args, null); code.loadConstant(argIndex, 0); - code.loadConstant(one, 1); - code.loadConstant(snapshotLen, 0); - code.loadConstant(nullObj, null); - // check XposedBridge.disableHooks flag - - FieldId disableHooksField = - xposedBridgeTypeId.getField(TypeId.BOOLEAN, "disableHooks"); - code.sget(disableHooksField, disableHooks); - // disableHooks == true => no hooking - code.compareZ(Comparison.NE, noHookReturn, disableHooks); - - // check callbacks length - code.sget(mHookInfoFieldId, hookInfo); - code.iget(hookInfoTypeId.getField(callbacksTypeId, "callbacks"), callbacks, hookInfo); - code.invokeVirtual(callbacksTypeId.getMethod(objArrayTypeId, "getSnapshot"), snapshot, callbacks); - code.arrayLength(snapshotLen, snapshot); - // snapshotLen == 0 => no hooking - code.compareZ(Comparison.EQ, noHookReturn, snapshotLen); + code.sget(mHookerFieldId, hooker); // start hooking - // prepare hooking locals int paramsSize = mParameterTypeIds.length; - int offset = 0; - // thisObject - if (mIsStatic) { - // thisObject = null - code.loadConstant(thisObject, null); - } else { - // thisObject = args[0] - offset = 1; - code.move(thisObject, allArgsLocals[0]); - } - // actual args (exclude thisObject if this is not a static method) - code.loadConstant(actualParamSize, paramsSize - offset); + code.loadConstant(actualParamSize, paramsSize); code.newArray(args, actualParamSize); - for (int i = offset; i < paramsSize; i++) { + for (int i = 0; i < paramsSize; i++) { Local parameter = allArgsLocals[i]; // save parameter to resultObj as Object autoBoxIfNecessary(code, resultObj, parameter); - code.loadConstant(argIndex, i - offset); + code.loadConstant(argIndex, i); // save Object to args code.aput(args, argIndex, resultObj); } - // create param - code.newInstance(param, paramTypeId.getConstructor()); - // set method, thisObject, args - code.sget(mMethodFieldId, method); - code.iput(paramTypeId.getField(memberTypeId, "method"), param, method); - code.iput(paramTypeId.getField(TypeId.OBJECT, "thisObject"), param, thisObject); - code.iput(paramTypeId.getField(objArrayTypeId, "args"), param, args); - - // call beforeCallbacks - code.loadConstant(beforeIdx, 0); - - code.mark(beginCallBefore); - // start of try - code.addCatchClause(throwableTypeId, tryBeforeCatch); - - code.aget(callbackObj, snapshot, beforeIdx); - code.cast(callback, callbackObj); - code.invokeVirtual(callBeforeCallbackMethodId, null, callback, param); - code.jump(noExceptionBefore); - - // end of try - code.removeCatchClause(throwableTypeId); - - // start of catch - code.mark(tryBeforeCatch); - code.moveException(throwable); - code.invokeStatic(logThrowableMethodId, null, throwable); - code.invokeVirtual(setResultMethodId, null, param, nullObj); - code.loadConstant(returnEarly, false); - code.iput(returnEarlyFieldId, param, returnEarly); - code.jump(incrementAndCheckBefore); - - // no exception when calling beforeCallbacks - code.mark(noExceptionBefore); - code.iget(returnEarlyFieldId, returnEarly, param); - // if returnEarly == false, continue - code.compareZ(Comparison.EQ, incrementAndCheckBefore, returnEarly); - // returnEarly == true, break - code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one); - code.jump(checkAndCallBackup); - - // increment and check to continue - code.mark(incrementAndCheckBefore); - code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one); - code.compare(Comparison.LT, beginCallBefore, beforeIdx, snapshotLen); - - // check and call backup - code.mark(checkAndCallBackup); - code.iget(returnEarlyFieldId, returnEarly, param); - // if returnEarly == true, go to call afterCallbacks directly - code.compareZ(Comparison.NE, noExceptionOrig, returnEarly); - // try to call backup - // try start - code.addCatchClause(throwableTypeId, tryOrigCatch); - // we have to load args[] to paramLocals - // because args[] may be changed in beforeHookedMethod - // should consider first param is thisObj if hooked method is not static - offset = mIsStatic ? 0 : 1; - for (int i = offset; i < allArgsLocals.length; i++) { - code.loadConstant(argIndex, i - offset); - code.aget(resultObj, args, argIndex); - autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true); - } - // get pre-created Local with a matching typeId - if (mReturnTypeId.equals(TypeId.VOID)) { - code.invokeStatic(mBackupMethodId, null, allArgsLocals); - // TODO maybe keep preset result to do some magic? - code.invokeVirtual(setResultMethodId, null, param, nullObj); - } else { - Local returnedResult = resultLocals.get(mReturnTypeId); - code.invokeStatic(mBackupMethodId, returnedResult, allArgsLocals); - // save returnedResult to resultObj as a Object - autoBoxIfNecessary(code, resultObj, returnedResult); - // save resultObj to param - code.invokeVirtual(setResultMethodId, null, param, resultObj); - } - // go to call afterCallbacks - code.jump(noExceptionOrig); - // try end - code.removeCatchClause(throwableTypeId); - // catch - code.mark(tryOrigCatch); - code.moveException(throwable); - // exception occurred when calling backup, save throwable to param - code.invokeVirtual(setThrowableMethodId, null, param, throwable); - - code.mark(noExceptionOrig); - code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one); - - // call afterCallbacks - code.mark(beginCallAfter); - // save results of backup calling - code.invokeVirtual(getResultMethodId, lastResult, param); - code.invokeVirtual(getThrowableMethodId, lastThrowable, param); - // try start - code.addCatchClause(throwableTypeId, tryAfterCatch); - code.aget(callbackObj, snapshot, beforeIdx); - code.cast(callback, callbackObj); - code.invokeVirtual(callAfterCallbackMethodId, null, callback, param); - // all good, just continue - code.jump(decrementAndCheckAfter); - // try end - code.removeCatchClause(throwableTypeId); - // catch - code.mark(tryAfterCatch); - code.moveException(throwable); - code.invokeStatic(logThrowableMethodId, null, throwable); - // if lastThrowable == null, go to recover lastResult - code.compareZ(Comparison.EQ, noBackupThrowable, lastThrowable); - // lastThrowable != null, recover lastThrowable - code.invokeVirtual(setThrowableMethodId, null, param, lastThrowable); - // continue - code.jump(decrementAndCheckAfter); - code.mark(noBackupThrowable); - // recover lastResult and continue - code.invokeVirtual(setResultMethodId, null, param, lastResult); - // decrement and check continue - code.mark(decrementAndCheckAfter); - code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one); - code.compareZ(Comparison.GE, beginCallAfter, beforeIdx); - - // callbacks end - // return - code.invokeVirtual(hasThrowableMethodId, hasThrowable, param); - // if hasThrowable, throw the throwable and return - code.compareZ(Comparison.NE, throwThrowable, hasThrowable); - // return getResult + // handleHookedMethod if (mReturnTypeId.equals(TypeId.VOID)) { + code.invokeVirtual(handleHookedMethodMethodId, null, hooker, args); code.returnVoid(); } else { - // getResult always return an Object, so save to resultObj - code.invokeVirtual(getResultMethodId, resultObj, param); + // hooker always return an Object, so save to resultObj + code.invokeVirtual(handleHookedMethodMethodId, resultObj, hooker, args); // have to unbox it if returnType is primitive // casting Object TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId); @@ -555,21 +276,6 @@ public class HookerDexMaker { // return code.returnValue(toReturn); } - // throw throwable - code.mark(throwThrowable); - code.invokeVirtual(getThrowableMethodId, throwable, param); - code.throwValue(throwable); - - // call backup and return - code.mark(noHookReturn); - if (mReturnTypeId.equals(TypeId.VOID)) { - code.invokeStatic(mBackupMethodId, null, allArgsLocals); - code.returnVoid(); - } else { - Local result = resultLocals.get(mReturnTypeId); - code.invokeStatic(mBackupMethodId, result, allArgsLocals); - code.returnValue(result); - } } private Local[] createParameterLocals(Code code) { diff --git a/edxp-core/src/main/java/de/robv/android/xposed/LspHooker.java b/edxp-core/src/main/java/de/robv/android/xposed/LspHooker.java new file mode 100644 index 00000000..b6418b94 --- /dev/null +++ b/edxp-core/src/main/java/de/robv/android/xposed/LspHooker.java @@ -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(); + } + +}