xposed android_art,Art模式下Xposed实现原理

算是作为一个 Art 学习的切入点吧

网上找了很多资料,大多数基于dvm的资料。

(Dvm可以 参考 邓凡平先生的 博客 :https://blog.csdn.net/Innost/article/details/50461783)

今天也是自我总结一下 Art模式下的

在github 搜索 可得如下三个项目,分别介绍一下

b60b30180228

image.png

XposedInstaller ,这是 Xposed 的插件管理和功能控制 APP,也就是说 Xposed 整体管控功能就是由这个 APP 来完成的,它包括启用 Xposed 插件功能,下载和启用指定插件 APP,还可以禁用 Xposed 插件功能等。注意,这个 app 要正常无误得运行必须能拿到 root 权限。

Xposed,这个项目属于 Xposed 框架,其实它就是单独搞了一套 xposed 版的 zygote。这个 zygote 会替换系统原生的 zygote。所以,它需要由 XposedInstaller 在 root 之后放到 /system/bin 下。

XposedBridge,这个项目也是 Xposed 框架,它属于 Xposed 框架的 Java 部分,编译出来是一个 XposedBridge.jar 包。

问题1 :

当我们获取Root以后安装Xposed会发生什么?

下载XposedInstaller 导入 AndroidStudio

先看看 安装之前 :

从页面初始化开始入手

StatusInstallerFragment-》onCreateView

b60b30180228

image.png

第一次见到user_de目录 使用如下

b60b30180228

image.png

安装完毕之后 ,当我们点击

b60b30180228

image.png

获取手机的各种信息,拼接成下载的URL

b60b30180228

image.png

我们以 安卓 7.1 arm64的 为标准

下载完毕的 zip内容如下 主要包含 system 和 META-INT两个文件夹

system->

---lib64->

------libart.so

------libart-compiler.so

------libart-disassembler.so

------libsigchain.so

------libxposed_art.so

---lib->

------libart.so

------libart-compiler.so

------libsigchain.so

------libxposed_art.so

---framework->

------XposedBridge.jar(注入到对应app进程里面的jar包)

---bin->

------app_process32_xposed

------app_process64_xposed

------dex2oat

------patchoat

xposed.prop->

xposed版本说明文件

META-INT->

---里面有文件配置脚本 flash-script.sh 配置各个文件安装位置

下载完毕会跳转到 InstallationActivity

onCreate 函数 -》创建 InstallationFragment

b60b30180228

image.png

-》调用 startInstallation解压 zip文件-》执行done回调 -》

b60b30180228

image.png

替换系统system对应目录下的文件-》执行刷机命令-》重启手机

问题2:

Xposed 如何注入到zygote 进程中的?

首先复习一下Art虚拟机启动流程:

主要大致流程

①Linux init进程解析配置脚本->②app_process(zygote进程对应的程序)->③ZygoteInit

① 解析配置脚本

b60b30180228

image.png

service zygote:它告诉init进程,现在我们要配置一个名为zygote的服务。

/system/bin/app_process: 声明zygote进程对应的文件路径。init创建服务的处理逻辑很简单,就是启动(fork)一个子进程来运行指定的程序。对zygote服务而言这个程序就是/system/bin/app_process。

-Xzygote/system/bin--zygote--start-system-server:传递给app_process的启动参数。

②app_process 创建

frameworks\base\cmds\app_process.cpp-》main函数

b60b30180228

image.png

b60b30180228

image.png

frameworks\base\core\jni\AndroidRuntime.cpp-》start函数

核心函数为: init,startVm

b60b30180228

image.png

三个函数主要功能:

1. JNI_GetDefaultJavaVMInitArgs -- 获取虚拟机的默认初始化参数

2. JNI_CreateJavaVM -- 在进程中创建虚拟机实例

3. JNI_GetCreatedJavaVMs -- 获取进程中创建的虚拟机实例

ART像Dalvik一样,都实现Java虚拟机接口,这三个接口也是ART虚拟机核心接口。

startVm函数很复杂牵扯逻辑也很多,不 逐一描述了。

③ZygoteInit

继续查看 frameworks\base\core\jni\AndroidRuntime.cpp-》start函数

b60b30180228

image.png

参数className的值等于“com.android.internal.os.ZygoteInit”,本地变量env是从调用另外一个成员函数startVm创建的ART虚拟机获得的JNI接口。函数的目标就是要找到一个名称为com.android.internal.os.ZygoteInit的类,以及它的静态成员函数main,然后就以这个函数为入口,开始运行ART虚拟机。为此,函数执行了以下步骤:

① 调用JNI接口FindClass加载com.android.internal.os.ZygoteInit类。

② 调用JNI接口GetStaticMethodID找到com.android.internal.os.ZygoteInit类的静态成员函数main。

③ 调用JNI接口CallStaticVoidMethod开始执行com.android.internal.os.ZygoteInit类的静态成员函数main。

下面看看 Xposed是如何做拦截的

开打 Xposed项目

b60b30180228

image.png

大于21编译走的是app_main2.cpp 看看 具体改动了哪些

经过查阅,被修改的main函数,一共有两个地方。

其一,红框的地方 是判断是否是Xposed版本的虚拟机

b60b30180228

在解析开启启动init脚本的时候 添加了--xposedversion 版本号的命令

这块启动的已经是自定义的虚拟机了

b60b30180228

handleOptions函数

第二个地方在 start函数这块,先看看 原函数。

b60b30180228

原函数

b60b30180228

xposed zygote函数

也是在这个地方 进行的初始化 判断是否初始化成功 。

initialize 函数返回的是否加载成功的 一个全局变量 isXposedLoaded

b60b30180228

initialize函数

b60b30180228

xposed自定义的数据结构体

初始化完毕以后开始调用真正的start函数

下面看 runtimeStart 函数

这块很有趣 在libart.so里面根据符号表信息尝试拿到Android::start函数

上面这些只要有一步失败了,在刷入的时候就可能变砖。

如果获取到了,则可以直接通过函数指针调用,主要是针对一些特殊的安卓版本号。

如果都没有找到 可以看到 Log会打印 。

“app_process: could not locate AndroidRuntime::start() method.”

b60b30180228

runtimeStart函数

(这个地方有个小技巧,可以对so文件里面的全部函数名字进行逐一字符判断,

比如可以对这个字符串 判断 是否含有 R u n t i m e s t a r t这几个字符,来绕过因为编译优化字符串不同问题)

这样一来完美替换了原虚拟机。

在新的虚拟机里面 会 将 XposedBridge.jar 进行注入,这么一来,所有被Xposed fork的进程都具备了 XposedBridge.jar 的代码 。

问题3:

当我们findAndHookMethod一个函数以后Xposed是怎么处理的?

打开XposedBridge项目

找到 findAndHookMethod

b60b30180228

findAndHookMethod

跟入XposedBridge.hookMethod

参数1 是一个 接口 可能传入的是一个 Constructor (构造方法的反射实例)也可能是 Method

Member 类型是Constructor 和Method都已经实现的,因为Xposed支持 Hook构造和Method。

b60b30180228

hookMethod

最终 走到HookMethodNative方法,注册地方在Xposed里面的libXposed_common.cpp中

slot 是 Method在类中的偏移位置

b60b30180228

image.png

重点分析一下实现过程

返回到Xposed 项目

libxposed_art.cpp-》XposedBridge_hookMethodNative函数

b60b30180228

XposedBridge_hookMethodNative

ScopedObjectAccess soa(env);

(SOA,就是约定的调用,包装env,出了函数范围自动释放)

FromReflectedMethod是ArtMethod里面的方法

b60b30180228

FromReflectedMethod

也很简单 就是调用里面的GetArtMethod,在art虚拟机中,每一个加载的类方法都有一个对应的ArtMethod对象。

返回去 继续看 EnableXposedHook 函数

EnableXposedHook 在art_method.cc里面

b60b30180228

EnableXposedHook①

(PrettyMethod函数有个小技巧 当我们分析被So中注册函数的时候 ,可以直接用ArtMethod的this指针调用 PrettyMethod 函数拿到签名信息)

继续查看 backup_method表示其为Hook方法的原方法,然后为备份的ArtMethod创建对应的Method对象。

b60b30180228

EnableXposedHook②

(

fast_jin模式科普:

下文参考资料(《深入理解ART虚拟机》:

安卓 函数执行 分为两条线 第一种是 Java层,第二种JNI层 也就是 so层

当函数调用Java层进入到JNI层的是时候,虚拟机会将执行线程的状态从Runnable转换为Native。

如果JNI层又调用Java层相关函数的时候,执行线程的状态又得从Native层转换为Runnable。

线程的切换需要浪费时间,所以,对于某个特别强调执行速度的JNI函数可以设置成 fast jni模式

这种模式下执行这个native函数 将不会进行 状态切换,即执行线程的状态 始终为Runnable。

当然,这种模式的使用对GC有一些影响,所哟最好在那些本身函数执行时间段的,又不会阻塞的情况下使用。

另外,这种模式目前在art虚拟机内部 很多java native都有使用

为了和其他Native函数 进行区分,当使用fast jni模式的函数的签名信息 必须以 “!”开头

)

b60b30180228

EnableXposedHook③

把Method对象,方法额外信息和原始方法保存至XposedHookInfo结构体中,并调用SetEntryPointFromJni()把这个结构体变量的内存地址保存在ArtMethod对象中。这个方法原本是用来保存native方法的入口地址的,既然使用了这个位置,那么就必须把对应的标志位清除,代码实现的最后调用SetAccessFlags((GetAccessFlags() & ~kAccNative & ~kAccSynchronized) | kAccXposedHookedMethod)来完成标志位的清除(设置Fast_jni模式),此时这个ArtMethod对象对应是Hook后的方法,这个方法的实现不是native的。

b60b30180228

EnableXposedHook④

这么一来完成了整体Hook流程

总结:

执行流程:

XposedBridge.hookMethod-》XposedBridge.hookMethodNative-》EnableXposedHook

核心代码So层里面的 hookMethodNative 和 XposedBridge_hookMethodNative 里面

1,hookMethodNative 先将java层传入的 被Hook的信息转换成 ArtMethod,方便调用方法进行Hook,调用EnableXposedHook 方法。

2,在 EnableXposedHook 进行简单的判断 是否是被Hook的方法,以及是否已经被Hook过

准备一个备份的 ArtMethod 存放 原方法的信息,将备份的ArtMethod 设置信息,所属类,告诉虚拟机这个方法不需要JIT编译,并将其设置成Native,准备一个简单的结构体XposedHookInfo保存,保存被Hook方法的信息,包括原方法的信息,地址,最后将 入口设置成 XposedHookInfo,设置机械码执行的首地址,将原方法的 CodeItem偏移设置0。

喜欢文章的话 可以点个关注,如果对 逆向,脱壳,新技术 感兴趣的 同学 可以加 我Q群 欢迎各位能人志士 一起讨论

欢迎加入故事,群聊号码:773642813 也可以加入笔者的 小密圈,各种安卓新技术,源码分享等

b60b30180228

image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值