抱歉,Xposed真的可以为所欲为——终 · 庖丁解码(上)

简介: Xposed的使用不难,API也就那些,难点是: 逆向弄清楚Hook APP的方法调用流程,怎么调,参数都是干嘛的等。经过反复练习,逆向Hook一个普通的APP(非企业级加固)写出可用的Xposed插件早已驾轻就熟(主要是磨时间),但有一个顾虑一直萦绕心间:不知道Xposed底层的具体实现原理。Tips:Xposed通常只能 Hook java层 及 应用资源的替换,有两个实现版本:4.4前的Dalvik虚拟机实现 和 5.0后ART虚拟机实现,本文针对后者进行分析,同时搭配 Android 5.1.1_r6 源码食用。

0x1、Xposed的组成


这个庞大的工程由下面四个项目组成:


  • Xposed → C++部分,Xposed版的zygote,用于替换原生zygote,并为XposedBridge提供JNI方法,需由XposedInstaller在root后放到/system/bin目录下;


  • XposedBridge → Java部分,编译后会生成一个jar包,负责在Native层与Framework层进行交互;


  • XposedInstaller → Xposed插件管理及功能控制的APP,包括启用、下载、禁用插件等功能;



0x2、Xposed的使用


一个简单的Hook示例如下:


网络异常,图片无法展示
|


几步走:


  • ① 类实现 IXposedHookLoadPackage 接口


  • ② 重写 handleLoadPackage() 方法


  • ③ 调用 XposedHelpers.findAndHookMethod(),传入完整类名、类加载器、方法名,参数类型,XC_MethodHook实例;


  • ④ 按需重写 beforeHookedMethod()(方法调用前执行代码) 和 afterHookedMethod() 方法(方法调用后执行代码);


通过这样的操作,即可随意蹂躏一个Java方法的逻辑,以达到自己的目的。


0x3、Android系统的启动过程


在开始探索实现原理前,先来了解下Android系统的启动过程,简略流程图如下:


网络异常,图片无法展示
|


关注 Zygote进程 , 它由 init进程 启动,启动时会创建一个Davlik虚拟机实例,并把Java运行时库加载到进程中,并注册一些Android核心类的JNI到前面创建的Dalvik虚拟机实例中。


所有APP进程都是由Zygote进程孵化(fork) 而来的,fork时不仅仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起 共享Java运行时库


Xposed就是利用这样的机制,只往Zygote中注入 XposedBridge.jar,就可以实现全局注入。


Tips:Android 5.0 开始zygote是两个进程,32位(兼容armeabi和armeabi-v7a等32 位架构的本地动态库的应用) 和 64位(arm64-v8a等64 位架构本地库动态库),init.rc 文件也做了区分,init.zygote32.rc 启动 32位的zygote,init.zygote64 启动 64位的zygote。


0x4、Zygote的启动流程


跟下 /system/core/rootdir/init.zygote.rc


service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server


分段解析下这段代码:


  • service → ATL语言语法,启动一个服务进程;


  • zygote → 启动的程序名称,这指zygote进程;


  • /system/bin/app_process → 可执行文件路径( app_main.cpp );


  • -Xzygote /system/bin → 指定参数传到app_main.cpp中;


  • --zygote --start-system-server → 传的具体参数值;


简单点说就是:启动了Zygote进程,传递的参数可在 /frameworks/base/cmds/app_process/app_main.cpp 中找到:


网络异常,图片无法展示
|


对传进来的参数做匹配,zygote、startSystem标志位设置为true,接着定位下哪里用到了zygote这个标记:


网络异常,图片无法展示
|


跟下:runtime.start() 定位到 frameworks/base/core/jni/AndroidRuntime.cpp,关键代码如下:


// ① 初始化jni接口
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
// ② 创建VM虚拟机
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
    return;
}
onVmCreated(env);
// ③ 注册JNI方法
if (startReg(env) < 0) {
    ALOGE("Unable to register all android natives\n");
    return;
}
// ④ 调用className类的static void main(String args[]) 方法
slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
// 找到main函数
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
    "([Ljava/lang/String;)V");
if (startMeth == NULL) {
    ALOGE("JavaVM unable to find main() in '%s'\n", className);
    /* keep going */
} else {
    // 通过 JNI 调用 main 函数,从 C++ 到 Java
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
    if (env->ExceptionCheck())
        threadExitUncaughtException(env);
}


所以这里创建了一个虚拟机,注册JNI方法,然后调用 com.android.internal.os.ZygoteInitmain(),跟下frameworks/base/core/java/com/android/internal/os/ZygoteInit.java


public static void main(String argv[]) {
    try {
        ...
        // ① 注册一个name为zygote的socket,用于和其他进程通信
        registerZygoteSocket(socketName);
        // ② 预加载所需资源到VM中,如class、resource、OpenGL、公用Library等;
        // 所有fork的子进程共享这份空间而无需重新加载,减少了应用程序的启动时间,
        // 但也增加了系统的启动时间,Android启动最耗时的部分之一。
        preload();
        // ③ 初始化gc,只是通知VM进行垃圾回收,具体回收时间、怎么回收,由VM内部算法决定。
        // gc()需在fork前完成,这样将来复制的子进程才能有尽可能少的垃圾内存没释放;
        gcAndFinalize();
        // ④ 启动system_server,即fork一个Zygote子进程
        if (startSystemServer) {
            startSystemServer(abiList, socketName);
        }
        // ⑤ 进入循环模式,获取客户端连接并处理
        runSelectLoop(abiList);
        // ⑥ 关闭和清理zygote socket
        closeServerSocket();
    } catch (MethodAndArgsCaller caller) {
        caller.run();
    } catch (RuntimeException ex) {
        Log.e(TAG, "Zygote died with exception", ex);
        closeServerSocket();
        throw ex;
    }
}


跟下 startSystemServer()


private static boolean startSystemServer(String abiList, String socketName)
        throws MethodAndArgsCaller, RuntimeException {
    int pid;
    try {
        ...
        // fork出system_server进程,返回pid,此处pid为0
        pid = Zygote.forkSystemServer(
            parsedArgs.uid, parsedArgs.gid,
            parsedArgs.gids,
            parsedArgs.debugFlags,
            null,
            parsedArgs.permittedCapabilities,
            parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
        /* 进入子进程 */
        if (pid == 0) {
            // Android 5.0上有两个Zygote进程:zygote 和 zygote64
            // 对于有两个zygote进程的情况,需等待第二个zygote创建完成;
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            // 完成system_server进程的剩余工作
            handleSystemServerProcess(parsedArgs);
        }
        return true;
}


Tips:fork()方法被调用一次,返回两次,区别是:子进程的返回值是0,父进程的返回值是子进程的进程id,可以保证子进程的进程id不可能为0。


相关文章
|
消息中间件 缓存 安全
抱歉,Xposed真的可以为所欲为——终 · 庖丁解码(下)
Xposed的使用不难,API也就那些,难点是: 逆向弄清楚Hook APP的方法调用流程,怎么调,参数都是干嘛的等。 经过反复练习,逆向Hook一个普通的APP(非企业级加固)写出可用的Xposed插件早已驾轻就熟(主要是磨时间),但有一个顾虑一直萦绕心间:不知道Xposed底层的具体实现原理。Tips:Xposed通常只能 Hook java层 及 应用资源的替换,有两个实现版本:4.4前的Dalvik虚拟机实现 和 5.0后ART虚拟机实现,本文针对后者进行分析,同时搭配 Android 5.1.1_r6 源码食用。
1589 0
|
4月前
|
Python
惊呆了!学会这一招,你的Python上下文管理器也能玩出花样来文管理器也能玩出花样来
【7月更文挑战第6天】Python的上下文管理器是资源优雅管理的关键,与with语句结合,确保资源获取和释放。通过实现`__enter__`和`__exit__`,不仅能做资源分配和释放,还能扩展实现如计时、自动重试、事务处理等功能。例如,TimerContextManager类记录代码执行时间,展示了上下文管理器的灵活性。学习和利用这一机制能提升代码质量,增强功能,是Python编程的必备技巧。
33 0
|
数据库连接 数据库
红皮书——错误点
红皮书——错误点
红皮书——错误点
|
小程序 Java 机器人
使用Java实现发送微信消息(附源码)_此程序在手再也不怕对象跟你闹了
此程序在手再也不怕女朋友跟你闹了!!!!自从有了女朋友比如:早安、晚安之类的问候语可不能断,但是也难免有时候会忘记那么该咋么办呢?很简单写一个程序么,近日闲来无趣想着用Java写一个自动发送微信的小程序,实现定时给指定的好友发送指定的消息,这不就很Nice了?本文主要包括实现的思路、代码的实现、打包为jar快捷方式!
155 0
|
JSON 应用服务中间件 数据格式
杨洋撒撒一大片,Controller接收中文不再“不正经”,乱码问题这样解决,你信或不信
杨洋撒撒一大片,Controller接收中文不再“不正经”,乱码问题这样解决,你信或不信
124 0
杨洋撒撒一大片,Controller接收中文不再“不正经”,乱码问题这样解决,你信或不信
|
安全 Java API
抱歉,Xposed真的可以为所欲为——终 · 庖丁解码(中)
Xposed的使用不难,API也就那些,难点是: 逆向弄清楚Hook APP的方法调用流程,怎么调,参数都是干嘛的等。 经过反复练习,逆向Hook一个普通的APP(非企业级加固)写出可用的Xposed插件早已驾轻就熟(主要是磨时间),但有一个顾虑一直萦绕心间:不知道Xposed底层的具体实现原理。Tips:Xposed通常只能 Hook java层 及 应用资源的替换,有两个实现版本:4.4前的Dalvik虚拟机实现 和 5.0后ART虚拟机实现,本文针对后者进行分析,同时搭配 Android 5.1.1_r6 源码食用。
602 0
|
API
抱歉,Xposed真的可以为所欲为——1.基础知识储备(下)
本节简单介绍了什么是Xposed,基本原理,如何创建一个Xposed项目以及Xposed常用的类与方法。
748 0
|
Java Linux API
抱歉,Xposed真的可以为所欲为——1.基础知识储备(上)
本节简单介绍了什么是Xposed,基本原理,如何创建一个Xposed项目以及Xposed常用的类与方法。
440 0
|
存储 网络协议 数据安全/隐私保护
邮件传输的过程都看不懂。那我走(狗头)
给学习网络的新手一个我自己总结的建议: 我觉得学网络先要先把最基础最常用的协议的原理搞的明明白白,然后再学习难的协议或者是拓展的协议就好理解容易上手了。
231 0
邮件传输的过程都看不懂。那我走(狗头)
|
SQL
开胃菜解析
开胃菜解析
153 0