您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
Xposed框架原理深入研究
 
  3574  次浏览      15
 2019-3-5  
 
编辑推荐:
本文来自csdn,主要介绍了Xposed的核心内容,通过案例的讲解进行了一个深入的研究。

Xposed框架核心思想在于将Java层普通函数注册成本地JNI方法,以此来变相实现hook机制(放在文章开头的话很重要哦,记住!)。

Xposed框架的技术核心建立在Jvm原生的JNI机制之上,为了对Xposed框架进行深入分析,同时方便大家理解,我们从以下三个问题着手:

1.Dalvik虚拟机在执行java层代码时如何识别JNI方法?

2.怎样才能将java层普通方法注册成JNI方法?

3.Xposed框架做了什么?

有疑问,那就一个一个来解决吧。

第一个问题:Dalvik虚拟机在执行java层代码时如何识别JNI方法?

先来了解一下类的加载过程:当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:

(1)方法代码存于何处,

(2)它有哪些参数,

(3)方法的描述符(public、native之类), etc...

如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。下面来看看方法描述符的样子。

注意:方法描述符实际上是一个结构体!

主要到上面的accessFlags了吗,它标记着该方法的类型,public、native等等,所以,要是想把一个方法注册成本地方法,这个标记是必须要改的!

我们知道了accessFlags的作用还不够的,最好还是再了解一下它在执行过程中是怎么发挥作用的,追本溯源吗!

这里简要的描述一下Dalvik虚拟机执行代码基本过程:

一个类在执行之前先会被装载,之后要进行字节码验证(dvmVerifyCodeFlow(),感兴趣的去查查源码),检验完成之后虚拟机会紧接着调用FindClass()来查找并装载main的方法类。此后JNIEnv类调用成员函数CallStaticVoidMethod执行main方法,程序开始运行。来看下面这张函数调用流程图。

找到应用的入口函数(main),依次调用执行。

最终会调用到dvmCallMethodV这个方法,下面是这个方法的实现。

调用了一个关键的函数dvmIsNativeMethod,这个方法的作用就是判断函数的类型,通过比对方法描述符中的accessFlags与ACC_NATIVE(常量)是否相等来识别该方法是否是NATIVE方法。如下图。

我们现在已经了解了java层方法执行的流程,以及识别Native方法的机理,下面就来具体研究一下如何将一个普通方法注册成一个Native方法。

第二个问题:怎样才能将java层普通方法注册成JNI方法?

上面是xposed.cpp中将普通方法注册为NATIVE方法时所执行的关键一步。将method描述符中的accessFlags修改为NATIVE方法FLAG.

nativeFunc存储该方法实际调用的本地方法的执行位置(注意,考虑到跨平台的问题这里存储的可能是JNIbridge代码执行的位置,在分析xpose框架时暂不需要关注此细节,有Dalvik自行处理),将要hook的java层方法注册成本地方法,Native层方法统一的入口点设置为xposedCallHander。继续往下看。

insns存储该方法真实执行地址(没有注册成Native方法前的执行位置)。

为注册的nativeFunc方法参数列表分配的空间,与java自带方法的等大小即可。

上面两个问题都弄清楚了,下面该来具体研究一下Xposed框架具体做了什么工作。

第三个问题::Xposed框架做了什么?

Xposed修改了app_process程序,在执行第一个java程序(com.Android.internal.os.ZygoteInit)之前进行截获,改变执行流程,进入到自身的main函数体内,这部分java层代码都写在了XposedBridge.jar中。执行的入口点在XposedBridge.jar包中的XposedBridge.java。main函数执行体内主要完成了下面四个部分功能,我们来逐条研究。

initNative是本地方法,完成了Xposed框架的初始化工作。

为了方便Xpsoed框架的native方法对上层java方法的调用,在该部分对相关native方法进行了初始化工作。刚才提到的native层方法xposedCallHander回调的handleHookedMethod真正实现体就放在java层。同时将invokeOriginalMethodNative注册为本地方法,初始化了hook资源文件时用到的对象等。

比较重要。

initXbridgeZygote()主要hook了几个涉及到应用进程创建、启动的关键类。新的应用进程创建时会调用这几个类的特定方法(调用其中的一种,与应用进程创建模式有关系),Xposed框架通过截获应用进程创建时的相关信息来决定对其处理逻辑(是否加载开发人员所写的hook模块,有感兴趣的应用,如微信、相机等创建时,去注册hook模块中所指定的相关方法为本地方法)。

第一处,

下面一段代码是执行自定义代码的示例,

下面这几行代码是hook了xposedInstaller(Xposed框架提供给用户的管理工具)的getActiveXposedVersion方法,如果系统中的Xposed框架没有正常启动,显示的版本号不正确。

第二处,

第三处,

这一处用于处理资源文件hook。

下面在来看看main函数体内做的第三件重要的事情,读取系统中放置的hook模块。简单的说就是把hook模块随便找个地方丢进去,然后把该模块的路径写进conf/modules.list中就可以了。

读取“conf/modules.list”文件中写入的apk名称。

从每一个apk中读取其“/assets/xposed_init”文件中声明的hook主模块类的名称。

classLoader加载各hook主模块类。

判断加载的类属于哪一种类别。

判断加载的类属于哪一种类别,根据类别调用不同的处理方法。

共有三种,常用的IXposedHookLoadPackage

Xposed框架加载完毕后执行将执行权还给com.android.internal.os.ZygoteInit,完成正常的系统启动流程。Xposed框架加载完毕,系统正常启动。

最后,简单总结一下,Xposed框架执行流程:

创建新应用,获取包名等信息。

调用XC_LoadPackage.callAll,依次执行各hook模块的代码。

如果有包名匹配的hook模块,则注册模块中要hook的方法为本地方法。

当该方被调用的时候,转移到本地xposedCallHandler。

xposedCallHandler回调上层handlerHookedMethod(因为加载的hook模块代码,一些变量都存储在java层)。

handlerHookedMethod执行加载的各hook模块。

(不同hook模块可设置优先级,根据优先顺序来执行)

   
3574 次浏览       15
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程