Loading... > Android Jni调试 ## JNI_OnLoad JNI\_ OnLoad函数大概功能就是在程序加载so的时候,会执行JNI\_ OnLoad函数,做一系列的准备工作。 很多时候,程序猿们会将一些重要信息放在此函数中,而不是通过某种事件来重复触发。包括说将反调试函数放置在此函数中。因此,调试手段发生了改变,上述调试方法基本上被淘汰。 当这种调试手法出现之后,将特殊函数,或者反调试函数放在JNI\_ OnLoad中也不是那么的安全了。此时,程序猿们通过分析系统对SO文件的加载链接过程发现,JNI\_ OnLoad函数并不是最开始执行的。在JNI\_ OnLoad函数执行之前,还会执行init段和init_array中的一系列函数。 因此,现在的调试方法,都是将断点下在init_array中~ 至于下断点的方法,可以类比于在JNI\_ OnLoad中下断点的方法,在init\_ array的函数中下断点。还有一种方法便是通过在linker模块中,通过对其中函数下断点,然后也能单步到init_array中 ## Dump dex文件 关键是找到加载dex文件的位置,在调用函数处下断点,比如dvmDexFileOpenPartia,然后看寄存器的值,容易得知:R0寄存器指向的地址就是dex文件在内存中的地址,R1寄存器就是dex文件的大小。IDA脚本: ```python static main(void) { auto fp, begin, end, dexbyte; fp = fopen("C:\\dump.dex", "wb"); begin = r0; end = r0 + r1; for ( dexbyte = begin; dexbyte < end; dexbyte ++ ) fputc(Byte(dexbyte), fp); } ``` 当然这只是最简单脱壳方法,很多高级壳会动态修改dex的结构体,比如将codeoffset指向内存中的其他地址,这样的话你dump出来的dex文件其实是不完整的,因为代码段保存在了内存中的其他位置。 ## 其他 我们知道在so的加载时候有个这个过程: .init->->.init array->->JNI\_ Onload->->java_ com_XXX; 还有我们在脱壳的过程中会在一些系统级的.so中下断点比如:fopen,fget,dvmdexfileopen,等等 而.init以及.init_array一般会作为壳的入口地方,那我们索性叫它外壳级的.so文件 这里归纳为三类: 应用级别的:java\_ com\_XXX; 外壳级别的:JNI\_ Onload,.init,.init\_array; 系统级别的:fopen,fget,dvmdexfileopen; 对于在应用级别的和系统级别的就不说了比较简单容易理解,这里也是在实现篇中会重点说的,看到上面的.so的加载执行过程我们知道如果说反调试放在外壳级别的.so文件的话我们就会遇程序在应用级核心函数一下断点就退出的尴尬,事实上多数的反调试会放在这,那么过反调试就必须要在这些地方下断点,那么我们就重点的说如何在.init\_ array和JNI_Onload处理下断点。 ``` ``` Last modification:January 16th, 2021 at 01:32 pm © 允许规范转载 Support 确定不打赏一下支持博主吗 ×Close Appreciate the author Sweeping payments Pay by AliPay