返回介绍

24.2 脱壳过程

发布于 2024-10-10 22:32:23 字数 10135 浏览 0 评论 0 收藏 0

上面简单分析了加密的原理和流程,但是没有继续往下面分析了,因为这不是本章讲解的重点,本章的重点是如何脱掉加密的壳。脱壳的核心就一个:给 dvmDexFileOpenPartial 函数下断点,dump 出内存的 dex 文件即可,那么下面就用 IDA 开始脱壳操作了。

第一步:启动设备中的 android_server

进行端口转发:

第二步:用 debug 模式启动程序

代码如下:

这里的包名和入口 Activity 都可以在上面反编译之后的 AndroidManifest.xml 文件中找到:

第三步:双开 IDA

一个用于静态分析 libdvm.so,一个用于动态调试,如图 24-7 所示。

图 24-7 获取函数相对地址

记录 dvmDexFileOpenPartial 函数的相对地址 4777C,再次打开一个 IDA,进行 attach 调试进程,如图 24-8 所示。

图 24-8 附加进程

第四步:使用 jdb 命令 attach 上调试器

代码如下:

第五步:对 dvmDexFileOpenPartial 函数下断点

进入调试页面之后,用 Ctrl+S 键查找 libdvm.so 的内存基地址 415BB000,如图 24-9 所示。

图 24-9 获取 so 的基地址

将第三步中获取到的相对地址加上基地址 4777C+415BB000=4160277C,得到了 dvmDex-FileOpenPartial 在内存中的绝对地址。

注意,这里还有一个更方便的办法,就是直接打开 Modules View,如图 24-10 所示。

图 24-10 Modules 视图

在这里查找 libdvm.so 文件,如图 24-11 所示。

然后双击 libdvm.so 文件,如图 24-12 所示。

图 24-11 查找 libdvm.so 文件

图 24-12 获取函数的绝对地址

查找需要下断点的函数名称,看到这里的绝对地址也是 4160277C。这里有两种方式可以得到一个函数在内存中的绝对地址。然后使用 G 键,直接跳转到函数处,下断点,如下所示:

第六步:设置 Debugger Options 选项

此操作能够让程序断在 dvmDexFileOpenPartial 函数处,如图 24-13 所示。

图 24-13 设置 Debugger Options 选项

注意:上面的第四步、第五步、第六步没有顺序,只要在运行之前设置就可以了。

第七步:运行程序

出现这个对话框不要在意,一路点击 Cancel 即可,如图 24-14 所示。

图 24-14 提示对话框

jdb 也 attach 上了调试程序:

一直点击运行按钮,直到运行到 dvmDexFileOpenPartial 处的断点,但是可惜的是,这里遇到了错误,如图 24-15 所示。

图 24-15 运行报错

点击 OK 之后,出现了下面对话框,如图 24-16 所示。

图 24-16 报错对话框

再次点击任何一个按钮,都会退出调试页面,如图 24-17 所示。

图 24-17 退出调试页面

再重新尝试一次上面的流程,开始调试,但是错误是一样的,到这里就立马想到了,之前第 8 章说的 IDA 调试 so 遇到的那个问题:反调试检测。其实这是现在加固平台必要选择的一种方式,反调试原理很简单,就是在程序运行最早的时机,比如 so 加载的时候即 JNI_OnLoad 方法中,读取本进程的 status 文件,查看 TracerPid 字段是否为 0,如果不为 0,那么就表示自己的进程被别人跟踪了,也就是 attach 了,这时候立马退出程序,下面使用 IDA 在 attach 进程成功之后,查看本进程的 status 信息:

看到这里的 TracerPid 为 24336,不为 0,表示被 24336 进程 attach 了,那么可以查看一下这个进程是谁:

这个进程就是在设备中安插的 android_server,它用于和 IDA 进行通信。

到这里,可以看到应用做了反调试检测,按照上一章的内容,可以给 JNI_OnLoad 函数下断点,然后找到检测代码,把对应的 ARM 指令改成空指令,检测失效了,但是这里两个 so 文件被处理了,IDA 没法分析了,那么该怎么办呢?

如何应对反调试呢?可以借助 IDA 修改寄存器和内存数据的特性来做到。首先上面分析了反调试的原理,一般在 native 代码去做检测的话,都是用 fopen 系统函数打开 status 文件,然后用 fgets 函数读取一行的内容,一般操作文件都是用 fopen 函数。

那么思路就有了,既然反调试肯定用到了 fopen 和 fgets 这两个函数,那么直接像给 dvmDexFileOpenPartial 下断点的方式一样,给这两个函数下断点,然后运行到 fgets 断点处的时候,发现如果是读取 TracerPid 这行内容的时候,就开始修改内存内容,把 TracerPid 字段的值改成 0,或者修改 R0 寄存器的内容,跳过反调试检测。

这两个函数是在 libc.so 文件中的,可以把设备的/system/lib/libc.so 使用 adb pull 到本地即可,然后用 IDA 得到相对地址,在调试页面得到基地址,然后相加得到绝对地址,跳转即可。但是这里不用这种复杂的方式,有两种方式可以进行跳转。

第一种方式:在 Modules 界面找到 libc.so,然后再找到这两个函数,就可以得到它们的绝对地址了,如图 24-18 所示。

图 24-18 查看 fopen 函数的绝对地址

然后使用 G 键,跳转下断点即可,如下所示:

第二种方式:也是最简单的方式,就是用 G 键,本身就有可以直接输入函数名进行跳转的功能,如图 24-19 所示。

图 24-19 输入函数名跳转

下断点,如下所示:

到这里就给这两个函数下好了断点,当然这里还需要给 dvmDexFileOpenPartial 函数下断点,一切弄好了之后,这时候再次运行,如下所示:

停在了 fopen 断点处,使用 F8 键单步调试,看到 R7 寄存器中的内容是/proc/...,直接点击 R7 查看全部内容,如下所示:

内容有点长,大致的内容是/proc/self/cmdline.debug.atrace.app_cmdlines,这个是干什么的?看看这个目录内容:

发现没有这个文件内容,只有 cmdline 文件,但是可以先不管它了,知道这个肯定不是读取 status 文件的,那直接略过这个断点,点击 F9 键运行到下一个断点,中间过程先忽略,一路用 F9 键,直到运行到了 fopen 这个断点,如下所示:

果然,这里使用了 fopen 来读取 status 文件了,点击 R7 寄存器查看全部内容,如下所示:

这个 16396 就是本进程的 id:

到这里,下一个断点肯定是 fgets,所以点击 F9 键进入到 fgets 断点处,如下所示:

这里还看不到什么信息,继续点击 F8 键单步调试,如下所示:

途中,会看到有 memchr 和 memcpy 这两个重要函数,这个也是操作字符串的核心点,继续往下走,如下所示:

到了 fgets 函数结束的地方,看到了 R0 寄存器的内容是 Name,点击 R0 查看全部内容,如下所示:

全部内容是 Name:der.crackme0201,这就是 status 文件的第一行内容:

开始读取 status 文件的每行内容了,但是到 TracerPid 那行还要继续执行 5 次 fgets 函数,所以还会进入 5 次断点。为了节省时间,这里点击 5 次 F9 键,直接运行到读取 TracerPid 那行的内容的 fgets 断点处,如下所示:

看到关键的内容 TracerPid 字段了,这时候打开 Hex View 查看 16 进制的内存数据,如下所示:

但是看到,这并没有和调试页面 View 位置相对应,查看数据很费劲,所以可以这么操作,在寄存器窗口查看到 R0 寄存器的内容,如下所示:

这里就是 TracerPid 字段在内存的地址,记录一下,然后在 Hex View 页面中使用 G 键,进行跳转,一定要注意是在 HexView,而不是调试页面,调试页面使用 G 键跳转到的是指令地址了,如图 24-20 所示。

图 24-20 Hex View 页面

这里看到 TracerPid 的内存内容了,就开始修改吧,选择要修改的内容:是 11340 那里,如图 24-21 所示。

图 24-21 修改寄存器值

选择内容开始处,右击,选择 Edit,进入修改状态,如下所示:

改了之后的内容是橘黄色的,修改完成之后,再点击右键,选择 Apply changes,如图 24-22 所示。

完成修改,颜色变成灰土色了,如下所示:

注意:这里其实还可以直接修改寄存器 R0 的值,如图 24-23 所示。

图 24-22 保存设置

图 24-23 直接修改寄存器值

这时候就修改成功了,继续使用 F8 键单步调试下去,如下所示:

到这里就开始把 TracerPid 字段的值和 0 进行比较了,点击 R0 寄存器查看全部内容,如下所示:

这里的值已经被改成了 0,所以就骗过去了。继续运行会发现,又进入了 fopen 函数的断点处,而且查看还是读取 status 文件。这个也不好奇,因为反调试检测肯定是一个轮询机制,所以肯定会反复地读取这个文件,fopen 走多次也是正常的,但是这个反调试肯定是在子线程的,所以只要到了主线程中解密 dex 文件肯定到了 dvmDexFileOpenPartial,这里会多次重复上面的操作,修改多次 TracerPid 的值。这里就不演示了。在操作的过程中修改了三次,当没有走 fopen 函数的时候,遇到了这个错误,这里不用关心,直接点击 OK 就可以了,如图 24-24 所示。

图 24-24 警告对话框

再次点击运行,如下所示:

这里说明已经开始解密 dex 文件了,应该离成功不远了,继续运行,如下所示:

终于到了想要的地方了,到这里就好办了,直接点击 Shirt+F2 键,打开脚本运行窗口,运行下面脚本,如图 24-25 所示。

图 24-25 运行脚本

把内存中的 dex 保存到 F:\dump.dex 中。这里 R0 寄存器就是 dex 在内存中的起始地址,R1 寄存器就是 dex 文件的大小,如图 24-26 所示。

图 24-26 查看 dex 文件的起始地址和文件大小

使用 G 键,可以在 HexView 页面中查看 R0 寄存器中的地址内容,如下所示:

这里就是 dex 的头文件格式。

得到了内存中的 dex 数据之后,可以使用 baksmali 工具转化成 smali 源码,查看代码逻辑即可,这里不再演示了。最后还有一步:还原 apk。下一节介绍。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文