- 对本书的赞誉
- 前言
- 基础篇
- 第 1 章 Android 中锁屏密码加密算法分析
- 第 2 章 Android 中 NDK 开发
- 第 3 章 Android 中开发与逆向常用命令总结
- 第 4 章 so 文件格式解析
- 第 5 章 AndroidManifest.xml 文件格式解析
- 第 6 章 resource.arsc 文件格式解析
- 第 7 章 dex 文件格式解析
- 防护篇
- 第 8 章 Android 应用安全防护的基本策略
- 第 9 章 Android 中常用权限分析
- 第 10 章 Android 中的 run-as 命令
- 第 11 章 Android 中的 allowBackup 属性
- 第 12 章 Android 中的签名机制
- 第 13 章 Android 应用加固原理
- 第 14 章 Android 中的 so 加固原理
- 工具篇
- 第 15 章 Android 逆向分析基础
- 第 16 章 反编译神器 apktool 和 Jadx
- 第 17 章 Hook 神器 Xposed
- 第 18 章 脱壳神器 ZjDroid
- 第 19 章 Native 层 Hook 神器 Cydia Substrate
- 操作篇
- 第 20 章 静态方式逆向应用
- 第 21 章 动态调试 smali 源码
- 第 22 章 IDA 工具调试 so 源码
- 第 23 章 逆向加固应用
- 第 24 章 逆向应用经典案例分析
- 第 25 章 Android 中常见漏洞分析
- 第 26 章 文件加密病毒 Wannacry 样本分析
23.2 获取解密之后的 dex 文件
分析完上面的破解流程之后,发现首要的任务是先得到源 apk 程序,通过分析知道,处理源 apk 程序很难找到,所以就要引出本章的内容了:使用动态调试,给 libdvm.so 中的函数 dvmDexFileOpenPartial 下断点,然后得到 dex 文件在内存中的起始地址和大小,然后 dump 出 dex 数据即可。
这里会有人提出几个问题。
第一个问题:为何要在 dvmDexFileOpenPartial 函数下断点?
不管之前的源程序如何加固,放到哪了,最终都是需要被加载到内存中然后运行,而且是没有加密的内容,那么只要找到 dex 的内存位置,把这部分数据弄出来就可以了,管它之前是如何加固的,那么问题就变成了,如何获取加载到内存中的 dex 的地址和大小。这要用到函数 dvmDexFileOpenPartial,因为这个函数是最终分析 dex 文件,加载到内存中的函数:
第一个参数是 dex 内存起始地址,第二个参数是 dex 大小。
第二个问题:如何使用 IDA 给这个函数下断点
在前一章中介绍了动态调试 so,下断点的时候必须知道一个函数在内存中的绝对地址,而函数的绝对地址是:这个函数在 so 文件中的相对地址+so 文件映射到内存中的基地址。这个函数肯定是存在 libdvm.so 文件中的,因为一般涉及 dvm 有关的函数功能都是存在这个 so 文件中,那么可以从这个 so 文件中找到这个函数的相对地址,运行程序之后,再找到 libdvm.so 的基地址,相加即可。如何获取到 libdvm.so 文件呢?这个文件是存放在设备的/system/lib 目录下的:
只需要使用 adb pull 命令把这个 so 文件弄出来就可以了。
解决了这两个问题,下面就开始操作了。
第一步:运行设备中的 android_server 命令,使用 adb forward 进行端口转发:
这里的 android_server 工具可以去 ida 安装目录中 dbgsrv 文件夹中找到:
第二步:使用命令以 debug 模式启动 apk:
因为需要给 libdvm.so 下断点,这个库是系统库,所以加载时间很早。需要像之前给 JNI_OnLoad 函数下断点一样,采用 debugger 模式运行程序,这里通过上面的 AndroidManifest.xml 中得到应用的包名和入口 Activity:
而且这里的 android:debuggable=true 可以进行 debug 调试的。
第三步:双开 IDA,一个用于静态分析 libdvm.so,一个用于动态调试 libdvm.so。
通过 IDA 的 Debugger 菜单,如图 23-6 所示。进行进程附加操作,如图 23-7 所示。
图 23-6 设置调试端口
图 23-7 搜索附加进程
第四步:使用 jdb 命令启动连接 attach 调试器:
但是这里可能会出现这样的错误:
这是因为 8700 端口没有指定,这时候可以通过 Eclipse 的 DDMS 进行端口查看,如图 23-8 所示。
图 23-8 查看进程调试端口
这里是 8600 端口,但是基本端口 8700 不在。有两种处理方式,一种方式是把上面的命令的端口改成 8600,还有一种方式是选中这个应用,使其具有 8700 端口,如图 23-9 所示。
图 23-9 设置 8700 端口
点击这个条目即可,这时候再运行上面的 jdb 命令:
处于等待状态。
第五步:给 dvmDexFileOpenPartial 函数下断点。
使用一个 IDA 静态分析得到这个函数的相对地址 43308,如下所示:
在动态调试的 IDA 中进行解密,使用 Ctrl+S 键找到 libdvm.so 在内存中的基地址 41579000,如图 23-10 所示。
图 23-10 函数的基地址
然后将两者相加得到绝对地址 43308+41579000=415BC308,使用 G 键,跳转,如图 23-11 所示。
图 23-11 跳转到绝对地址
跳转到 dvmDexFileOpenPartial 函数处,下断点,如下所示:
第六步:点击运行按钮或者 F9 键运行程序。
之前的 jdb 命令就连接上了:
IDA 出现一些提示界面,不要理会,一路点击 Cancel 按钮即可,如图 23-12 所示。
图 23-12 加载 so 文件对话框
运行到了 dvmDexFileOpenPartial 函数处,如下所示:
使用 F8 键进行单步调试。这里需要注意的是,只要运行过了 PUSH 命令就可以了,记得不要越过下面的 BL 命令,因为没必要走到那里。当执行了 PUSH 命令之后,就是使用脚本来 dump 出内存中的 dex 数据了。这里有一个知识点:R0~R4 寄存器一般是用来存放一个函数的参数值的,而 dvmDexFileOpenPartial 函数的第一个参数就是 dex 内存起始地址,第二个参数就是 dex 大小,如下所示:
这里就可以使用这样的脚本进行 dump 操作:
脚本不解释了,非常简单,而且这是固定的格式,以后 dump 内存中的 dex 都是用这段代码,将 dump 出来的 dex 保存到 F 盘中。
然后使用 Shirt+F2 键调出 IDA 的脚本,如图 23-13 所示。
图 23-13 dump 出 dex 文件
点击 Run,这里可能需要等一会。运行成功之后,去 F 盘得到 dump.dex 文件。到这里,IDA 使命就完成了,因为得到了内存的 dex 文件了,下面只要分析 dex 文件即可。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论