Dalvik 虚拟机的 dvmCallJNIMethod 方法
Android安全交流群:478084054
这个方法很重要,Dalvik虚拟机在执行native函数时都会经过这个函数。dvmCallJNIMethod先准备参数,再调用dvmPlatformInvoke执行JNI方法指令,实际的指令地址保存在method->insns中。
很多app都会将重要操作放到so中,比如加密算法等,所以在这个方法上下点功夫,能够帮助我们了解到相关app的很多重要信息,比如它在哪个关键节点调用了哪个关键的native函数。甚至对于某某银行app,在这个方法上进行hook,能够直接获取到登录密码。

EncryptPwd就是在用户登录时捕捉到的native函数调用信息,传入的参数“naicha_987”就是登录时输入的密码。
为什么说Dalvik虚拟机在执行native函数时都会调用dvmCallJNIMethod?这里结合源码进行分析。
1)对于调用RegisterNatives主动注册的JNI方法
RegisterNatives的实现在“/dalvik/vm/Jni.cpp”文件中。

该函数调用dvmRegisterJNIMethod对每个JNI方法进行注册。

dvmRegisterJNIMethod又调用dvmUseJNIBridge来完成JNI函数注册。

看dvmUseJNIBridge的注释就明白了,JNI方法注册实际上是将Dalivk里面表示一个方法的Method对象的nativeFunc成员设置成了bridge函数,如果不打开CheckJni开关的话,bridge函数就是dvmCallJNIMethod,而实际的native方法指令赋值给Method对象的insns成员。
所以Dalvik在执行主动注册的JNI方法时,肯定是通过bridge函数dvmCallJNIMethod来完成,因为这个Method对象的nativeFunc指向的就是dvmCallJNIMethod。
这里接着把dvmSetNativeFunc的代码也贴一下:

2)对于Dalvik根据特定规则来查找的JNI方法
JNI函数也可以不主动注册,而是根据一定的规则进行命名,然后由Dalvik自己去查找对应的实现指令。对于这类JNI方法,Dalvik在加载类时会将对应的Method对象的nativeFunc成员设置为dvmResolveNativeMethod函数地址。那么在执行该JNI方法时,就会首先执行dvmResolveNativeMethod,它的实现在“/dalvik/vm/Native.cpp”文件中。

这里只贴出关键代码,dvmResolveNativeMethod先遍历查找(借助dlopen/dlsym)所有已加载的so,找到JNI函数地址后,再调用dvmUseJNIBridge函数设置Method的nativeFunc和insns成员,这个函数上面已经介绍过了,最后再调用“*method->nativeFunc”,所以还是先调用dvmCallJNIMethod,再由dvmCallJNIMethod执行相应的JNI方法。