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            #进程端口转发

1557899006483

然后调整IDA的调试选项,如下:

1557899051732

选择对应的进程后,页面跳转,选择modules下的libdvm.so中的dvmDexFileOpenPartial方法,如下:

1557899178517

找到方法后,在其第一行下断点,如下情形。

1557899194095

然后转发jdb。此处jdb不可过早转发,不然下一步运行不到指定地址,将会跳过调试模式进入软件内。

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

附加完成后可以看到CMD卡在回车处暂无回显,在IDA下运行F9到指定断点处自动下断。

1557899222926

单步运行F8一次即可,可以看到寄存器窗口中R0,R1的地址值。

1557899257190

在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成功。

1557899587774

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);
}

参考文章:

IDA动态调试脱壳步骤





# Android逆向  

tocToc: