该文章来自R0ysue书籍SO HOOk的总结
11.1 Native基础
11.1.1 NDK基础介绍:
1、安卓开发中除了java编译的dex由ART/Davilk虚拟机执行外、还存在C/C++编译的动态链接库文件由CPU执行
2、JAVA依赖SDK、C/C++需要NDK依赖库
3、NDK关键之一JNI、JNI可以完成在java调用C/C++函数。也可以在C/C++中调用java函数
4、JNI是JVM虚拟机的一部分
11.2.1 NDK开发
创建项目,会比不使用NDK的多cpp目录,cmaklists.txt用于指明C/C++代码如何编译。CPP用于存放native层相关代码
mainactivity代码如下
Static块中使用System.loadLibrary()来加载目标动态库文件到内存。最终生成的动态库文件名为lib+目标名+.so,这里就是libnatice-lab.so
native关键词修饰的方法即为JNI函数。函数内容由C/C++语言实现。使用crtl加左键进入函数主题
1、Native函数从Java的String变为JString
2、native函数名从java中的stringFromJNI变为Java_com_example_sohook_MainActivity_stringFromJNI。命名规则为java+报名+类名+方法名。是为了能够让Java层找到对应的C/C++函数
3、JNIEXPORT表示可被外部调用,即可导出函数
4、JNICALL是一个空声明
5、extern c表示以C命名规则,否则为C++,存在名称粉碎规则。可以使用c++filt进行还原
6、JNIENV*表示当前java线程的执行环境、通过这个JNIEnv* 指针,就可以对Java端的代码进行操作
7、如果native方法不是static的化,obj表示这个方法的类对象、否之为该方法的类
8、JNI层全局只有一个JAVAVM,在JNI_Oload函数参数中,Jni_onload函数在加载so文件后第一个运行,也是一定会运行的函数
JNIEXPORT jint JNI_OnLoad(JAVAVM* vm,void* reserved)
11.1.3 JNI函数逆向的基本流程
1、由C/C++编译生成的so文件存在lib目录下的相关架构中,使用Uname -a查看架构
2、JNI逆向过程中、首先需要找到在java层函数在Native层中对应的函数地址
3、Android系统为了快速找到JNI函数,起命名规则是固定的,可以使用objection 查看
memory list module 查看被加载到内存的模块
确认内存中由该模块,使用如下命令查看该模块的导出函数
memory list exports libnative-lib.so
函数对应的绝对地址为0x7133cd01dc,该地址在每次运行会变动,不变的是偏移量,
7、可以通过偏移量+基地址找到对应函数。
11.2 Frida native层Hook
11.2.1 native层Hook基础
在Frida脚本中实现native层Hook的API函数是interceptor.attch()函数,他的第一个参数是要Hook的函数地址、第二个参数是callbacks回调。回调中存在俩个函数:
onEnter()函数是在调用目标Hook函数前调用的处理函数、被Hook函数的参数以数组的形式放在args参数中。
onLeave()函数则是在被Hook的目标函数执行完成后执行的函数、被Hook的函数返回值就是retval
function hook_native(){ var addr = Module.getExportByName("libnative-lib.so", "Java_com_roysue_r0so_MainActivity_stringFromJNI"); Interceptor.attach(addr,{ onEnter:function(args){ console.log("jnienv pointer =>",args[0]) console.log("jobj pointer =>",args[1]) },onLeave:function(retval){ console.log("retval is =>",Java.vm.getEnv().getStringUtfChars(retval, null).readCString()) console.log("=================") } }) }
Naive Hook的要点就是Hook函数的首地址
11.2.2 Native层Hook-Hook函数首地址定位方法
0x01 导出函数
1、使用Module.getExportByName(moduleName|null,exportname)或者Module.findExportByName(moduleName|null,exportName)获取相应函数首地址
2、第一个参数为模块名、第二个参数是导出函数名,若第一个参数为null。则会在所有加载在内存中的模块搜索相应导出函数名。不为空则在该模块下
HookstringFromJNI函数代码如下
function hook_native(){ var addr = Module.getExportByName("libnative-lib.so", "Java_com_roysue_r0so_MainActivity_stringFromJNI"); Interceptor.attach(addr,{ onEnter:function(args){ console.log("jnienv pointer =>",args[0]) console.log("jobj pointer =>",args[1]) },onLeave:function(retval){ console.log("retval is =>",Java.vm.getEnv().getStringUtfChars(retval, null).readCString()) 使用frida提供的API函数java.vm.getenv函数获取当前线程的Jenv结构,使用GetStringUtfChars获取java的字符串对应的C字符串地址 最后通过readCString函数来读取内存中的C字符串 console.log("=================") } })
0x02非导出函数定位函数地址
1、通过Module.findBaseAddress(name)或者Module.getBaseAddress(name)俩个函数来获取模块基地址、使用add(offset)函数传入固定的偏移获取函数绝对地址
2、动态注册JNI函数和静态注册JNI函数。动态注册函数在naive层实现的函数名称不定、使用Jint RegisterNative(jclass clazz, const JNINativeMethod* methods,jint nMethods)
3、clazz参数使用FindClass JNI函数获取。
4、methods参数是一个数组、其中包含函数的签名信息和对应在native层的函数指针
4、nmethods参数是methods数组的数量
动态注册JNI函数
JNIEXPORT jstring JNICALL stringFromJNI3( JNIEnv* env, jobject /*this*/) { std::string hello = "Hello from C++ stringFromJNI3 r0ysue "; return env->NewStringUTF(hello.c_str()); } jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv * env; vm->GetEnv((void**)&env,JNI_VERSION_1_6); JNINativeMethod methods[] = { {"sI3","()Ljava/lang/String;",(void*)stringFromJNI3}, }; env->RegisterNatives(env->FindClass("com/roysue/r0so/MainActivity"),methods,1); return JNI_VERSION_1_6; }
此时再使用objection就无法找到该函数地址
4、偏移量的获取、通过HOok实现动态注册Registerxxx来获取动态注册后的stringFormJNI3函数
5、使用frida_hook_libart,该项目包含JNI函数和art函数的hook脚本,这里使用hook_RegisterNatives.js
hook到stringFromJNI3偏移量
最后写出hook对应JNI函数的脚本
function hook_native(){ var addr = Module.getExportByName("libnative-lib.so", "Java_com_roysue_r0so_MainActivity_stringFromJNI"); Interceptor.attach(addr,{ onEnter:function(args){ console.log("jnienv pointer =>",args[0]) console.log("jobj pointer =>",args[1]) },onLeave:function(retval){ console.log("retval is =>",Java.vm.getEnv().getStringUtfChars(retval, null).readCString()) console.log("=================") } }) } function hook_native3(){ var libnative_addr = Module.findBaseAddress('libnative-lib.so'); console.log("libnative_addr is => ",libnative_addr) var stringfromJNI3 = libnative_addr.add(0xf444); console.log("stringfromJNI3 address is =>",stringfromJNI3); Interceptor.attach(stringfromJNI3,{ onEnter:function(args){ console.log("jnienv pointer =>",args[0]) console.log("jobj pointer =>",args[1]) // console.log("jstring pointer=>",Java.vm.getEnv().getStringUtfChars(args[2], null).readCString() ) },onLeave:function(retval){ console.log("retval is =>",Java.vm.getEnv().getStringUtfChars(retval, null).readCString()) console.log("=================") } }) } function main(){ hook_native3() } setImmediate(main) function main(){ hook_native() } setImmediate(main)
运行结果
Hook SO文章
https://www.jianshu.com/p/b052e0fe3af5
原文始发于微信公众号(安全宇宙):【创宇小课堂】渗透测试-Frida逆向入门之native层Hook
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论