1、dvmDexFileOpenPartial脱壳原理
基于libdvm.so下dvmDexFileOpenPartial的文件脱壳,当然如果是新版的加固方式一般不可行,因为会基本会重写dvmDexFileOpenPartial方法。如此,先了解一下libdvm.so的方法dvmDexFileOpenPartial是干什么的。
加密dex文件在软件运行时,必然会解密加载到内存中运行,而dvmDexFileOpenPartial方法的参数:
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
第一个参数代表dex文件的基地址,第二个参数代表dex文件的长度,第三个参数代表出参,dex文件的类,方法等信息。
方法的源码解读:从源码看Dex Dump于dvmDexFileOpenPartial原理
断点脱壳原理分析:dvmDexFileOpenPartial断点脱壳原理分析
示例APP:jscrack
由于使用的Android 4.4.4的环境,加载模式为Dalvik虚拟机模式,libdvm.so则是此模式下的文件,当然在Android 5.0以上取消了Dalvik模式,自然也不存在libdvm.so文件。dalvik虚拟机会把dex文件优化为odex文件,dvmDexFileOpenPartial则用来解析内存中优化过的dex文件,此时dex已经加载进内存,所以就可以dump出来了。
2、示例演示
首先调试模式启动APP,
adb forward tcp:23946 tcp:23946 #IDA端口转发
adb shell am start -D -n 包名/activity名 #调试模式启动APP
adb shell ps | grep 包名 #获取APP的PID
adb forward tcp:8700 jdwp:pid #进程端口转发
然后调整IDA的调试选项,如下:
选择对应的进程后,页面跳转,选择modules下的libdvm.so中的dvmDexFileOpenPartial方法,如下:
找到方法后,在其第一行下断点,如下情形。
然后转发jdb。此处jdb不可过早转发,不然下一步运行不到指定地址,将会跳过调试模式进入软件内。
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
附加完成后可以看到CMD卡在回车处暂无回显,在IDA下运行F9到指定断点处自动下断。
单步运行F8一次即可,可以看到寄存器窗口中R0,R1的地址值。
在file选项中点击script command运行如下脚本。则可以在D盘下看到dump出来的dex文件。
static main(void){
auto fp, dex_addr, end_addr;
fp = fopen("D:\\dump.dex", "wb");
end_addr = R0 + R1;
for ( dex_addr=R0; dex_addr < end_addr; dex_addr ++ )
fputc(Byte(dex_addr), fp);
}
选择jadx打开dex文件,可以看到源码已dump成功。
3、系统差异
此处使用Android 4.4.4,如果使用Android 5.0以上不存在lindvm.so,则需要在libart.so中对Openmemory函数下断,同样操作,保存R1,R2的值,R1代表基地址,R2代表长度。
脚本为
static main(void){
auto fp, dex_addr, end_addr;
fp = fopen("D:\\dump.dex", "wb");
end_addr = R1 + R2;
for ( dex_addr=R1; dex_addr < end_addr; dex_addr ++ )
fputc(Byte(dex_addr), fp);
}
参考文章: