本帖最后由 加菲猫 于 2016-1-6 17:33 编辑
写在前面:
0. 主要针对 http://blog.csdn.net/pengyan0812/article/details/46275317 这篇文章进行二次加工,因此算作“Android 分享”,感谢原作者的分享
1. 在实验过程中,原作个别步骤存在问题,因此按照自己的实验过程重新记录实验步骤
2. 文章以实践为主,很少涉及理论知识
3. 由于图片较多,上传过于混乱,因此你可以下载图文排版异常精美的 PDF 版及其对应的 apk 文件
4. 贴后答疑,如果你在实验的过程中存在问题可以留言(不要咨询我特别高深的问题,因为这篇文章都是我瞎编了,我也不会,哈哈哈)
5. 感谢所有乐于分享的人们
6. 附件内容超过 1M,请前往:http://pan.baidu.com/s/1nuyxc9N 密码:d29f
0x00 摘要
Apk脱壳方法有两种:
(1)使用脱壳神器ZjDroid进行脱壳(现在此方法不是很好使,因为很多应用都同步更新自己的防御机制,个人觉得熟练脱壳还是得使用 IDA 进行操练)。
(2)使用 IDA Pro 在 dvmDexFileOpenPartial 这个函数下断点进行脱壳。(大杀技) 加壳能防止源代码被偷窥,但是这只能防止静态分析,无法防止动态调试。不管怎么加壳保护,原始的classes.dex在App运行时都要加载到内存中。所以如果在App加载classes.dex处下个断点,然后再把classes.dex对应内存中的内容抠出来还原成原始的classes.dex文件,就能达到脱壳的目的了。
0x01 实验
(1)以第1届Alictf的EvilApk300(如图0所示)为例,简单介绍一下使用IDA Pro 进行脱壳的步骤
[Bash shell] 纯文本查看 复制代码 [/size][size=16px]adb install C:\Users\Bravelee\Desktop\jscrack.apk[/size]
[size=16px]-------启动服务,开启监听-------[/size]
[size=16px]adb shell[/size]
[size=16px]su root[/size]
[size=16px]chmod 777 /data/local/tmp/android_server[/size]
[size=16px]/data/local/tmp/android_server[/size]
[size=16px]------------端口转发------------[/size]
[size=16px]再次打开一个终端窗口:[/size]
[size=16px]adb forward tcp:23946 tcp:23946[/size]
[size=16px]-----------root 模式下启动该 app----------[/size]
[size=16px]因为加壳了,所以只能查看 manifest.xml 文件才能获取 apk 对应的包名[/size]
[size=16px]adb shell[/size]
[size=16px]su root[/size]
[size=16px]am start -D -n com.ali.tg.testapp/com.ali.tg.testapp.MainActivity[/size]
[size=16px]
如果成功,手机上 app 会弹出 “Waiting For Debugger”
依次点击”Debbuger -> Attach -> Remote ARMLinux/Android debugger”启动IDA Pro中的Android Debbuger
然后在弹出的对话框中点击”Debug options”按钮,将“Suspend on process entry point”,“Suspend on thread start/exit”,“Suspend on library load/unload”这几个选项勾选上,再将Hostname配置为localhost,端口:23946
注意:Ctrl + F 方便查找
- step 3:继续 IDA
脱壳的时候重点关注:dvmDexFileOpenPartial 函数(在该函数处下断点)
依次点击“Debugger -> Debugger windows -> Module list”,找到so文件列表
在Module list中找到libdvm.so这个文件(注意:Ctrl + F 方便查找)
双击libdvm.so,在弹出的函数列表中找到dvmDexFileOpenPartial函数,然后双击该函数就看到dvmDexFileOpenPartial函数的具体实现
在dvmDexFileOpenPartial函数处下断点(F2 下断点)
点击 ida 左上角的绿色运行按钮(F9)
打开 DDMS 工具(android-sdk\sdk\tools\ddms.bat)
使用jdb命令进行调试时,一般选择8700端口,因为8700是默认的调试端口;打开终端窗口,输入:
[Bash shell] 纯文本查看 复制代码 [/size]
[size=16px]jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700[/size]
[align=left][size=16px]
此时 IDA 会弹出 ”Add map…” 窗口(点击 cancel 按钮即可):
此时进程就执行到了dvmDexOpenPartial函数断点处,dvmDexOpenPartial 函数的定义: [C++] 纯文本查看 复制代码 [/size]
[size=16px]/*[/size]
[size=16px]* Create a DexFile structure for a "partial" DEX. This is one that is in[/size]
[size=16px]* the process of being optimized. The optimization header isn't finished[/size]
[size=16px]* and we won't have any of the auxillary data tables, so we have to do[/size]
[size=16px]* the initialization slightly differently.[/size]
[size=16px]*[/size]
[size=16px]* Returns nonzero on error.[/size]
[size=16px]*/[/size]
[size=16px]int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)[/size]
[size=16px]{[/size]
[size=16px] DvmDex* pDvmDex;[/size]
[size=16px] DexFile* pDexFile;[/size]
[size=16px] int parseFlags = kDexParseDefault;[/size]
[size=16px] int result = -1;[/size]
[size=16px] /* -- file is incomplete, new checksum has not yet been calculated[/size]
[size=16px] if (gDvm.verifyDexChecksum)[/size]
[size=16px] parseFlags |= kDexParseVerifyChecksum;[/size]
[size=16px] */[/size]
[size=16px] pDexFile = dexFileParse((u1*)addr, len, parseFlags);[/size]
[size=16px] if (pDexFile == NULL) {[/size]
[size=16px] ALOGE("DEX parse failed");[/size]
[size=16px] goto bail;[/size]
[size=16px] }[/size]
[size=16px] pDvmDex = allocateAuxStructures(pDexFile);[/size]
[size=16px] if (pDvmDex == NULL) {[/size]
[size=16px] dexFileFree(pDexFile);[/size]
[size=16px] goto bail;[/size]
[size=16px] }[/size]
[size=16px] pDvmDex->isMappedReadOnly = false;[/size]
[size=16px] *ppDvmDex = pDvmDex;[/size]
[size=16px] result = 0;[/size]
[size=16px]bail:[/size]
[size=16px] return result;[/size]
[size=16px]}[/size]
[size=16px]
dvmDexFileOpenPartial 函数的原型如下所示:
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
其中 addr表示Dex文件在内存中的起始地址,
len 表示Dex文件的大小,
ppDvmDex是一个指向DvmDex类型的二级指针,具体表示什么,我也不知道 脱壳只用到 addr 和 len 这两个参数,所以需要获取 R0 和 R1 寄存器的值(ARM的传递参数机制规定 R0 保存着函数从左至右的第一个参数,R1 保存着函数从左至右的第二个参数),可以查看到寄存器列表中的内容如图所示:
- step 5:在 IDA 中编写idc脚本dump内存还原dex文件
选择 File -> Script command…
稍等片刻,即可以把 dump 出来的 dex 文件保存在 C 盘根目录
IDC脚本: [C] 纯文本查看 复制代码
auto fp, dex_addr,end_addr;
fp = fopen("C:\\dump.dex","wb");
end_addr = r0 + r1;
for (dex_addr = r0; dex_addr < end_addr; dex_addr ++)
fputc(Byte(dex_addr),fp);
- step 6:使用 JEB 分析dump 出来的 dex 文件[此处内容请参考原文]
本文脱壳核心思想:在 dvmDexFileOpenPartial 函数处下断点,然后动态调试 Apk,待App 运行到断点处后,写一个 idc 脚本将 dex 文件所对应的内存 dump 出来,然后还原成 dex 文件就完成脱壳操作了,最后再分析反编译 dex 所得到的 smali 文件。 |
[1] 听鬼哥说ZJDROID脱壳的简单使用:http://blog.csdn.net/guiguzi1110/article/details/38727753
[2] 安卓逆向学习笔记(9)- 使用IDA Pro进行简单的脱壳 :http://blog.csdn.net/pengyan0812/article/details/46275317
[3] Android应用方法隐藏及反调试技术浅析:http://www.kuqin.com/shuoit/20151012/348473.html
|