原文链接 梆梆&爱加密java反调试绕过
各位道友有没有在越过重重native反调试脱掉壳之后想要调试smali的过程中突然发现在使用AndroidStdio动态调试smali的时候只要一挂载APP就会崩溃,本篇主要整理一下在调试梆梆&爱加密过程中遇到的java层反调试以及绕过的方式
在将梆梆&爱加密脱壳之后通过AS调试APP遇到反调试挂载失败,只要debug便会杀掉APP进程
虽然Dalvik已经在逐步退出市场,但是这里还是由他开始,大家都知道Dalvik的核心库是libdvm.so,在调试的过程中与APP建立连接少不了对libdvm.so的引用,因此libdvm.so几乎是最有嫌疑的地方。
所谓获取libdvm.so需要获取APP未启动时libdvm.so源文件,这里只要去/system/lib目录下将其提取到本地即可。
其次需要加固APP运行时libdvm.so,这里我通过IDA动态挂载APP之后将其从内存中dump出来。
static main(void) { auto fp, begin, end, dexbyte; fp = fopen("D:\\dump.so", "wb"); begin = 0x786A0000; end = begin + 0x67000 ; for ( dexbyte = begin; dexbyte < end; dexbyte ++ ) fputc(Byte(dexbyte), fp); }
小小脚本,不成敬意
在获取到两个libdvm.so之后通过BeyondCompare进行比较
通过比较可以看出在加固的APP运行之后,libdvm.so被改了几部分数据,并不是所有修改的地方都和反调试有关,通过IDA对照便宜去看看都改了什么地方,最终发现与java调试相关的地址为0x423B0,当然其他地方也有一些小惊喜,比如梆梆&爱加密抽取函数所hook的函数什么的。
_Z12dvmDbgActivev是java调试相关函数,主要功能是啥我也不是很清楚,有清楚的大佬希望可以留言告知一下,万分感谢。
由此可见加固APP是通过hook _Z12dvmDbgActivev函数使其跳转到它自己构造的函数,并且在其中将进程kill掉防止调试。那么我们只要将Hook之后的libdvm.so再Hook修改为原始函数,让其正常运行_Z12dvmDbgActivev函数,就可以绕过反调试了。
这里通过frida hook _Z12dvmDbgActivev函数地址,打印内存,将原本函数正确内存数据写入对应地址中
# -*- coding: utf-8 -*- import frida import sys def on_message(message, data): print(message) redv = frida.get_remote_device() session = redv.attach("com.sf.activity") src = """ var jnibind1 = undefined; var jnibind2 = undefined; var jnibind3 = undefined; var jnibind4 = undefined; var jnibind5 = undefined; var jnibind6 = undefined; var jnibind7 = undefined; var f = Module.findBaseAddress("libdvm.so"); console.log("address: " + f); jnibind1 = f.add(0x000423C0); var p1P = new NativePointer(jnibind1); aryBuffer1 = Memory.readByteArray(p1P, 16); console.log(aryBuffer1); var vArray = [0x0D,0x4B,0x0E,0x4A,0x7B,0x44,0x10,0xB5,0x9C,0x58,0x94,0xF8,0xC6,0x33]; Memory.writeByteArray(p1P, vArray); var p1P = new NativePointer(jnibind1); aryBuffer1 = Memory.readByteArray(p1P, 16); console.log(aryBuffer1); """ script = session.create_script(src) script.on("message", on_message) script.load() sys.stdin.read()
frida hook之后结果
反调试已经绕过啦!!!
art虚拟机下的反调试绕过同上述的dalvik方法相同,只是关键的运行库libdvm.so替换为libart.so,同样要去/system/lib目录下将其提取到本地,这里需要注意的是在提取之前要确认运行的是libart.so还是libart64.so。
在获取到两个libart.so之后通过BeyondCompare进行比较
最终确认反调试Hook地址为0x10ABC4
_ZN3art3Dbg8GoActiveEv函数功能同Dalvik中_Z12dvmDbgActivev函数功能类似,绕过方法与Dalvik保持一致。
这里通过frida hook _ZN3art3Dbg8GoActiveEv函数地址,打印内存,将原本函数正确内存数据写入对应地址中
# -*- coding: utf-8 -*- import frida import sys def on_message(message, data): print(message) redv = frida.get_remote_device() session = redv.attach("com.sf.activity") src = """ var jnibind1 = undefined; var jnibind2 = undefined; var jnibind3 = undefined; var jnibind4 = undefined; var jnibind5 = undefined; var jnibind6 = undefined; var jnibind7 = undefined; var f = Module.findBaseAddress("libart.so"); console.log("address: " + f); jnibind1 = f.add(0x00115BC4); var p1P = new NativePointer(jnibind1); aryBuffer1 = Memory.readByteArray(p1P, 16); console.log(aryBuffer1); var vArray = [0x2D, 0xE9, 0xF0, 0x4F, 0x9B, 0xB0, 0xDF, 0xF8, 0xBC, 0x0D, 0xDF, 0xF8, 0xBC, 0x1D]; Memory.writeByteArray(p1P, vArray); var p1P = new NativePointer(jnibind1); aryBuffer1 = Memory.readByteArray(p1P, 16); console.log(aryBuffer1); """ script = session.create_script(src) script.on("message", on_message) script.load() sys.stdin.read()
#总结
这里再简述一下反调试的原理,壳APP通过Hook libdvm.so&libart.so中的_Z12dvmDbgActivev&_ZN3art3Dbg8GoActiveEv函数,使其跳转到自己写的函数中执行kill操作导致无法调试。
目前在脱壳之后遇到java反调试,皆可用对比libdvm.so&libart.so寻找差异点将其修复绕过反调试,感兴趣的大佬可以通过其他被hook的函数研究加固或者函数抽取的原理。
[培训]科锐逆向工程师培训班38期--远程教学预课班将于 2020年5月28日 正式开班!
最后于 20小时前 被Night_elf编辑 ,原因: