0%

修改系统内核绕过TracerPid检测反调试

背景:最近投了腾讯的实习岗位,得到一个Android的CrackMe来考察逆向知识,分析后发现其关键函数位于native so库中,并且在函数开始时启动了线程进行TracerPid检测的反调试,当发现程序被调试时会直接杀死应用。虽然题目最后卡在了native 逆向与反调试绕过这里,下去后我学习了下如何绕过这个反调试。主要是两种:(1)patch so,直接修改关键值来绕过;(2)修改系统内核文件来将TracerPid写死为0。刚好我手头有一个之前做实验已root的小米6,便决定采用修改系统内核的方式,一劳永逸。

按照著名的“将大象放进冰箱的步骤”,我们也可以概述下我们的步骤:(1)取出内核文件;(2)修改内核文件;(3)修改后的内核刷入。参考了很多文章基本都是差不多的,但是在具体应用到我的Mi6时出现了一点问题,不过最后解决了这些小问题。

提取boot.img

取出boot.img有很多种方式,这里列举几种:

adb shell

boot.img一般位于/dev/block/platform下面的文件夹种,对于我的MI6,如下:

1
2
3
sagit:/ $ su
sagit:/ # ls /dev/block/platform/soc/1da4000.ufshc/by-name/ -l | grep boot
lrwxrwxrwx 1 root root 16 1973-10-08 02:03 boot -> /dev/block/sde40

是一个链接,指向/dev/block/sde40,使用dd写出文件后用adb pull取出即可。

1
2
dd if=/dev/block/sde40 of=/data/local/boot.img
adb pull /data/local/boot.img boot.img

这里如果出现权限问题,需要修改一下boot.img的权限。

REC导出

这里我的MI 6 刷了第三方rec twrp,也可以利用其直接备份分区。

Flashify

也可以使用Flashify这个APP备份kernel。

有一点需要注意的是,以上方式都是需要root手机的。

修改内核文件

取出来的boot.img是一个打包文件,包含了ramdiskkernel等很多东西,首先需要将其解开。这里我使用的是一个github上的工具Android_boot_image_editor,具体使用可以参考一下其readme。、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
➜  Android_boot_image_editor git:(master) ✗ .\gradlew unpack
Starting a Gradle Daemon, 2 incompatible and 1 stopped Daemons could not be reused, use --status for details

> Task :unpack
19:21:57:932 WARN PackableLauncher - [boot.img] will be handled by [BootImgParser]
19:21:58:031 WARN PackableLauncher - 'unpack' sequence initialized
19:21:58:047 INFO BootImgParser - header version 0
19:21:58:062 WARN BootHeaderV2 - BootImgHeader constructor
19:21:58:320 WARN EnvironmentVerifier - 'xz' not installed. Please install it manually to analyze DTB files
19:21:58:428 INFO ZipHelper - decompress(gz) done: build/unzip_boot/ramdisk.img.gz -> build/unzip_boot/ramdisk.img
19:21:58:431 INFO AndroidCpio - Cleaning D:\secTools\android\Android_boot_image_editor\build\unzip_boot\root ...
19:21:58:446 WARN AndroidCpio - root/.backup has improper file mode 000, fix it
19:21:58:451 WARN AndroidCpio - root/.backup/.magisk has improper file mode 000, fix it
19:21:58:468 WARN AndroidCpio - root/config has improper file mode 555, fix it
19:21:58:556 INFO AndroidCpio - cpio trailer found, mode=000001ed
19:21:58:556 INFO Common - ramdisk extracted : build/unzip_boot/ramdisk.img -> build\unzip_boot\root
19:21:58:559 INFO BootV2 - verify type is VB1.0, skip AVB parsing
19:21:58:596 INFO BootV2 -
Unpack Summary of boot.img
┌───────────────────────────────────────┬──────────────────────────────────────┐
│What │Where │
└───────────────────────────────────────┴──────────────────────────────────────┘
┌───────────────────────────────────────┬──────────────────────────────────────┐
│image info │build/unzip_boot/boot.json │
├───────────────────────────────────────┼──────────────────────────────────────┤
│kernel │build/unzip_boot/kernel │
├───────────────────────────────────────┼──────────────────────────────────────┤
│ramdisk │build/unzip_boot/ramdisk.img.gz │
│\-- extracted ramdisk rootfs │build/unzip_boot/root │
└───────────────────────────────────────┴──────────────────────────────────────┘
19:21:58:623 WARN PackableLauncher - 'unpack' sequence completed

BUILD SUCCESSFUL in 22s
8 actionable tasks: 1 executed, 7 up-to-date

最后解包得到下面内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  Android_boot_image_editor git:(master) ✗ cd .\build\unzip_boot\
➜ unzip_boot git:(master) ✗ ls

Directory: D:\secTools\android\Android_boot_image_editor\build\unzip_boot

Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2021/4/16 19:21 root
-a--- 2021/4/16 19:21 1135 boot.json
-a--- 2021/4/16 19:21 16826616 kernel
-a--- 2021/4/16 19:21 5966 ramdisk_filelist.txt
-a--- 2021/4/16 19:21 6792680 ramdisk.img
-a--- 2021/4/16 19:21 2693750 ramdisk.img.gz

下面开始修改文件夹中的kernel文件。这个kernel中也包含了很多东西,需要解开得到我们需要修改的部分。

其实这里就是我看其他人写的博客有出入的地方。参考的博客都说kernel应该是一些其他文件之后附加一个压缩包,但是针对我的实际情况是gzip文件+设备文件。由于此前跟着其他人博客做,没有注意到自己情况的特殊性,绕了很多弯路。

这里我建议先用binwalk看一下kernel是怎么组织的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
sec@fun:~/test_dir$ binwalk kernel

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
11358134 0xAD4FB6 device tree image (dtb)
11734142 0xB30C7E Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
11770255 0xB3998F device tree image (dtb)
12152895 0xB9703F Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
12189492 0xB9FF34 device tree image (dtb)
12566784 0xBFC100 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
12607122 0xC05E92 device tree image (dtb)
12990994 0xC63A12 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
13031757 0xC6D94D device tree image (dtb)
13414353 0xCCAFD1 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
13450891 0xCD3E8B device tree image (dtb)
13838523 0xD328BB Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
13879747 0xD3C9C3 device tree image (dtb)
14263623 0xD9A547 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
14304386 0xDA4482 device tree image (dtb)
14678262 0xDFF8F6 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
14714908 0xE0881C device tree image (dtb)
15102452 0xE671F4 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
15143648 0xE712E0 device tree image (dtb)
15526260 0xECE974 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
15562798 0xED782E device tree image (dtb)
15943262 0xF3465E Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
15980333 0xF3D72D device tree image (dtb)
16360801 0xF9A561 Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system
16397872 0xFA3630 device tree image (dtb)
16785420 0x100200C Unix path: /dev/block/platform/soc/1da4000.ufshc/by-name/system

可以看到,对于我的kernel,其前面部分是一个gzip,后面是一大堆的设备文件。所以我们需要做的是提取出头部的gzip,解压修改后拼接回去。这里可以用python脚本完成,执行后得到一个kernel_core.gz,对其进行解压得到我们需要分析的kernel。

1
2
3
4
with open("./kernel","rb") as f:
content=f.read(0xAD4FB6)
with open("./kernel_core.gz","wb") as f:
f.write(content)

说一下TracerPid检测实现反调试的原理,linux下的进程会在/proc/pid.status下面保存进程的相关信息,当基于ptrace的调试器对其进行调试时,会修改TracerPid值,所以其反调试一般是对此值进行检测实现的。我们绕过需要修改kernel将其固定为0。

首先需要借用ida对其中的字符串进行解析,定位到TracerPid位置。

用ida加载kernel file,处理器类型选择ARM Little-endian [ARM]

fig1

进入IDA后按shift+f12解析字符串,搜索TracerPid定位字符串,偏移为0x155A09B

fig2

找到字符串后需要对其进行修改,这里我用的winhex修改的,将25 64修改为30 09

fig3

至此,修改完内核文件,重命名为kernel。之后就是将内核重新压缩后和原来内核文件剩余的部分拼接起来。先将其重新gzip压缩。

1
2
3
sec@fun:~/test_dir$ gzip ./kernel
sec@fun:~/test_dir$ ls -l | grep kernel
-rw-r--r-- 1 sec sec 11374276 Apr 16 19:30 kernel.gz

然后将这个和原来的拼起来。这里也是写个简单的python脚本处理。

1
2
3
4
5
6
7
8
9
with open("./kernel.gz","rb") as f:
content=f.read()

with open("./kernel","rb") as f:
f.seek(0xAD4FB6)
content += f.read()

with open("./kernel_new","wb") as f:
f.write(content)

将得到的kernel_new放入之前解包的文件夹,并重命名为kernel替换掉原来的kernel文件。

可以顺便修改一下root下面的default.prop文件,将其中的ro.debuggable=0改为ro.debuggable=1,这个就是控制全局调试的开关,不是当前的重点,只是顺手改一下。

之后调用工具重打包,在根目录下得到boot.img.signed文件。

boot.img刷入

将得到的boot.img.signed修改为boot_new.img放入手机的随意一个文件夹,之后转入twrp rec: 安装刷入镜像,选择处理后的镜像刷如即可。

搞定,可以愉快的进行调试了。