前言
上次DDCTF结束后,和BinCrack师傅交流,他提到用了自己编译的安卓内核,直接修改源码更改了TracerPID,之后在国赛也遇到了对status的检测,虽然可以修改内存绕过(ref 4),但是还是想试一试把内核patch掉。查阅了很多资料后,大概有三种方法:
1.修改源码,重新编译内核 (ref 2)
2.逆向修改内核文件,patch二进制文件
3.hook fopen 函数,检测/proc/pid/status调用 (ref 3)
比较了几种方法的工作量和自己的基础知识(并没有),选择了第二种方法,以看雪的一篇文章最为典型,但由于机型和版本的区别,遇到了几个坑,找到了一个较为通用的方法。
以下工作基于samsung note3 9008,armeabi-v7a2,第三方官改rom,5.0,twrp rec
原文同时保存在blog
准备工作
boot.img提取
首先确保有root权限
先找到目录
将boot导出为boot.img
dd if=/dev/block/mmcblk0p14 of=/data/local/boot.img
adb pull /data/local/boot.img boot.img
bootimg-tools工具是一款基于mkbootimg开发的boot.img 解包重打包C语言工具,github地址:https://github.com/pbatard/bootimg-tools
git clone https://github.com/pbatard/bootimg-tools.git
下载后进入bootimg-tools目录,执行make 命令编译该项目,在 makebootimg目录下生成了相应的二进制执行文件。
解开boot.img
注:使用其他工具一直提取失败,最后回头来看其实并不重要,如果熟悉boot.img格式的话,手动提取也可以。但是三星的这个文件,试了很多工具,也只有上面提到的这个工具能提取出来,但打包有些问题,后面有相应的解决方法。
提取原始zImage
将kernel文件复制为文件名为zImage.gz的文件,并使用010editor查找十六进制1f 8b 08 00,找到后把前面的数据全删掉,使文件变成标准的gzip压缩文件,这样子就可以使用gunzip解压了。
命令:gunzip zImage.gz
生成文件就是祼二进制文件zImage。
内核修改
zImage文件可以直接使用IDA(推荐6.8,7.0的话需要自己尝试定位函数)去打开,但需要设置参数。
在上图,设置处理器类型为ARM Little-endian,点ok后,弹下图
在ROM start address和Loading address填0xc0008000,点ok,没有函数名,不方便定位代码。
先回到root下的adb shell,输入命令:
echo 0 > /proc/sys/kernel/kptr_restrict
关闭符号屏蔽
再输入以下命令查看这两函数的地址
echo 0 > /proc/sys/kernel/kptr_restrict
cat /proc/kallsyms |grep proc_pid_status
cat /proc/kallsyms |grep __task_pid_nr_ns
回到IDA,按g跳转到c0964058(__task_pid_nr_ns)地址处,在光标放在在该函数处:
然后按x,弹出引用搜索框,找到sub_c0888ac0(这里由于解析程度不同,显示和我的会不一样,但是重点在于sub_c0888ac0),双击进入。
如果IDA没有分析出该函数,就进行以下操作:按shift+f12,搜索TracerPid,找到以下项
双击
我这里是没有解析出来引用函数的,如果有引用函数,跟进去就可以解析出来sub_c0888ac0了。
在这里,我们强制解析sub_c0888ac0函数,按g输入c0888ac0,如果ida没有识别为函数,按p强制解析。回到c0964058,按x寻找sub_c0888ac0。
跟进去:
根据其他文章的分析和我的实践,得到的修改方法是把MOVEQ R10, R0替换为MOV R10, #0,机器码为00 A0 A0 E3,指令的文件偏移为(C0889110-C0008000),及把下面第三行的BL sub_C0964058替换为MOV R0, #0,机器码为00 00 A0 E3,指令的文件偏移为(C0889120-C0008000)。根据偏移地址在文件里使用010修改或者使用keypatch插件直接修改。
内核文件修改成功。
另,在更改的过程中联想到直接修改TracerPid的格式字符串值,因为不太熟悉,感觉可能会导致占位符后移而报错,就没考虑这个思路,之后也发现确实可以这样做。原始格式化字符串内容为:\t%s\nTgid:\t%d\nPid:\t%d\nPPid:\t%d\nTracerPid:\t0\t\nUid:\t%d\t%d\t%d\t%d\nGid:\t%d\t%d\t%d\t%d\n
这里应该用到了C语言中的占位符%d,来进行值的填充,那么我们可以把TracerPid那一项的占位符%d,改成'0',但是'%d'是两个字符,所以我们可以改成'00',或者'0\t'(十六进制30 09),或者'0\n';只要保证修改后的字符串内容对齐就好。这样TracerPid这一项的值占位符就失效了,值永远都是0了。
打包boog.img
使用之前的解包时提供的打包方法,打包后刷入失败..
最后探索到的方法是直接修改原boot.img
使用gzip -n -f -9 zImage
压缩修改后的内核裸文件,压缩后会比原来的小,必须比原来的文件小才可以。
得到zImage.gz,我们使用010分别打开zImage.gz和boot.img,搜索1F 8B 08 00。
按下insert键,将010改为overwrite,注意这里必须是覆盖,这样就不用考虑插入后大小的问题了,把zImage.gz的内容复制到boot.img的相应位置。
修改完成,使用twrp rec刷入镜像,选择刷入boot。重启。
调试查看
使用ida调试so文件,查看status文件。
更改后
更改成功。
变砖补救
但愿还是能够一次成功的,刷内核失败无限重启或者不能开机也比较正常。在尝试过程中失败了三次,只要之前备份了原始的boot.img,变砖之后重新进入rec,刷入原始的boot.img就好。
如果是使用odin刷入的话,需要将boot.img打包为tar文件才可以。
ref
1.逆向修改手机内核,绕过反调试
2.Android系统内核编译及刷机实战 (修改反调试标志位)
3.基于HOOK的Anti-debug调用点trace和Anti-anti
4.Android动态调试和ptrace反调试之读取进程status文件