527900417 发表于 2018-5-2 16:10

逆向修改内核,绕过TracerPID反调试

本帖最后由 527900417 于 2018-11-21 00:39 编辑

### 前言
上次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**
**原文**同时保存在(https://www.xmsec.cc/re-modify-kernel-bypass-antidebug/)
### 准备工作
#### boot.img提取
首先确保有root权限   
先找到目录   
!(http://o9ufz2772.bkt.clouddn.com/201805/FrjzAn1q1REOE1uVjeqAAdRNZ_F7.png)   
将boot导出为boot.img      
```
dd if=/dev/block/mmcblk0p14 of=/data/local/boot.img
adb pull /data/local/boot.img boot.img
```
#### 准备bootimg-tools工具
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目录下生成了相应的二进制执行文件。
!(http://o9ufz2772.bkt.clouddn.com/201805/Fn45BYYDxqbHlZYJM58anzcVoN0M.png)
解开boot.img
!(http://o9ufz2772.bkt.clouddn.com/201805/FnUZUhSF5S9Pzn7fK2Qdd5RIexvb.png)

注:使用其他工具一直提取失败,最后回头来看其实并不重要,如果熟悉boot.img格式的话,手动提取也可以。但是三星的这个文件,试了很多工具,也只有上面提到的这个工具能提取出来,但打包有些问题,后面有相应的解决方法。
#### 提取原始zImage
将kernel文件复制为文件名为zImage.gz的文件,并使用010editor查找十六进制1f 8b 08 00,找到后把前面的数据全删掉,使文件变成标准的gzip压缩文件,这样子就可以使用gunzip解压了。
命令:gunzip zImage.gz   
生成文件就是祼二进制文件zImage。   
!(http://o9ufz2772.bkt.clouddn.com/201805/FkC2yIHjESZO3I01isl_G-oNR0dL.png)
### 内核修改
zImage文件可以直接使用IDA(推荐6.8,7.0的话需要自己尝试定位函数)去打开,但需要设置参数。
!(http://o9ufz2772.bkt.clouddn.com/201805/FoOzIFTNngyh84Kugw9HdBHOq2JG.png)
在上图,设置处理器类型为ARM Little-endian,点ok后,弹下图
!(http://o9ufz2772.bkt.clouddn.com/201805/FiJkPZPoiGsNUOvBy9f8XebfF5BY.png)
在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
```
!(http://o9ufz2772.bkt.clouddn.com/201805/FtRMzOapuBcKrx3TeW9iF0CcGDSp.png)
回到IDA,按g跳转到c0964058(__task_pid_nr_ns)地址处,在光标放在在该函数处:
!(http://o9ufz2772.bkt.clouddn.com/201805/FmrtCdEToGBDhoGtwZOfRcK-HV-J.png)
然后按x,弹出引用搜索框,找到sub_c0888ac0(这里由于解析程度不同,显示和我的会不一样,但是重点在于sub_c0888ac0),双击进入。
!(http://o9ufz2772.bkt.clouddn.com/201805/FmxiBVfo80w0b-WnOCbdwIoOGqCo.png)
如果IDA没有分析出该函数,就进行以下操作:按shift+f12,搜索TracerPid,找到以下项
!(http://o9ufz2772.bkt.clouddn.com/201805/Ft9ttPWjfE0jsR2E2jYMqMZEw-Wf.png)
双击
!(http://o9ufz2772.bkt.clouddn.com/201805/FkP-Bo421kItvfV_cDQsHlkpI3cN.png)
我这里是没有解析出来引用函数的,如果有引用函数,跟进去就可以解析出来sub_c0888ac0了。
在这里,我们强制解析sub_c0888ac0函数,按g输入c0888ac0,如果ida没有识别为函数,按p强制解析。回到c0964058,按x寻找sub_c0888ac0。
跟进去:
!(http://o9ufz2772.bkt.clouddn.com/201805/FvuNVtEkrbS316dOCQ7wmTEcDvtA.png)
根据其他文章的分析和我的实践,得到的修改方法是把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的相应位置。
!(http://o9ufz2772.bkt.clouddn.com/201805/FnAy5kaViLN66ZSB875MZC85tmkc.png)
修改完成,使用twrp rec刷入镜像,选择刷入boot。重启。
### 调试查看
使用ida调试so文件,查看status文件。
!(http://o9ufz2772.bkt.clouddn.com/201805/FhnP1YDYiijeHyCo73Mx6Wabyqqj.png)
更改后
!(http://o9ufz2772.bkt.clouddn.com/201805/FlFP8GeyEiGU_L6ar2KSlmMvY35m.png)
更改成功。
### 变砖补救
但愿还是能够一次成功的,刷内核失败无限重启或者不能开机也比较正常。在尝试过程中失败了三次,只要之前备份了原始的boot.img,变砖之后重新进入rec,刷入原始的boot.img就好。   
如果是使用odin刷入的话,需要将boot.img打包为tar文件才可以。

**ref**
1.[逆向修改手机内核,绕过反调试](https://bbs.pediy.com/thread-207538.htm)
2.(https://blog.csdn.net/u012417380/article/details/73353670)
3.[基于HOOK的Anti-debug调用点trace和Anti-anti](https://bbs.pediy.com/thread-199671.htm)
4.(http://eternalsakura13.com/2018/01/30/ali_re2/)

作为一只菜鸟,搞了好久才将内核更改成功,斗胆与各位分享自己的经历,希望各位大神多指点。

原文同时保存在blog

mrnull 发表于 2018-6-27 20:27

先说一下,这里的教程不适用用于大多数机子,我昨天搞了一晚上了,说几个需要注意的
首先是提取boot,有些机子是另一种格式叫什么mtd好像,提取和刷内核的话我还是推荐用第三方的rec的备份和恢复功能,这里需要关闭md5校验
然后是解压boot,mtk和高通好像都不一样,但是好像有通用的工具,所以这个不算一个坑
然后是解压内核,这里不一定是gzip压缩的,需要用binwalk看个究竟,比如我的三星a5就是用lz4压缩的,这是第一个坑,记好
然后调试,cpu有32和64,所以入口又不一样了
修改,修改我并不怎么会,所以说不出什么
压缩,这个真心是个谜,我昨天晚上解压不修改重新压缩回刷都能开不了机子,所以这个看运气吧
整个过程就是在看运气
最后就是最好别先改东西刷入,先试一下不改什么然后压缩再压缩回刷看看有没有问题,再继续下一步,这个真的有必要
连这个都不行的话,就看你自己了= =反正我是不行,似乎头文件的数据是验证来着,不然我不改什么回刷都能开不了机子

mrnull 发表于 2018-6-27 20:30

然后我贴一下网上找的一段代码吧


static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
                                struct pid *pid, struct task_struct *p)
{
        struct user_namespace *user_ns = seq_user_ns(m);
        struct group_info *group_info;
        int g;
        struct fdtable *fdt = NULL;
        const struct cred *cred;
        pid_t ppid, tpid;
        rcu_read_lock();
        ppid = pid_alive(p) ?
                task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0;
        tpid = 0;
        if (pid_alive(p)) {
                struct task_struct *tracer = ptrace_parent(p);
                if (tracer)
                        tpid = task_pid_nr_ns(tracer, ns);
        }
        cred = get_task_cred(p);
        seq_printf(m,
                "State:\t%s\n"
                "Tgid:\t%d\n"
                "Pid:\t%d\n"
                "PPid:\t%d\n"
                "TracerPid:\t%d\n"
                "Uid:\t%d\t%d\t%d\t%d\n"
                "Gid:\t%d\t%d\t%d\t%d\n",
                get_task_state(p),
                task_tgid_nr_ns(p, ns),
                pid_nr_ns(pid, ns),
                ppid, tpid,
                from_kuid_munged(user_ns, cred->uid),
                from_kuid_munged(user_ns, cred->euid),
                from_kuid_munged(user_ns, cred->suid),
                from_kuid_munged(user_ns, cred->fsuid),
                from_kgid_munged(user_ns, cred->gid),
                from_kgid_munged(user_ns, cred->egid),
                from_kgid_munged(user_ns, cred->sgid),
                from_kgid_munged(user_ns, cred->fsgid));
        task_lock(p);
        if (p->files)
                fdt = files_fdtable(p->files);
        seq_printf(m,
                "FDSize:\t%d\n"
                "Groups:\t",
                fdt ? fdt->max_fds : 0);
        rcu_read_unlock();
        group_info = cred->group_info;
        task_unlock(p);
        for (g = 0; g < group_info->ngroups; g++)
                seq_printf(m, "%d ",
                           from_kgid_munged(user_ns, GROUP_AT(group_info, g)));
        put_cred(cred);
        seq_putc(m, '\n');
}

Ericky 发表于 2018-5-2 16:23

https://bbs.pediy.com/thread-207538.htm

527900417 发表于 2018-5-2 16:53

Ericky 发表于 2018-5-2 16:23
https://bbs.pediy.com/thread-207538.htm

对,ref第一条便是您说的看雪的这篇文章。其中几个具体的交叉引用可能无法识别,有一些小改动。感谢。

牛奶君 发表于 2018-5-2 17:07

boot.img中有两个1f 8b 08 00。( ̄(工) ̄)这可怎么办

527900417 发表于 2018-5-2 17:37

牛奶君 发表于 2018-5-2 17:07
boot.img中有两个1f 8b 08 00。( ̄(工) ̄)这可怎么办

可以查一下bootimg.h的结构信息,开头是一个header,之后第一个出现的是kernel,一般情况下第一个1f 8b 08 00就是kernel的位置。

6767 发表于 2018-5-2 22:23

锁死ptraceID的一个风险是,如果有应用ptrace ME去检测就会露馅。。。
不过到目前还没有遇到过这样检测的

神一样的小白 发表于 2018-5-2 23:30

看不懂。感觉好牛逼

chenjingyes 发表于 2018-5-3 00:17

感谢楼主分享哈哈哈{:1_921:}

刘留留 发表于 2018-5-3 05:19

学习了,谢谢分享

sstm 发表于 2018-5-3 09:04

看看,了解了解。
页: [1] 2 3 4 5 6 7
查看完整版本: 逆向修改内核,绕过TracerPID反调试