上面就简单分析了爱加密的原理和流程,但是我们没有继续往下面分析了,因为这个不是我们今天讲解的重点,我们今天的重点是如何脱掉爱加密的壳,那么还是开始说到的,脱壳的核心就一个:给dvmDexFileOpenPartial函数下断点,dump出内存的dex文件即可,那么下面我们就是用IDA开始脱壳操作了:
第一步:启动设备中的android_server,然后进行端口转发
adb forward tcp:23946 tcp:23946
15.png (7.03 KB, 下载次数: 10)
2016-6-12 09:03 上传
第二步:用debug模式启动程序
adb shell am start -D -n com.droider.crackme0201/.MainActivity
这里的包名和入口Activity都可以在上面反编译之后的AndroidManifest.xml中找到
16.png (10.03 KB, 下载次数: 9)
2016-6-12 09:03 上传
第三步:双开IDA,一个用于静态分析libdvm.so,一个用于动态调试
17.png (116.22 KB, 下载次数: 12)
2016-6-12 09:03 上传
记录dvmDexFileOpenPartial函数的相对地址:4777C
再次打开一个IDA,进行attach调试进程
18.png (33.9 KB, 下载次数: 13)
2016-6-12 09:03 上传
第四步:使用jdb命令attach上调试器
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
19.png (10.44 KB, 下载次数: 11)
2016-6-12 09:03 上传
第五步:对dvmDexFileOpenPartial函数下断点
进入调试页面之后,Ctrl+S查找libdvm.so的内存基地址:415BB000
20.png (41.73 KB, 下载次数: 11)
2016-6-12 09:03 上传
在第三步得到相对地址:4777C+415BB000=4160277C 得到了dvmDexFileOpenPartial在内存中的绝对地址
注意:
当然这里还有一个更方便的办法:
就是直接打开Modules View:
21.png (34.14 KB, 下载次数: 10)
2016-6-12 09:03 上传
在这里查找libdvm.so文件:
22.png (17.1 KB, 下载次数: 10)
2016-6-12 09:03 上传
然后双击libdvm.so文件:
查找需要下断点的函数名称,看到这里的绝地地址也是:4160277C
这里有两种方式可以得到一个函数在内存中的绝对地址。
然后我们使用G键,直接跳转到函数处,下断点:
23.png (17.71 KB, 下载次数: 11)
2016-6-12 09:03 上传
第六步:设置Debugger Options选项
能够让程序断在dvmDexFileOpenPartial函数处
24.png (18.03 KB, 下载次数: 11)
2016-6-12 09:03 上传
注意:
上面的第四步,第五步,第六步,没有顺序的,只要在运行之前设置到就可以了。
第七步:运行程序
出现这个对话框,不要在意,一路点击Cancel即可
25.png (37.98 KB, 下载次数: 14)
2016-6-12 09:03 上传
jdb也attach上了调试程序:
26.png (30.73 KB, 下载次数: 8)
2016-6-12 09:03 上传
我们一路点击运行按钮,知道运行到dvmDexFileOpenPartial处的断点,但是可惜的是,这里我们遇到了错误:
27.png (16.86 KB, 下载次数: 11)
2016-6-12 09:03 上传
我们点击OK之后,出现了下面对话框:
28.png (30.06 KB, 下载次数: 8)
2016-6-12 09:03 上传
再次点击任何一个按钮,都会退出了调试页面:
29.png (27.64 KB, 下载次数: 9)
2016-6-12 09:03 上传
我们在重新尝试一次上面的流程,开始调试,但是错误是一样的,好了,到这里我们就立马想到了,之前说的IDA调试so的那篇文章遇到的那个问题:反调试检测
当时我们也是遇到这个情况,在没有运行到我们下的断点处,就退出了调试页面,其实这个是现在加固平台必要选择的一种方式,其实反调试原理很简单,就是在程序运行最早的时机比如so加载的时候即:JNI_OnLoad方法中,读取本进程的status文件,查看TracerPid字段是否为0,如果不为0,那么就表示自己的进程被别人跟踪了,也就是attach了,那么这时候立马退出程序,下面我们使用IDA在attach进程成功之后,查看本进程的status信息:
30.png (19.94 KB, 下载次数: 10)
2016-6-12 09:03 上传
看到这里的TracerPid为11340,不为0,表示被11340进程attach了,那么我们可以查看一下这个进程是谁:
31.png (20.16 KB, 下载次数: 11)
2016-6-12 09:03 上传
其实这个进程就是我们在设备中安插的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,然后在找到这两个函数,就可以得到他们的绝对地址了
32.png (9.74 KB, 下载次数: 12)
2016-6-12 09:03 上传
然后使用G键,跳转下断点即可:
33.png (16.88 KB, 下载次数: 9)
2016-6-12 09:03 上传
第二种方式:也是最简单的方式,就是G键,本身就有可以直接输入函数名进行跳转的功能
34.png (20.47 KB, 下载次数: 9)
2016-6-12 09:03 上传
下断点:
35.png (22.61 KB, 下载次数: 10)
2016-6-12 09:03 上传
看到了吧,这种方式是不是非常简单高效
好了到这里就给这两个函数下好了断点,当然这里还需要给dvmDexFileOpenPartial函数下断点,一切弄好了之后,这时候我们再次运行:
36.png (19.08 KB, 下载次数: 9)
2016-6-12 09:03 上传
停在了fopen断点处,我们使用F8单步调试,看到R7寄存器中的内容是/proc/...,我们直接点击R7查看全部内容:
37.png (27.29 KB, 下载次数: 11)
2016-6-12 09:03 上传
内容有点长,大致的内容是:/proc/self/cmdline.debug.atrace.app_cmdlines,这个是干什么的?
我们看看这个目录内容:
38.png (24.06 KB, 下载次数: 11)
2016-6-12 09:03 上传
发现没有这个文件内容,只有cmdline文件,但是这里先不管他了,我们知道这个肯定不是读取status文件的,那我们直接略过这个断点,点击F9运行到下一个断点,中间过程先忽略,一路F9,直到运行到了fopen这个断点:
39.png (14.73 KB, 下载次数: 11)
2016-6-12 09:03 上传
果然,这里使用了fopen来读取status文件了,点击R7寄存器查看全部内容:
40.png (25.29 KB, 下载次数: 10)
2016-6-12 09:03 上传
这个16396就是我们本进程的id:
41.png (18.39 KB, 下载次数: 11)
2016-6-12 09:03 上传
到这里,我们知道下一个断点肯定是fgets,所以点击F9进入到fgets断点处:
42.png (11.08 KB, 下载次数: 9)
2016-6-12 09:03 上传
这里还看不到什么信息,我们继续点击F8单步调试:
44.png (22.2 KB, 下载次数: 10)
2016-6-12 09:03 上传
途中,会看到有memchr和memcpy这两个重要函数,这个也是操作字符串的核心点,继续往下走:
45.png (22.83 KB, 下载次数: 10)
2016-6-12 09:03 上传
到了fgets函数结束的地方,我们看到了R0寄存器的内容是Name...点击R0查看全部内容:
46.png (20.74 KB, 下载次数: 11)
2016-6-12 09:03 上传
全部内容是:Name: der.crackme0201;这个就是status文件的第一行内容:
47.png (13.37 KB, 下载次数: 9)
2016-6-12 09:03 上传
到这里,我们知道了,开始读取status文件的每行内容了,但是到TracerPid那行还要继续执行5次fgets函数,所以还会进入5次断点,为了节省时间,这里点击5次F9,直接运行到读取TracerPid那行的内容的fgets断点处:
48.png (22.17 KB, 下载次数: 9)
2016-6-12 09:03 上传
看到了关键的内容了TracerPid字段了,这时候,我们打开Hex View 查看16进制的内存数据:
49.png (17.92 KB, 下载次数: 10)
2016-6-12 09:03 上传
但是我们看到,这个并没有和调试页面View位置相对应,我们可以这么操作:
在寄存器窗口查看到R0寄存器的内容: