0x00前言
  一个安卓应用可以被调试的条件是应用AndroidManifest.xml显示指定android:debuggable="true",如果没有设置android:debuggable的值,则默认android:debuggable="false",所以发布的应用大部分都是不可调试的,如果要调试,则需要解包,改属性然后重打包,这样非常麻烦,而且效率低。第二个条件是内核配置文件default.prop的属性ro.debuggable=1,这样就不用管应用里面设置的属性了,看来是一个比较好的解决方案,我们只要修改一次内核就可以一劳永逸了。
  安卓应用程序常用的一种反调试手段是查看/proc/[pid]/status下的信息,如果处于调试状态,那么TracerPid的值就是调试进程的Pid,那么程序就会做出相应的行为来反调试。

 

0x01提取内核

查看boot所在的分区



ls -l /dev/block/platform/msm_sdcc.1/by-name



android 内核编译配置 android内核调试_解包

提取内核



dd if= /dev/block/mmcblk0p17 of=/data/local/boot.img adb pull /data/local/boot.img boot.img



解包内核



bootimg.exe --unpack-bootimg



android 内核编译配置 android内核调试_解包_02

解包之后的文件结构

android 内核编译配置 android内核调试_移动开发_03

 

0x02修改ro.debuggable

修改initrd/default.prop文件中的ro.debuggable=1

android 内核编译配置 android内核调试_嵌入式_04

 

0x03修改kernel文件

复制一份kernel为zImage.gz方便后面的修改

用010editor打开zImage.gz查找十六进制1F 8B 08 00,删除前面的所有数据,使文件变成一个标准的gzip压缩文件,这样就可以使用gunzip解包了。

 

android 内核编译配置 android内核调试_解包_05



gunzip zImage.gz



android 内核编译配置 android内核调试_android 内核编译配置_06

解包生成的zImage就是内核二进制文件了。

用IDA打开文件,设置处理器类型为ARM Little-endian

android 内核编译配置 android内核调试_嵌入式_07

设置ROM start address和Loading address为0xc0008000

android 内核编译配置 android内核调试_移动开发_08

在安卓root终端关闭符号屏蔽



echo 0 > /proc/sys/kernel/kptr_restrict



查看proc_pid_status和__task_pid_nr_ns函数地址



cat /proc/kallsyms | grep proc_pid_status



android 内核编译配置 android内核调试_操作系统_09



cat /proc/kallsyms | grep __task_pid_nr_ns



android 内核编译配置 android内核调试_解包_10

为什么要查找这两个函数呢,我们根据源码/kernel/msm/fs/proc/array.c来看一下

android 内核编译配置 android内核调试_解包_11

android 内核编译配置 android内核调试_移动开发_12

函数proc_pid_status内联了task_state函数,在task_state内联函数里面通过函数__task_pid_nr_ns 获取到tracerpid并且打印出来。

在IDA中按快捷键g跳转到函数c0187f88(__task_pid_nr_ns)函数处,按x出来引用搜索框,在其中找到函数c02764b8(proc_pid_status)

android 内核编译配置 android内核调试_android 内核编译配置_13

查看局部的调用为

android 内核编译配置 android内核调试_嵌入式_14

可以看到调用的结果会存储在R11中,所以修改命令MOV R11, R0为MOV R11, #0,机器码为00 B0 A0 E3,文件的偏移为(0xC02765F8-0xC0008000= 26E5F8)

android 内核编译配置 android内核调试_操作系统_15

重新压缩zImage



gzip -n -f -9 zImage



用010editor添加原kernel的首部和尾部二进制数据到文件zImage.gz(新的zImage.gz文件必须比原zImage.gz文件小,并且回写回去时不能改变原kernel文件的大小及修改原kernel文件后面的内容,否则会很麻烦),这时得到了kernel文件。

添加首部3DEB长的数据

android 内核编译配置 android内核调试_解包_16

android 内核编译配置 android内核调试_操作系统_17

先占位,然后复制首部的数据到头部

android 内核编译配置 android内核调试_移动开发_18

添加尾部数据

android 内核编译配置 android内核调试_移动开发_19

替换原先的kernel文件,重新生成新的boot.img



bootimg.exe --repack-bootimg



android 内核编译配置 android内核调试_android 内核编译配置_20

 

0x04刷入新的内核

手机重启到bootloader模式



adb reboot bootloader



刷入新的boot



fastboot flash boot boot-new.img



重启



fastboot reboot



如果手机开不了机,那么重新刷回老的内核



fastboot flash boot boot-old.img



 

0x05SELinux导致IDA无法调试

在安卓端以root权限启动andorid_server之后,并在本机做端口转发,IDA可以正常列出可调试的应用列表,但是当选择某个程序的时候就会出现如下错误

The debugger could not attach to the selected process.

This can perhaps indicate the process was just terminated, or that you dot't have the necessary privileges.

android 内核编译配置 android内核调试_解包_21

关闭SELinux,之后就正常了,不知道是不是MIUI独家的问题。

 

检测SELinux是否打开



getenforce



返回值:Enforcing:强制模式  Permissive:宽容模式  Disabled:关闭

 

临时关闭SELinux



setenforce 0



0为关闭,1为打开,执行后立即生效,无需重启

 

0x06小结

  工欲善其事,必先利其器,有一个基本的调试环境对逆向学习是非常有帮助的。因为很多的手机产商没有将手机系统源代码放出,所以只能采取逆向内核的方式进行修改,如果手机厂商有把系统的源代码放出,那么从源码编译将会更具有修改性,可以定制更多的内核特性。如果手机有开源的安卓系统支持,比如lineage os或者CM的支持,也可以选择这些优秀的开源代码来编译。

 

参考:

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

Android逆向之旅—应用的”反调试”方案解析(附加修改IDA调试端口和修改内核信息)

[原创]支持windows下打包boot/recovery.img的bootimg.exe,且支持自动解包/打包dt.img,加入MTK机型支持

Android反调试笔记

记录下android SELinux造成ida无法调试

修改Android手机内核,绕过反调试