Xposed源码剖析——app_process作用详解

Xposed源码剖析——app_process作用详解

首先吐槽一下CSDN的改版吧,发表这篇文章之前其实我已经将此篇文章写过了两三次了。就是发表不成功。而且CSDN将我的文章草稿也一带>删除掉了。弄得我现在只有使用sublime写一次,保证它们删不掉。


承接上文 http://blog.csdn.net/yzzst/article/details/47659987

上面我们分析Xposed项目的源码,从XposedInstaller开始说明了Xposed安装的原理与过程。我们知道,XposedInstaller主要的工作就是:

  • 替换系统的app_process(当然,这个操作需要Root权限)
  • 将xposed的api文件,XposedBridge.jar文件放置到私有目录中

至于
为什么要替换app_process文件?
系统中的app_process文件有什么作用?
替换后的app_process为什么能够帮助我们hook?

下面我们就开始看看,rovo89大神的xposed开源项目。从GitHub上面clone下来xposed项目,我们在目录中看到其目录结构,如下所示:

从目录中,我们能够清楚的了解到,其中xposed项目现在已经支持Dalvik虚拟机与art虚拟机的架构了。


app_main.cpp 源码阅读与对比

ok这里,我们先从app_process的源码开始阅读,打开app_main.cpp文件,估计大家和我一下,一时间也看不出来xposed针对源码修改了一些什么。

那么,我们就直接拿源码与xposed中的app_main.cpp进行对比。

源码地址:/frameworks/base/cmds/app_process/app_main.cpp

发现了,rovo89针对了一下几个地方进行了修改。

atrace_set_tracing_enabled 进行了替换修改

onVmCreated 增加了Xposed的回调

main函数中,增加了 xposed 的 options 操作

我们在xposed.cpp中,能够看到其handleOptions的具体逻辑,其实就是处理一些xposed的特殊命令而已。
如下所示:

/** Handle special command line options. */
bool handleOptions(int argc, char* const argv[]) {
    parseXposedProp();

    if (argc == 2 && strcmp(argv[1], "--xposedversion") == 0) {
        printf("Xposed version: %s\n", xposedVersion);
        return true;
    }

    if (argc == 2 && strcmp(argv[1], "--xposedtestsafemode") == 0) {
        printf("Testing Xposed safemode trigger\n");

        if (detectSafemodeTrigger(shouldSkipSafemodeDelay())) {
            printf("Safemode triggered\n");
        } else {
            printf("Safemode not triggered\n");
        }
        return true;
    }

    // From Lollipop coding, used to override the process name
    argBlockStart = argv[0];
    uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
    uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);
    end += strlen(argv[argc - 1]) + 1;
    argBlockLength = end - start;

    return false;
}

* main函数中,启动的时候增加了启动一些逻辑 *

具体的, 我们可以看到。runtime.start那一段。做出了一个启动。

    isXposedLoaded = xposed::initialize(zygote, startSystemServer, className, argc, argv);
    if (zygote) {
        // 当xposed成功启动的时候,start XPOSED_CLASS_DOTS_ZYGOTE这个类
        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "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;
        // 当xposed成功启动的时候,start XPOSED_CLASS_DOTS_ZYGOTE这个类
        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : "com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }

这里的我们看到,在main函数中启动了逻辑,

runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");

其中,XPOSED_CLASS_DOTS_ZYGOTE 变量在,xposed.h头文件中有定义,如下所示:

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

发现,其实这个类就是我们之前向私有目录防止的XposedBridge项目的包名。

而runtime.start这个包名有什么作用呢?我们在AndroidRuntime中找到start方法的具体逻辑
在源代码中/frameworks/base/core/jni/AndroidRuntime.cpp中看到

/*
 * Start the Android runtime.  This involves starting the virtual machine
 * and calling the "static void main(String[] args)" method in the class
 * named by "className".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
void AndroidRuntime::start(const char* className, const char* options)

系统源码对start方法的定义,就是启动对应类的 start void main入口函数。这里,就将三个项目的逻辑连接起来了。


XposedBridge.java

我们在XposedBridge.java代码中,看到其main方法,如下所示:

    /**
     * Called when native methods and other things are initialized, but before preloading classes etc.
     */
    protected static void main(String[] args) {
        // Initialize the Xposed framework and modules
        try {
            SELinuxHelper.initOnce();
            SELinuxHelper.initForProcess(null);

            runtime = getRuntime();
            if (initNative()) {
                XPOSED_BRIDGE_VERSION = getXposedVersion();
                if (isZygote) {
                    startsSystemServer = startsSystemServer();
                    // 为启动一个新的 zygote做好 hook准备
                    initForZygote();
                }
                // 载入Xposed的一些modules
                loadModules();
            } else {
                log("Errors during native Xposed initialization");
            }
        } catch (Throwable t) {
            log("Errors during Xposed initialization");
            log(t);
            disableHooks = true;
        }

        // 调用系统原来的启动方法
        if (isZygote)
            ZygoteInit.main(args);
        else
            RuntimeInit.main(args);
    }

ok,那么,整个app_process的复制hook逻辑,到这里我们已经清楚了。逻辑如下图所示。

那么,xposed具体怎么实现系统api逻辑的replace和inject我们下次在做分析。

/*
* @author zhoushengtao(周圣韬)
* @since 2015年8月21 日 9:39:32
* @weixin stchou_zst
* @blog http://blog.csdn.net/yzzst
* @交流学习QQ群:341989536
* @私人QQ:445914891
/

这里写图片描述

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

北漂周

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值