android程序启动:Zygote

​ android系统中应用程序都是由zygote孵化出来的,zygote由init进程启动,zygote在进程启动时都会创建一个dalvik虚拟机实例,每当他孵化一个新的应用程序时都会将这个dalvik虚拟机实例复制到应用程序里面去,从而使每一个应用进程都有一个独立的dalvik虚拟机实例。

​ zygote除了会在应用程序启动时创建一个虚拟机实例,还会将Java运行时库加载到进程中去,以及注册一些Android核心类JNI方法到创建的dalvik虚拟机实例中去。一个应用程序被创建出来时,不仅会有一个独立的dalvik虚拟机实例,还会与zygote共享一个Java运行时库。这也是我们把XposedBridge加载到每一个android进程中去的原因,XposedBridge有一个私有的Native方法HookMethodNative,这个函数在app_process中使用。这个函数提供一个方法对象利用Java的Reflection机制来对内置方法覆写。

Xposed原理

Xposed核心思想是将Java层方法注册为本地Native方法,以此来实现hook机制

hook/replace

​ 在android启动时,zygote会加载XposedBridge将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,xposedCallHandler再转入handleHookedMethod这个Java方法执行用户规定的HookFunc。

1

​ XposedBridge这个jar包含有一个私有的本地方法:hookMethodNative,该方法在附加的app_process程序中也得到了实现。它将一个方法对象作为输入参数(你可以使用Java的反射机制来获取这个方法)并且改变Dalvik虚拟机中对于该方法的定义。它将该方法的类型改变为native并且将这个方法的实现链接到它的本地的通用类的方法。换言之,当调用那个被hook的方法时候,通用的类方法会被调用而不会对调用者有任何的影响。在hookMethodNative的实现中,会调用XposedBridge中的handleHookedMethod这个方法来传递参数。handleHookedMethod这个方法类似于一个统一调度的Dispatch例程,其对应的底层的C++函数是xposedCallHandler。而handleHookedMethod实现里面会根据一个全局结构hookedMethodCallbacks来选择相应的hook函数

Dalvik识别JNI方法

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

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

(2)它有哪些参数,

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

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

方法描述符的结构体:

2

accessFlags标记了该方法的类型:public、native等

Dalvik虚拟机执行代码基本过程:一个类在执行之前先会被装载,之后要进行字节码验证,检验完成之后虚拟机会紧接着调用FindClass()来查找并装载main的方法类。此后JNIEnv类调用成员函数CallStaticVoidMethod执行main方法,程序开始运行。

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

3

dvmCallMethodV这个方法是关键,该方法源码:

4

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

5

以上是如何识别native方法,Xposed在这个流程中改变method描述符中的accessFlags为NATIVE方法FLAG.

6

继续分析上图,其中nativeFunc存储该方法实际调用的本地方法的执行位置,将要hook的java层方法注册成本地方法,Native层方法统一的入口点设置为xposedCallHander。

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

总结

以上内容为xposed工作原理的简介,由于没有结合源码所以理解不够么深刻,总结起来的流程就是,xposed通过替换zygote将java方法执行修改为xposed中的native方法的执行,并把原来方法作为参数传递进去,这就意味着,每一次这个被hook的方法将被调用之前就会被取而代之地调用,同时调用者并不知情。并且将传输参数到这个方法,引用等当中。 而且这个方法会检测到已经在这个方法里面注册过的唤醒回调信号。 这样一来, 我们就可以修改当前方法调用的参数,修改实例化的或者静态的变量, 唤醒其他方法, 根据返回的结果去干一些事情…当然也可跳过任何东西.,一切都显得非常灵活。

Comments

⬆︎TOP