前言
上次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
準備工作
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工具
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文件