android hook 框架 xposed 如何实现注入

前面分析的adbi框架和libinject都是使用so注入的方式,实现将指定代码装入目标进程,这种方式有几个特点:

1. 是动态的,需要目标进程已经启动

2. 无法影响全局,比如注入A进程挂钩里边libc.so的open函数,此时,B进程使用的libc.so的open函数还是老函数,linux系统通过COW机制,在你注入A进程并执行对open的挂钩的时候,拷贝了新的页面,放入新的函数。如果要影响全局,应该注入到类似 Zygote 这样的进程,且应该在zygote进程启动之后马上注入,这样后续zygote进程生成子进程时就能使用挂钩后的函数

3. 需要依赖ptrace机制,某些情况下,目标进程无法被执行ptrace,则这种方式会失效

这一篇我们分析另外一种方式,是著名的xposed框架使用的方式,不需要动态注入,而是直接替换android系统的一个可执行程序。

一,android应用层进程启动最初始的几步

linux系统装载并初始化各个子系统完毕后,执行第一个应用层程序init, android 的 init 程序是自己定制的,与其它linux发行版不一样,它同样会解析并执行 init.rc 配置文件。其中,有一步如下,调用 app_process 程序启动 zygote 进程,xposed 替换的就是这个  /system/bin/app_process 程序

system/core/rootdir/init.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

android\frameworks\base\cmds\app_process\app_main.cpp  : main 函数

  if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start("com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {

app_process 是native世界进入java世界的入口,它初始化了虚拟机的执行时环境,并根据不同的参数,调用 com.android.internal.os.ZygoteInit 或者 com.android.internal.os.RuntimeInit 这两个java类的main函数,如果是前者,则进入的 zygote 的世界。

Xposed\app_main.cpp : main 函数

if (zygote) {
        runtime.start(keepLoadingXposed ? XPOSED_CLASS_DOTS : "com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start(keepLoadingXposed ? XPOSED_CLASS_DOTS : "com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {

#define XPOSED_CLASS_DOTS "de.robv.android.xposed.XposedBridge"

与标准流程不一样的地方,如果检测到android版本支持xposed且已经安装了Xposed,则 runtime.start 启动的是  de.robv.android.xposed.XposedBridge 的main函数,进入了 xposed 的世界

xposedbridge.java

private static void main(String[] args) {
        // the class the VM has been created for or null for the Zygote process
        String startClassName = getStartClassName();

        // initialize the Xposed framework and modules
        try {
            // initialize log file
            try {
                logFile = new File(BASE_DIR + "log/error.log");
                if (startClassName == null && logFile.length() > MAX_LOGFILE_SIZE_SOFT)
                    logFile.renameTo(new File(BASE_DIR + "log/error.log.old"));
                logWriter = new PrintWriter(new FileWriter(logFile, true));
                logFile.setReadable(true, false);
                logFile.setWritable(true, false);
            } catch (IOException ignored) {}

            String date = DateFormat.getDateTimeInstance().format(new Date());
            determineXposedVersion();
            log("-----------------\n" + date + " UTC\n"
                    + "Loading Xposed v" + XPOSED_BRIDGE_VERSION
                    + " (for " + (startClassName == null ? "Zygote" : startClassName) + ")...");
            if (startClassName == null) {
                // Zygote
                log("Running ROM ‘" + Build.DISPLAY + "‘ with fingerprint ‘" + Build.FINGERPRINT + "‘");
            }

            if (initNative()) {
                if (startClassName == null) {
                    // Initializations for Zygote
                    initXbridgeZygote();
                }

                loadModules(startClassName);
            } else {
                log("Errors during native Xposed initialization");
            }
        } catch (Throwable t) {
            log("Errors during Xposed initialization");
            log(t);
            disableHooks = true;
        }

        // call the original startup code
        if (startClassName == null)
            ZygoteInit.main(args);
        else
            RuntimeInit.main(args);
    }

xposedbridge 类先初始化xposed需要的环境,然后加载注册到xposed框架里的 xposed 模块,这一步执行完后,所以 xposed 对虚拟机的挂钩已经完成,mian 函数最后,执行  ZygoteInit.main 或者 RuntimeInit.main ,  进入正常的流程

从这里可以看出,xposed 对虚拟机的注入采用的是比动态注入更优雅的方式,有几个特点:

1. 由于替换了 app_process ,替换后的app_process 肯定是先启动 xposed 然后再进入 zygote ,而其他app都是 zygote 创建的,这样xposed 的挂钩一定的全局性的,所有app都会被影响

2. 只需要安装xposed时拥有root权限以替换系统的 app_process , 之后不再需要root权限,而前面采用 so动态注入的方式,每次要挂钩都需要注入,每次注入zygote 都需要root权限

3. 不需要依赖 ptrace 等机制

时间: 2024-10-10 04:29:56

android hook 框架 xposed 如何实现注入的相关文章

android hook 框架 xposed 如何实现挂钩

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

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 框架 ADBI 如何实现dalvik函数挂钩

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

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

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

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

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