【创宇小课堂】渗透测试-Frida逆向入门之native层Hook

admin 2022年2月15日19:53:00评论178 views字数 4799阅读15分59秒阅读模式

该文章来自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层相关代码


【创宇小课堂】渗透测试-Frida逆向入门之native层Hook


mainactivity代码如下

【创宇小课堂】渗透测试-Frida逆向入门之native层Hook


Static块中使用System.loadLibrary()来加载目标动态库文件到内存。最终生成的动态库文件名为lib+目标名+.so,这里就是libnatice-lab.so


native关键词修饰的方法即为JNI函数。函数内容由C/C++语言实现。使用crtl加左键进入函数主题

【创宇小课堂】渗透测试-Frida逆向入门之native层Hook


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 查看被加载到内存的模块


【创宇小课堂】渗透测试-Frida逆向入门之native层Hook

确认内存中由该模块,使用如下命令查看该模块的导出函数

memory list exports libnative-lib.so


【创宇小课堂】渗透测试-Frida逆向入门之native层Hook

函数对应的绝对地址为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偏移量

【创宇小课堂】渗透测试-Frida逆向入门之native层Hook

最后写出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)


运行结果

【创宇小课堂】渗透测试-Frida逆向入门之native层Hook


Hook SO文章

https://www.jianshu.com/p/b052e0fe3af5

原文始发于微信公众号(安全宇宙):【创宇小课堂】渗透测试-Frida逆向入门之native层Hook

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年2月15日19:53:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【创宇小课堂】渗透测试-Frida逆向入门之native层Hookhttps://cn-sec.com/archives/779424.html

发表评论

匿名网友 填写信息