android hook 框架 xposed 如何实现挂钩

前面知道,安装xposed框架后,系统启动,执行init, init 再调用 app_process 程序,由于这时候 app_process 已经被换了,所以app_process 启动后先进入的是 xposedbridge.class 的 main 函数, 这个函数最后才进入标准的 zygoteInit.class 的 main 函数,在进入 zygote 之前,它调用了几个函数,初始化了xposed框架,下面逐个分析。

一. initNative

Xposed.cpp (xposed): {"initNative", "()Z", (void*)de_robv_android_xposed_XposedBridge_initNative},
XposedBridge.java (xposedbridge\src\de\robv\android\xposed):    if (initNative()) {
XposedBridge.java (xposedbridge\src\de\robv\android\xposed):    private native static boolean initNative();

XposedBridge.class 的 initNative 是一个 native 函数,真正的实现在 Xposed.cpp 里的  de_robv_android_xposed_XposedBridge_initNative

1. de_robv_android_xposed_XposedBridge_initNative(JNIEnv* env, jclass clazz)

 xposedHandleHookedMethod = (Method*) env->GetStaticMethodID(xposedClass, "handleHookedMethod",
        "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

首先,从xposedClass(即XposedBridge.class)类获取函数 handleHookedMethod, 这个函数是java函数,这里获取其对应的 Method 结构体指针,这样就可以在 native 里调用(jni的原理)。那么,这个 handleHookedMethod 是干嘛的呢,这个函数就是最终执行的挂钩函数,后面会分析。

Method* xposedInvokeOriginalMethodNative = (Method*) env->GetStaticMethodID(xposedClass, "invokeOriginalMethodNative",
        "(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");dvmSetNativeFunc(xposedInvokeOriginalMethodNative, de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative, NULL);

其次,从xposedClass(即XposedBridge.class)类获取函数  invokeOriginalMethodNative 函数的 Method 结构体指针,然后调用  dvmSetNativeFunc 为这个java函数设置其 jni 实现  de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative , 这样,调用 invokeOriginalMethodNative  函数其实执行的是后者。

objectArrayClass = dvmFindArrayClass("[Ljava/lang/Object;", NULL);
xresourcesClass = env->FindClass(XRESOURCES_CLASS);
    xresourcesClass = reinterpret_cast<jclass>(env->NewGlobalRef(xresourcesClass));
if (register_android_content_res_XResources(env) != JNI_OK) {}
xresourcesTranslateResId = env->GetStaticMethodID(xresourcesClass, "translateResId",
        "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");
xresourcesTranslateAttrId = env->GetStaticMethodID(xresourcesClass, "translateAttrId",
        "(Ljava/lang/String;Landroid/content/res/XResources;)I");

最后,获取其他一些java类或函数的标识

二, initXbridgeZygote

XposedBridge.class main 函数第二个重要的函数是 initXbridgeZygote

// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
        findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            。。。
}

首先,挂钩了 ActivityThread 类的  handleBindApplication 函数,这个函数是在android ams 系统创建新进程成功后在新进程内部调用的,挂钩这个函数,可以在新进程创建后做一些事情

这里调用了一个函数,实现了挂钩  findAndHookMethod 。这个函数定义在 XposedHelpers.java

XposedHelpers.class

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

        return XposedBridge.hookMethod(m, callback);
    }

这个函数找到类 clazz 的函数 methodName 所对应的 Method结构体,然后调用 XposedBridge 的 hookMethod 函数挂钩它

XposedBridge.class

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

        boolean newMethod = false;
        CopyOnWriteSortedSet<XC_MethodHook> callbacks;
        synchronized (sHookedMethodCallbacks) {
            callbacks = sHookedMethodCallbacks.get(hookMethod);
            if (callbacks == null) {
                callbacks = new CopyOnWriteSortedSet<XC_MethodHook>();
                sHookedMethodCallbacks.put(hookMethod, callbacks);
                newMethod = true;
            }
        }
        callbacks.add(callback);  // 先将被挂钩的函数 hookMethod 及挂钩它的函数保存起来
        if (newMethod) {
            Class<?> declaringClass = hookMethod.getDeclaringClass();
            int slot = (int) getIntField(hookMethod, "slot");

            Class<?>[] parameterTypes;
            Class<?> returnType;
            if (hookMethod instanceof Method) {
                parameterTypes = ((Method) hookMethod).getParameterTypes();
                returnType = ((Method) hookMethod).getReturnType();
            } else {
                parameterTypes = ((Constructor<?>) hookMethod).getParameterTypes();
                returnType = null;
            }

            AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
            hookMethodNative(hookMethod, declaringClass, slot, additionalInfo); // 最终调用 hookMethodNative 函数
} return callback.new Unhook(hookMethod); }

这个函数先将被挂钩的函数 hookMethod 及挂钩它的 XC_MethodHook结构体保存起来,然后调用 hookMethodNative 函数

{"hookMethodNative", "(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V", (void*)de_robv_android_xposed_XposedBridge_hookMethodNative},

这个函数定义在 xposed.cpp 里

static void de_robv_android_xposed_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 (xposedIsHooked(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 = &xposedCallHandler;
    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);
        }
    }
}

这个过程跟ADBI框架类似,先获取要挂钩的java函数的 Method 结构体指针,然后检查一下是否已经被挂钩了,如果是直接返回,否则,通过对 Method 结构体进行赋值的方式,完成挂钩

method->nativeFunc = &xposedCallHandler;
    method->insns = (const u2*) hookInfo;
    method->registersSize = method->insSize;
    method->outsSize = 0;

这里挂钩函数全部使用 xposedCallHandler 这个函数。挂钩的详细信息保存在 Method结构体的 insns 成员里

xposed.cpp

static void xposedCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
。。。
JValue result;
    dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,
        originalReflected, (int) original, additionalInfo, thisObject, argsArray);
。。。
}

可以看到,最终执行xposedHandleHookedMethod这个native函数,这个native函数前面 initNative 执行时,已经获取了它的java实现,真正的实现在

XposedBridge.java

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

这个函数查找被挂钩函数的挂钩 XC_MethodHook 结构体,然后执行里边的 beforeHookedMethod 函数,再通过 invokeOriginalMethodNative 执行挂钩前的原始函数,最后再执行 afterHookedMethod 函数。

findAndHookMethod 的实现就分析完了,本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc, registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 xposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找 xposed模块及xposed框架调用 findAndHookMethod 注册的 before,after 函数,如果有,就执行,再通过 invokeOriginalMethodNative 执行挂钩前函数。

回到 initXbridgeZygote  函数,xposed 框架预先挂钩的函数,除了 handleBindApplication 外,还有

com.android.server.ServerThread  系统thread创建时触发hookAllConstructors(LoadedApk.class, new XC_MethodHook(){。。。}  apk 包加载时触发这个挂钩findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication", 。。。 

3.  loadModules
/**
     * Try to load all modules defined in <code>BASE_DIR/conf/modules.list</code>
     */
    private static void loadModules(String startClassName) throws IOException {
        BufferedReader apks = new BufferedReader(new FileReader(BASE_DIR + "conf/modules.list"));
        String apk;
        while ((apk = apks.readLine()) != null) {
            loadModule(apk, startClassName);
        }
        apks.close();
    }

xposed框架本身挂钩的函数很少,真正的挂钩由具体的xposed模块实现,xposed模块也是正常的app,安装的时候注册其挂钩信息到xposed框架的 modules.list 等配置里。xposed框架初始化的时候, 加载这些模块,并完成挂钩函数的挂钩

/**
     * Load a module from an APK by calling the init(String) method for all classes defined
     * in <code>assets/xposed_init</code>.
     */
    @SuppressWarnings("deprecation")
    private static void loadModule(String apk, String startClassName) {
      。。。。             while ((moduleClassName = moduleClassesReader.readLine()) != null) {// call the init(String) method of the module
                    final Object moduleInstance = moduleClass.newInstance();
                    if (startClassName == null) {
                        if (moduleInstance instanceof IXposedHookZygoteInit) {
                            IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
                            param.modulePath = apk;
                            ((IXposedHookZygoteInit) moduleInstance).initZygote(param);
                        }

                        if (moduleInstance instanceof IXposedHookLoadPackage)
                            hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));

                        if (moduleInstance instanceof IXposedHookInitPackageResources)
                            hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
                    } else {
                        if (moduleInstance instanceof IXposedHookCmdInit) {
                            IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.StartupParam();
                            param.modulePath = apk;
                            param.startClassName = startClassName;
                            ((IXposedHookCmdInit) moduleInstance).initCmdApp(param);
                        }
                    }
                }}
loadModule 函数从配置文件里读取所有的模块,实例化模块类,xposed 提供了几个接口类供xposed模块继承,不同的接口类对应不同的hook时机

IXposedHookZygoteInit  zygote 初始化前就执行挂钩,即loadModule执行时就挂钩了IXposedHookLoadPackage  apk包加载的时候执行挂钩,先将挂钩函数保存起来,等加载apk函数执行后触发callback (这里的callback是xposed框架自己挂钩的函数),再执行模块注册的挂钩函数IXposedHookInitPackageResources apk资源实例化时执行挂钩,同上
时间: 2024-11-10 14:07:43

android hook 框架 xposed 如何实现挂钩的相关文章

android hook 框架 xposed 如何实现注入

前面分析的adbi框架和libinject都是使用so注入的方式,实现将指定代码装入目标进程,这种方式有几个特点: 1. 是动态的,需要目标进程已经启动 2. 无法影响全局,比如注入A进程挂钩里边libc.so的open函数,此时,B进程使用的libc.so的open函数还是老函数,linux系统通过COW机制,在你注入A进程并执行对open的挂钩的时候,拷贝了新的页面,放入新的函数.如果要影响全局,应该注入到类似 Zygote 这样的进程,且应该在zygote进程启动之后马上注入,这样后续zy

android hook 框架 ADBI 如何实现dalvik函数挂钩

前面几篇分析已经能做到注入一个so到目标进程并用so里的函数挂钩目标进程的函数,如果对这个实现不了解,请返回去阅读  android hook 框架 ADBI 简介.编译.运行  . android hook 框架 ADBI 如何实现so注入 .android hook 框架 ADBI 如何实现so函数挂钩, so函数的挂钩只能影响native世界,没有影响到java虚拟机内部,而android绝大部分逻辑都是跑在虚拟机内部的.所以这篇接着分析 adbi 剩下的部分代码,看它如何实现挂钩dalv

android hook 框架 libinject2 如何实现so注入

上一篇 android hook 框架 libinject 简介.编译.运行 实际运行了so的注入并调用了注入so里的一个函数,这篇开始分析其实现. 与之前分析的 abdi 项目一样,libinject2 也是依赖于linux系统的 ptrace 系统调用. android hook 框架 ADBI 简介.编译.运行 android hook 框架 ADBI 如何实现so注入 android hook 框架 ADBI 如何实现函数挂钩 这个库首先对ptrace的调用封装了几个helper函数 i

android hook 框架 libinject 如何实现so注入

前面两篇 android hook 框架 libinject2 简介.编译.运行 android hook 框架 libinject2 如何实现so注入 实际运行并分析了 Android中的so注入(inject)和挂钩(hook) - For both x86 and arm 这个博客给出了 libinject 改进版的代码. 今天分析一下古河大神原始的 libinject 的源码,libinject2 与 原始的 libinject 大部分代码是一致的,各种 ptrace 的封装函数基本照抄

Android.Hook框架Cydia篇

Cydia Substrate是一个代码修改平台.它可以修改任何主进程的代码,不管是用Java还是C/C++(native代码)编写的.而Xposed只支持HOOK app_process中的java函数,因此Cydia Substrate是一款强大而实用的HOOK工具. 官网地址:http://www.cydiasubstrate.com/ 官方教程:http://www.cydiasubstrate.com/id/38be592b-bda7-4dd2-b049-cec44ef7a73b SD

Android Hook框架adbi源码浅析(一)

adbi(The Android Dynamic Binary Instrumentation Toolkit)是一个Android平台通用hook框架,基于动态库注入与inline hook技术实现.该框架由两个主要模块构成,1.hijack负责将动态库注入到目标进程:2.libbase提供动态库本身,它实现了通用的hook功能. 而example则是一个使用adbi进行epoll_wait hook的demo. [email protected]PC:~/Android/adbi-maste

Android Hook框架adbi源码浅析(二)

二.libbase 其实上面加载完SO库后,hook的功能我们完全可以自己在动态库中实现.而adbi作者为了方便我们使用,编写了一个通用的hook框架工具即libbase库.libbase依然在解决两个问题:1.获取要hook的目标函数地址:2.给函数打二进制补丁即inline hook. 关于获取hook函数地址的方法这里不再赘述.直接看inline hook部分,这部分功能在base\hook.c的hook()函数中实现,先看hook_t结构体: struct hook_t { unsign

Hook框架xposed的简单demo

简介:Xposed框架是一款可以在不修改APK的情况下影响程序运行的框架服务,通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的虚拟机的劫持. Github地址:https://github.com/rovo89/Xposed 首先我们Hook无参数的函数 1.建立一个空工程,编写获取序列号的简单例子 1) 获取手机状态需要设置权限 <use

Android Hook神器——XPosed入门(登陆劫持演示)

如果想飞得高,就该把地平线忘掉. 前段时间写了一篇有关于CydiaSubstrate的广告注入的文章(地址:http://blog.csdn.net/yzzst/article/details/47318751),大家都直呼过瘾.但是,真正了解这一方面的同学应该这道,其实还有一个比CydiaSubstrate更出名的工具:XPosed. 不是因为Xposed比CydiaSubstrate做的多好,而是Xposed是彻底开源的.今天,我们就向大家简单的介绍一下Xposed,并书写一个简单的登陆劫持