• android hook 框架 xposed 如何实现注入


    Android so注入-libinject2 简介、编译、运行

    Android so注入-libinject2  如何实现so注入

    Android so注入-Libinject 如何实现so注入

    Android so注入挂钩-Adbi 框架简介、编译、运行

    Android so注入挂钩-Adbi 框架如何实现so注入

    Android so注入挂钩-Adbi 框架如何实现so函数挂钩

    Android so注入挂钩-Adbi 框架如何实现dalvik函数挂钩

    Android dalvik挂钩-Xposed框架如何实现注入

    Android dalvik挂钩-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

    androidframeworksasecmdsapp_processapp_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 的世界。

    Xposedapp_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("-----------------
    " + date + " UTC
    "
                        + "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 等机制

  • 相关阅读:
    前端之CSS
    mysql数据库 -- Navicat、pycharm连接数据库
    mysql数据库之表查询
    mysql 数据库之表操作
    前端 HTML
    数据库设计(第一范式,第二范式,第三范式
    MySQL之锁、事务、优化、OLAP、OLTP
    MySQL之创建用户和授权
    MySQL之索引原理与慢查询优化
    MySQL之视图、触发器、事务、存储过程、函数
  • 原文地址:https://www.cnblogs.com/jiayy/p/4305018.html
Copyright © 2020-2023  润新知