精华内容
下载资源
问答
  • 小肩膀安卓逆向Xposed HOOK工具AS3.3_settings.jar
  • Xposed Hook专题

    2020-09-24 15:54:39
    Xposed Hook模块开发教程,从Xposed框架安装,到Xposed模块开发,详细讲解Xposed中使用到的类与方法,以及实战案例分析。
  • xposed hook系统对话框

    2018-12-11 16:30:55
    基于Android studio3.2.1写的 xposed hook系统对话框的插件。
  • 本文以微信版本6.7.3为例进行分析有hook, 大部分做微信机器人的话,首先要实时抓取微信的消息,在这里展示三种方式对微信的消息进行hook: 1.基于UI层拉取加载进行监听 2.基于微信dao层调用的保存进行监听 3.基于...
  • Xposed Hook技术

    2020-05-25 22:29:32
    Xposed思维导图,便捷整理思路,新建Android项目、导入jar包、java代码、assets文件夹下xposed_init、配置AndroidManifest.xml文件
  • XposedHook,免.zip

    2019-09-26 09:48:55
    这种方案建议只在开发阶段调试的时候使用,因为这将损耗一些性能(需要额外加载 apk 文件),调试没问题后,直接修改 xposed_init 配置文件指向目标类即可。
  • Android Hook 系列教程(一) Xposed Hook 原理分析二. Android Hook 系列教程(二) 自己写APK实现Hook Java层函数三. Android Hook 系列教程(三) Cydia Hook Native 原理分析四. Android Hook 系列教程(四) 自己写APK...

    章节内容

    一. Android Hook 系列教程(一) Xposed Hook 原理分析

    二. Android Hook 系列教程(二) 自己写APK实现Hook Java层函数

    三. Android Hook 系列教程(三) Cydia Hook Native 原理分析

    四. Android Hook 系列教程(四) 自己写APK实现Hook Native层函数

    五. Android Hook 系列教程(五) 更多Hook方法

    六. Andoird Hook 系列教程(六) Hook的总结

    源代码下载

    Java层:https://github.com/rovo89/XposedBridge

    Native层:https://github.com/rovo89/Xposed

    开始

    一个好的开始等于成功了一半.

    为了分析Xposed Hook是怎么实现的,选取了findAndHookMethod函数作为研究对象.

    在分析过程中,我们应该做到有详有略,我将以关键代码(略过对分析目的无关紧要的部分,留下必须部分).

    当然在关键代码后面会贴出全部代码以供参照.

    Jave层分析

    findAndHookMethod

    从源代码:XposedHelpers.java处找到findAndHookMethod

    findAndHookMethod关键代码

    public static XC_MethodHook.Unhook findAndHookMethod(Class> clazz, String methodName, Object... parameterTypesAndCallback) {

    ...

    XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length-1];//获取回调函数

    Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));//获取Method

    return XposedBridge.hookMethod(m, callback);

    }

    public static XC_MethodHook.Unhook findAndHookMethod(Class> clazz, String methodName, Object... parameterTypesAndCallback) {

    if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof XC_MethodHook))

    throw new IllegalArgumentException("no callback defined");

    XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length-1];//获取回调函数

    Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));//获取Method

    return XposedBridge.hookMethod(m, callback);

    }

    上面的代码都很容易懂,简单说一下几个函数

    getParameterClasses

    功能:把函数所有参数转换为Class>数组返回.

    比如参数列表为

    String.class,int.class,"java.util.Map",回调函数

    返回结果相当于

    Class> []={

    String.class,

    int.class,

    Map.class

    };

    findMethodExact

    功能:根据方法名和方法参数类获取Method

    关键代码:

    public static Method findMethodExact(Class> clazz, String methodName, Class>... parameterTypes) {

    ...

    Method method = clazz.getDeclaredMethod(methodName, parameterTypes);//获取方法声明

    method.setAccessible(true);

    methodCache.put(fullMethodName, method);

    return method;

    }

    完整代码:

    public static Method findMethodExact(Class> clazz, String methodName, Class>... parameterTypes) {

    String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#exact";

    if (methodCache.containsKey(fullMethodName)) {

    Method method = methodCache.get(fullMethodName);

    if (method == null)

    throw new NoSuchMethodError(fullMethodName);

    return method;

    }

    try {

    Method method = clazz.getDeclaredMethod(methodName, parameterTypes);//获取方法声明

    method.setAccessible(true);

    methodCache.put(fullMethodName, method);

    return method;

    } catch (NoSuchMethodException e) {

    methodCache.put(fullMethodName, null);

    throw new NoSuchMethodError(fullMethodName);

    }

    }

    让我们继续跟进hookMethod

    hookMethod

    功能:把方法的参数类型,返回类型,回调函数记录到AdditionalHookInfo类里

    并通过hookMethodNative方法进入Native层进行Hook.

    在这个方法里面差不多都是关键代码,也就不省略,全部贴出.

    ​ 完整代码:

    public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {

    /*检测是否是支持的Method类型*/

    if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor>)) {

    throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());

    } else if (hookMethod.getDeclaringClass().isInterface()) {

    throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());

    } else if (Modifier.isAbstract(hookMethod.getModifiers())) {

    throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());

    }

    boolean newMethod = false;

    CopyOnWriteSortedSet callbacks;

    synchronized (sHookedMethodCallbacks) {

    callbacks = sHookedMethodCallbacks.get(hookMethod);

    if (callbacks == null) {//如果为null则函数没有被Hook,否则已经Hook

    callbacks = new CopyOnWriteSortedSet<>();

    sHookedMethodCallbacks.put(hookMethod, callbacks);

    newMethod = true;

    }

    }

    callbacks.add(callback);//记录回调函数

    if (newMethod) {//建立新Hook

    Class> declaringClass = hookMethod.getDeclaringClass();//获取类声明

    int slot;

    Class>[] parameterTypes;

    Class> returnType;

    if (runtime == RUNTIME_ART) {//判断是否是ART模式

    slot = 0;

    parameterTypes = null;

    returnType = null;

    } else if (hookMethod instanceof Method) {//实例方法

    slot = getIntField(hookMethod, "slot");//获取slot

    parameterTypes = ((Method) hookMethod).getParameterTypes();//获取参数类型Class>数组 和前面相同

    returnType = ((Method) hookMethod).getReturnType();//获取返回类型

    } else {//构造函数

    slot = getIntField(hookMethod, "slot");//获取slot

    parameterTypes = ((Constructor>) hookMethod).getParameterTypes();//获取参数类型Class>数组 和前面相同

    returnType = null;//构造参数返回类型为null

    }

    //把回调函数,参数类型,返回类型 记录在一个AdditionalHookInfo类里

    AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);

    //Native方法

    hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);

    }

    return callback.new Unhook(hookMethod);

    }

    看一下AdditionalHookInfo构造函数

    private AdditionalHookInfo(CopyOnWriteSortedSet callbacks, Class>[] parameterTypes, Class> returnType) {

    this.callbacks = callbacks;

    this.parameterTypes = parameterTypes;

    this.returnType = returnType;

    }

    Native层分析

    hookMethodNativevoid XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,

    jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {

    // Usage errors?

    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {

    dvmThrowIllegalArgumentException("method and declaredClass must not be null");

    return;

    }

    // Find the internal representation of the method

    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);

    Method* method = dvmSlotToMethod(declaredClass, slot);

    if (method == NULL) {

    dvmThrowNoSuchMethodError("Could not get internal representation for method");

    return;

    }

    if (isMethodHooked(method)) {

    // already hooked

    return;

    }

    // Save a copy of the original method and other hook info

    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));

    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));

    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));

    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    // Replace method with our own code

    SET_METHOD_FLAG(method, ACC_NATIVE);

    method->nativeFunc = &hookedMethodCallback;

    method->insns = (const u2*) hookInfo;

    method->registersSize = method->insSize;

    method->outsSize = 0;

    if (PTR_gDvmJit != NULL) {

    // reset JIT cache

    char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));

    if (currentValue == 0 || currentValue == 1) {

    MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;

    } else {

    ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);

    }

    }

    }

    详细分析:

    得到Native层的Method

    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);

    Method* method = dvmSlotToMethod(declaredClass, slot);

    从Android源代码的Dalvik虚拟机代码里找到dvmSlotToMethod实现如下

    实现非常简单,所以slot相等于记录Method的下标

    Method* dvmSlotToMethod(ClassObject* clazz, int slot)

    {

    if (slot < 0) {

    slot = -(slot+1);

    assert(slot < clazz->directMethodCount);

    return &clazz->directMethods[slot];

    } else {

    assert(slot < clazz->virtualMethodCount);

    return &clazz->virtualMethods[slot];

    }

    }

    Method结构

    struct Method {

    /* the class we are a part of */

    ClassObject* clazz;

    /* access flags; low 16 bits are defined by spec (could be u2?) */

    u4 accessFlags;

    /*

    * For concrete virtual methods, this is the offset of the method

    * in "vtable".

    *

    * For abstract methods in an interface class, this is the offset

    * of the method in "iftable[n]->methodIndexArray".

    */

    u2 methodIndex;

    /*

    * Method bounds; not needed for an abstract method.

    *

    * For a native method, we compute the size of the argument list, and

    * set "insSize" and "registerSize" equal to it.

    */

    u2 registersSize; /* ins + locals */

    u2 outsSize;

    u2 insSize;

    /* method name, e.g. "" or "eatLunch" */

    const char* name;

    /*

    * Method prototype descriptor string (return and argument types).

    *

    * TODO: This currently must specify the DexFile as well as the proto_ids

    * index, because generated Proxy classes don't have a DexFile. We can

    * remove the DexFile* and reduce the size of this struct if we generate

    * a DEX for proxies.

    */

    DexProto prototype;

    /* short-form method descriptor string */

    const char* shorty;

    /*

    * The remaining items are not used for abstract or native methods.

    * (JNI is currently hijacking "insns" as a function pointer, set

    * after the first call. For internal-native this stays null.)

    */

    /* the actual code */

    const u2* insns; /* instructions, in memory-mapped .dex */

    /* JNI: cached argument and return-type hints */

    int jniArgInfo;

    /*

    * JNI: native method ptr; could be actual function or a JNI bridge. We

    * don't currently discriminate between DalvikBridgeFunc and

    * DalvikNativeFunc; the former takes an argument superset (i.e. two

    * extra args) which will be ignored. If necessary we can use

    * insns==NULL to detect JNI bridge vs. internal native.

    */

    DalvikBridgeFunc nativeFunc;

    /*

    * JNI: true if this static non-synchronized native method (that has no

    * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore

    * uses this.

    */

    bool fastJni;

    /*

    * JNI: true if this method has no reference arguments. This lets the JNI

    * bridge avoid scanning the shorty for direct pointers that need to be

    * converted to local references.

    *

    * TODO: replace this with a list of indexes of the reference arguments.

    */

    bool noRef;

    /*

    * JNI: true if we should log entry and exit. This is the only way

    * developers can log the local references that are passed into their code.

    * Used for debugging JNI problems in third-party code.

    */

    bool shouldTrace;

    /*

    * Register map data, if available. This will point into the DEX file

    * if the data was computed during pre-verification, or into the

    * linear alloc area if not.

    */

    const RegisterMap* registerMap;

    /* set if method was called during method profiling */

    bool inProfile;

    };

    保存一些Hook信息

    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));

    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));

    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));

    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    替换方法(最关键的一步)

    SET_METHOD_FLAG(method, ACC_NATIVE);

    method->nativeFunc = &hookedMethodCallback;//替换方法为这个

    method->insns = (const u2*) hookInfo;

    method->registersSize = method->insSize;

    method->outsSize = 0;

    到这里我们已经分析完了,就是保存一些信息,并把方法替换为hookedMethodCallback.

    hookedMethodCallbackvoid hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {

    if (!isMethodHooked(method)) {

    dvmThrowNoSuchMethodError("Could not find Xposed original method - how did you even get here?");

    return;

    }

    XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns;

    Method* original = (Method*) hookInfo;

    Object* originalReflected = hookInfo->reflectedMethod;

    Object* additionalInfo = hookInfo->additionalInfo;

    // convert/box arguments

    const char* desc = &method->shorty[1]; // [0] is the return type.

    Object* thisObject = NULL;

    size_t srcIndex = 0;

    size_t dstIndex = 0;

    // for non-static methods determine the "this" pointer

    if (!dvmIsStaticMethod(original)) {

    thisObject = (Object*) args[0];

    srcIndex++;

    }

    ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT);

    if (argsArray == NULL) {

    return;

    }

    //循环获取参数

    while (*desc != '\0') {

    char descChar = *(desc++);

    JValue value;

    Object* obj;

    switch (descChar) {

    case 'Z':

    case 'C':

    case 'F':

    case 'B':

    case 'S':

    case 'I':

    value.i = args[srcIndex++];

    obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));

    dvmReleaseTrackedAlloc(obj, self);

    break;

    case 'D':

    case 'J':

    value.j = dvmGetArgLong(args, srcIndex);

    srcIndex += 2;

    obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));

    dvmReleaseTrackedAlloc(obj, self);

    break;

    case '[':

    case 'L':

    obj = (Object*) args[srcIndex++];

    break;

    default:

    ALOGE("Unknown method signature description character: %c", descChar);

    obj = NULL;

    srcIndex++;

    }

    setObjectArrayElement(argsArray, dstIndex++, obj);//把获取的参数加入数组

    }

    // call the Java handler function

    JValue result;

    dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,

    originalReflected, (int) original, additionalInfo, thisObject, argsArray);

    dvmReleaseTrackedAlloc(argsArray, self);

    // exceptions are thrown to the caller

    if (dvmCheckException(self)) {

    return;

    }

    // return result with proper type

    ClassObject* returnType = dvmGetBoxedReturnType(method);

    if (returnType->primitiveType == PRIM_VOID) {

    // ignored

    } else if (result.l == NULL) {

    if (dvmIsPrimitiveClass(returnType)) {

    dvmThrowNullPointerException("null result when primitive expected");

    }

    pResult->l = NULL;

    } else {

    if (!dvmUnboxPrimitive(result.l, returnType, pResult)) {

    dvmThrowClassCastException(result.l->clazz, returnType);

    }

    }

    }

    void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,

    jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {

    // Usage errors?

    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {

    dvmThrowIllegalArgumentException("method and declaredClass must not be null");

    return;

    }

    // Find the internal representation of the method

    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);

    Method* method = dvmSlotToMethod(declaredClass, slot);

    if (method == NULL) {

    dvmThrowNoSuchMethodError("Could not get internal representation for method");

    return;

    }

    if (isMethodHooked(method)) {

    // already hooked

    return;

    }

    // Save a copy of the original method and other hook info

    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));

    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));

    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));

    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    // Replace method with our own code

    SET_METHOD_FLAG(method, ACC_NATIVE);

    method->nativeFunc = &hookedMethodCallback;

    method->insns = (const u2*) hookInfo;

    method->registersSize = method->insSize;

    method->outsSize = 0;

    if (PTR_gDvmJit != NULL) {

    // reset JIT cache

    char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));

    if (currentValue == 0 || currentValue == 1) {

    MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;

    } else {

    ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);

    }

    }

    }

    相信到了这里读者已经可以根据注释自己看懂了,我在把流程梳理一下

    获取原先保存的Hook信息

    获取参数

    调用Java层函数

    dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,

    originalReflected, (int) original, additionalInfo, thisObject, argsArray);

    dvmReleaseTrackedAlloc(argsArray, self);

    看一下参数methodXposedBridgeHandleHookedMethod是什么,可以看到是上层的Java

    #define CLASS_XPOSED_BRIDGE "de/robv/android/xposed/XposedBridge"

    classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);

    jmethodID methodXposedBridgeHandleHookedMethod = NULL;

    methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",

    "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

    可以看到调用Java层的函数为XposedBridge.handleHookedMethod

    Java层函数handleHookedMethod

    功能:

    调用Hook该方法的所有beforeHookedMethod

    调用原始方法

    调用Hook该方法的所有afterHookedMethod

    关键代码

    private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,

    Object thisObject, Object[] args) throws Throwable {

    AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;

    MethodHookParam param = new MethodHookParam();

    param.method = method;

    param.thisObject = thisObject;

    param.args = args;

    // 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) {

    try {

    param.setResult(invokeOriginalMethodNative(method, originalMethodId,

    additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));

    } catch (InvocationTargetException e) {

    param.setThrowable(e.getCause());

    }

    }

    // 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();

    }

    完整代码

    private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,

    Object thisObject, Object[] args) throws Throwable {

    AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;

    if (disableHooks) {

    try {

    return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,

    additionalInfo.returnType, thisObject, args);

    } catch (InvocationTargetException e) {

    throw e.getCause();

    }

    }

    Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();

    final int callbacksLength = callbacksSnapshot.length;

    if (callbacksLength == 0) {

    try {

    return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,

    additionalInfo.returnType, thisObject, args);

    } catch (InvocationTargetException e) {

    throw e.getCause();

    }

    }

    MethodHookParam param = new MethodHookParam();

    param.method = method;

    param.thisObject = thisObject;

    param.args = args;

    // 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) {

    try {

    param.setResult(invokeOriginalMethodNative(method, originalMethodId,

    additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));

    } catch (InvocationTargetException e) {

    param.setThrowable(e.getCause());

    }

    }

    // 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();

    }

    好了,到这里我们已经分析完成.

    简单总结一下:

    Java层:

    记录需要Hook方法的信息包括:参数数组,返回值类型,回调函数.并且记录在AdditionalHookInfo类里面

    获取slot,声明方法的类

    把这些信息通过Native函数:hookMethodNative进行Hook

    Native层

    替换Native层Method的字段nativeFunc为函数 hookedMethodCallback完成Hook

    hookedMethodCallback里回调Java层函数XposedBridge.handleHookedMethod.

    handleHookedMethod里分别调用beforeHookedMethod,原始方法,afterHookedMethod

    展开全文
  • Xposed hook原理

    2019-11-20 06:58:06
    Xposedhook原理就是这么简单,但它有其他的问题要解决:如何将hook的代码注入到目标app的进程中? Xposed的实现是依赖与root,重写android的zygote代码,加入自身的加载逻辑。zygote是android 系统最最初运行的...

    先来个总结

    java源码经过编译后,得到很多个class文件, 考虑到手机的内存较小,google改进了字节码的组织形式,将一个app中的所有class文件合到了一起构成dex文件,当然并不是简单的拼接在一起,而是遵从dex的格式重新组织。

    dex文件最终会和资源文件等一起打包成为apk,签名后安装到手机上。

    PackageManager在安装apk的时候,做了一件事:优化dex文件为odex,存放在/data/dalvik-cache目录下。

    dex文件是遵从于dalvik虚拟机标准的文件,它具有跨dalvik虚拟机的特点,而odex是在特定dalvik虚拟机上优化得到的,通常不能跨dalvik虚拟机运行。

    程序执行体现在方法的执行上,因为我们重点关注下方法的组织形式。

    在dex文件中,方法体里面的内容最终存储在classData区域,方法体里面存储的是二进制的字节码。

    在说字节码之前,先来说说什么是dalvik虚拟机,dalvik虚拟机说白了就是用c/c++写的一套复杂的程序,它定义了一堆的smali指令(256个),这些字节码指令高度抽象,组合这些指令可以完成我们想要的功能。你可以等同于汇编指令理解,但它们在语言级别要高于汇编(在虚拟机里面执行的,虚拟机又是c/c++写的,自然看出高于汇编)。

    字节码是用简单的二进制数字表示的,与可阅读的smali指令存在对应关系,具体对应方法参考google的官方文档:
    http://source.android.com/devices/tech/dalvik/instruction-formats.htmlhttp://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

    虚拟机又是如何执行这些字节码的呢?
    答案在AOSP中(开源就是好),原来虚拟机对于每一个字节码,都写了一段代码来解释执行(你可以等价理解为API调用一样,调用某个API,后面一堆逻辑来实现这个API),只是不同cpu结构,实现方式不一样,比如arm使用了arm汇编来实现,x86使用了x86的汇编来实现,google提供了一个c/c++的实现方式来兼容一些未知的cpu结构,见:
    https://android.googlesource.com/platform/dalvik2/+/master/vm/mterp/out/InterpC-portable.cpp


    虚拟机在加载了odex(虚拟机总是使用odex文件,第一次使用时会先生成odex), 会将整个odex文件的内容mmap到内存中,之后就和odex文件没有关系了。

    虚拟机在加载了odex(虚拟机总是使用odex文件,第一次使用时会先生成odex), 会将整个odex文件的内容mmap到内存中,之后就和odex文件没有关系了。

    虚拟机在load一个Class的时候(参见DexClassLoader源码),根据类的描述符,在内存中的odex区域,查询到对应的数据,构建出ClassObject对象,以及这个ClassObject关联的Method。

    Method分为两种,dalvik虚拟机在处理的时候有区别,一种是directMethod,即Java世界里面实现的方法,一种是nativeMethod,即在c/c++里面实现的方法。

     

    一些apk加固厂商就是在这块做的手脚,这里衍生开来说一下:
    梆梆加固的实现方式为:将原始dex中的内容加密处理,在app运行时,解密出dex,mmap到内存,还原了内存结构。
    爱加密的方法则是,将方法体里面的字节码从dex中抠出来,加密到了自己的so中,在app运行时,从so中解密出方法体,然后修改mmap对应的内存,还原内存结构。

    这些加固方法说白了就是将原始dex做加固处理,在运行时还原内存结构。所以光从静态分析反编译加固后的dex文件,将得不到有用信息。

    但有一个基于xposed的zjdroid脱壳工具,可以在运行时dump出内存(odex结构的内存),保存为本地odex文件,再利用smali/baksmali还原出原始dex文件。

    Xposed hook原理

    在Android系统中,应用程序进程都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的。Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例。这也是Xposed选择替换app_process的原因。

    Zygote进程在启动的过程中,除了会创建一个Dalvik虚拟机实例之外,还会将Java运行时库加载到进程中来,以及注册一些Android核心类的JNI方法来前面创建的Dalvik虚拟机实例中去。注意,一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库。这也就是可以将XposedBridge这个jar包加载到每一个Android应用程序中的原因。XposedBridge有一个私有的Native(JNI)方法hookMethodNative,这个方法也在app_process中使用。这个函数提供一个方法对象利用Java的Reflection机制来对内置方法覆写。具体的实现可以看下文的Xposed源代码分析。

     

    Xposed 框架中真正起作用的是对方法的hook。在Repackage技术中,如果要对APK做修改,则需要修改Smali代码中的指令。而另一种动态修改指令的技术需要在程序运行时基于匹配搜索来替换smali代码,但因为方法声明的多样性与复杂性,这种方法也比较复杂。

    在Android系统启动的时候,zygote进程加载XposedBridge将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,xposedCallHandler在转入handleHookedMethod这个Java方法执行用户规定的Hook Func。

    XposedBridge这个jar包含有一个私有的本地方法:hookMethodNative,该方法在附加的app_process程序中也得到了实现。它将一个方法对象作为输入参数(你可以使用Java的反射机制来获取这个方法)并且改变Dalvik虚拟机中对于该方法的定义。它将该方法的类型改变为native并且将这个方法的实现链接到它的本地的通用类的方法。换言之,当调用那个被hook的方法时候,通用的类方法会被调用而不会对调用者有任何的影响。在hookMethodNative的实现中,会调用XposedBridge中的handleHookedMethod这个方法来传递参数。handleHookedMethod这个方法类似于一个统一调度的Dispatch例程,其对应的底层的C++函数是xposedCallHandler。而handleHookedMethod实现里面会根据一个全局结构hookedMethodCallbacks来选择相应的hook函数,并调用他们的before, after函数。

     

    有前面这些知识后,再理解Xposed的hook原理就不难了。
    前面已经知道,一个java方法在虚拟机里面对应的Method为directMethod,其insns指向了字节码位置。

    Xposed在对java方法进行hook时,先将虚拟机里面这个方法的Method改为nativeMethod(其实就是一个标识字段),然后将该方法的nativeFunc指向自己实现的一个native方法,这样方法在调用时,就会调用到这个native方法,接管了控制权。

    在这个native方法中,xposed直接调用了一个java方法,这个java方法里面对原方法进行了调用,并在调用前后插入了钩子,于是就hook住了这个方法。

    Xposed的hook原理就是这么简单,但它有其他的问题要解决:如何将hook的代码注入到目标app的进程中?

    Xposed的实现是依赖与root,重写android的zygote代码,加入自身的加载逻辑。zygote是android 系统最最初运行的程序,之后的进程都是通过它fork(你把它理解为复制吧)出来的。 于是zygote中加载的代码,在所有fork出来的子进程都含有(app进程也是fork出来的)。 所以xposed是一个可以hook android系统中任意一个java方法的 hook框架。

    而淘宝根据xposed改造出来的dexposed,仅仅是注入hook代码的方式不同而已,hook逻辑完全一致。
    dexposed不依赖与root,但需要开发者主动集成进来(我们集合了别人的广告sdk,其实也是让别人的程序跑到我们的进程里面,所以得小心点,给我一个入口,我也能hook住你的任何方法)。所以其hok的范围仅仅是被集成的应用(对于淘宝的AOP框架定义,这个效果刚刚好)


    以上文章参考:https://www.jianshu.com/p/b29a21a162ad

    https://blog.csdn.net/wxyyxc1992/article/details/17320911

    展开全文
  • ···package com.example.xposedhook;import android.app.Application;import android.content.Context;import android.util.Log;import java.lang.reflect.Method;import de.robv.android.xposed....

    ···

    package com.example.xposedhook;

    import android.app.Application;

    import android.content.Context;

    import android.util.Log;

    import java.lang.reflect.Method;

    import de.robv.android.xposed.IXposedHookLoadPackage;

    import de.robv.android.xposed.XC_MethodHook;

    import de.robv.android.xposed.XposedBridge;

    import de.robv.android.xposed.XposedHelpers;

    import de.robv.android.xposed.callbacks.XC_LoadPackage;

    public class Hook implements IXposedHookLoadPackage {

    @Override

    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lppararm) throws Throwable {

    if (lppararm.packageName.equals("xxx.xxxx.xxxx")) {

    XposedBridge.log("start hook");

    // Class clazz = lppararm.classLoader.loadClass("com.xxxx.xxx.xxx.xxx.xxx");

    // XposedHelpers.findAndHookMethod(clazz, "invoke", new XC_MethodHook() {

    //

    // protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

    //

    // super.beforeHookedMethod(param);

    //

    // Log.d("cxa"," has Hooked!");

    //

    // }

    //

    // protected void afterHookedMethod(MethodHookParam param) throws Throwable {

    // Object[] ars=(Object[])param.args[2];

    // Log.d("cxa",ars.toString());

    //

    // }

    //

    // });

    //hook 多dex

    XposedHelpers.findAndHookMethod(Application.class, "attach",

    Context.class, new XC_MethodHook() {

    @Override

    protected void afterHookedMethod(MethodHookParam param) throws Throwable {

    ClassLoader cl = ((Context) param.args[0]).getClassLoader();

    Class> hookclass = null;

    try {

    hookclass = cl.loadClass("com.kuaishou.android.security.kfree.a");

    } catch (Exception e) {

    Log.e("DEBUG", "load class error", e);

    return;

    }

    Log.i("DEBUG", "load success");

    XposedHelpers.findAndHookMethod(hookclass, "invoke", Object.class, Method.class, Object[].class,

    new XC_MethodHook() {

    //TODO: 相关hook操作

    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

    super.beforeHookedMethod(param);

    Log.d("cxa", " has Hooked!");

    }

    protected void afterHookedMethod(MethodHookParam param) throws Throwable {

    Object[] ars = (Object[]) param.args[2];

    Log.d("cxa", ars[0].toString());

    }

    });

    }

    });

    }

    }

    }

    ···

    展开全文
  • 在官方教程里找不到描述这种hook的方法, 亲测:使用美元符号连接内部类,见: 我不了解xposed(汗),不过Java类中的匿名类编译时是作为子类处理,并独立生成class文件的,例如SensorManager$1.class,建议可以从这...

    sensorManager.registerListener(new SensorEventListener() {

    @Override

    public void onSensorChanged(SensorEvent event) {

    }

    @Override

    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    }, sensorGravity, SensorManager.SENSOR_DELAY_NORMAL);

    怎么hook这里的SensorEventListener?

    目标是hook住android.hardware.SensorManager.registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs)的listener里的onSensorChanged(SensorEvent event)这个函数.

    XposedHelpers.findAndHookMethod("android.hardware.SensorManager", loadPackageParam.classLoader, "registerListener", SensorEvent.class, new XC_MethodHook() {

    @Override

    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

    SensorEventListener event = (SensorEventListener) param.args[0];

    }

    });

    在官方教程里找不到描述这种hook的方法,

    亲测:使用美元符号连接内部类,见:

    我不了解xposed(汗),不过Java类中的匿名类编译时是作为子类处理,并独立生成class文件的,例如SensorManager$1.class,建议可以从这个角度研究一下

    展开全文
  • 今天,在使用Xposed框架HOOK一个方法时,传入的参数类型是枚举类型,在解决的过程中,搜索了很多资料,现在就将解决的过程记录下来。 1.使用指定的类加载器查找类 Class<?> aClass= XposedHelpers.find...
  • // } 基于Xposed Hook框架查找和定位Android应用的按钮点击事件的代码位置的效果如下图所示: 说明:Xposed Hook的代码写的比较简单,还有很多需要优化和改进的地方,比如怎么提高Xposed Hook框架的Hook效率,怎么...
  • 在进行Android逆向分析的时候,经常需要进行动态调试栈回溯,查看Java函数的调用流程,Android的smali动态调试又不是很方便,因此使用Android的Java Hook的方法,打印Java函数调用堆栈信息辅助静态分析。package ...
  • 主要是 通过 在 4.4 dvm 虚拟机 Hook doLoad函数代码只是为了演示入门方法,在Android 4.4.4系统通过测试。实际存在一些已知问题,比如,因为权限控制第37行代码在高版本的Android系统会执行失败。下面 具体 这套...
  • xposed hook 简单教程

    千次阅读 2018-12-06 20:07:56
    7、新建hook入口类指向文件:在main目录下新建assets文件夹,并在文件夹下新建xposed_init文件,文件内容为Hook类的全路径(包名+类名)。 至此,hook类已编写完成,可安装到root手机内。   三、xposed...
  • ART模式下基于Xposed Hook开发脱壳工具

    千次阅读 2017-09-26 10:20:28
    import com.example.com.xposedhook.dexdump.R; public class MainActivity extends Activity { // static { // // // 加载动态库文件libhook.so // System.loadLibrary("hook"); // } private List...
  • //这个函数会在被hook的函数执行前执行 LOGI("beforeHook"); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param);//这个函数会在被hook的...
  • 最近在使用Xposed hook Android App时,在做一个需求的时候有点懵:如何获取到android.app.NotificationManager中的Context呢?我的代码如下:XposedHelpers.findAndHookMethod("android.app.NotificationManager",...
  • Xposed hook动态加载的class

    万次阅读 2018-06-07 14:04:49
    最近用Xposed hook QQ的一些class,总是hook不到。问题根源是ClassLoader不对,因为那几个class是动态加载进来的。通过先ClassLoader.loadCLass则可以很轻松的拿到想要的classLoader对象 XposedBridge....
  • Xposed_Hook示例_bbb.zip

    2019-08-28 16:11:42
    Xposed Hook 示例代码: Hook Method 并获取私有属性调用方法 可查看我相应博客
  • xposed hook所有类的所有函数

    千次阅读 2019-05-19 14:15:45
    package com.xiaojianbang.xposed; import android.util.Log; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_...import de.robv.android.xposed.XC_MethodHoo...
  • 前阵子刚从总公司撸了一台Google亲儿子Nexus 6,Root后并刷了Xposed,刚想爽一把,结果其中一个APP打开后就这幅德行了。 没办法,既然不让我爽,我自己亲自动手撸。 分析APK 最基本的分析可以用Android ...
  • Xposed Hook

    2021-03-22 18:54:19
    说明:仅供学习使用,请勿用于非法用途,若有侵权,请联系博主删除 作者:zhu6201976 安装包下载: 链接:...提取码:twrd 工具: ...3.Xposed Installer.apk ...1.Hook checkSN..
  • 背景 在对某加固应用开发xp模块中,发现模拟器环境下生效,到了真机环境无论怎么...所以选择目标类加载后的某一个类,hook 此类 ,再hook目标类。 XposedBridge.hookAllMethods(ClassLoader.class, "loadClass", new
  • Xposed hook 简单第一步hook前提:这里默认你打搭建好了环境1、创建xposed项目demo2、创建lib目录并添加xposedBrager.jar包:3、右键点击jar选择添加到依赖库:4、创建xposed demo类 ,并实现代码:5、编写HOOK代码,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,996
精华内容 1,598
关键字:

xposedhook