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

0x1.手机设备环境 Model number: Nexus 5 OS Version: Android 4.4.4 KTU84P Kernel Version: 3.4.0-gd59db4e

0x2.Android内核提取 查找Android设备的boot分区文件。高通芯片的设备可以通过下面的命令进行查找。 cd /home/androidcode/AndroidDevlop/modifyNexus5Boot

adb shell

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

root权限下,用 dd 将其dump到Nexus 5手机的sdcard文件夹下,然后导出到文件 /home/androidcode/AndroidDevlop/modifyNexus5Boot 下: adb shell su

dd if=/dev/block/mmcblk0p19 of=/sdcard/boot.img

exit exit

adb pull /sdcard/boot.img boot.img

使用abootimg工具对boot.img文件进行解包处理,解包之后得到的 zImage 文件即为Android的内核文件。 abootimg工具的github地址:https://github.com/ggrandou/abootimg abootimg工具的直接安装命令:sudo apt-get install build-essential abootimg  abootimg -x boot.img    ls -al

将zImage文件拷贝一份作为文件名为 kernel.gz 的文件,并使用 WinHex 工具查找十六进制 1F 8B 08 00,找到之后把前面的数据部分全删掉,使kernel.gz文件变成标准的gzip压缩文件,这样子就可以使用  gunzip/gzip 命令进行解压内核文件了。 cp ./zImage  ./kernel.gz

# 去掉解包的内核文件kernel.gz中前面的垃圾数据

gzip -d kernel_new.gz

ls -al

使用WinHex查找十六进制数据:

删除掉解包的内核文件kernel.gz中的前面的垃圾数据,然后重新保存修改后的 kernel.gz文件为 kernel_new.gz.

修改后的gzip格式的 kernel_new.gz 文件的解压得到kernel_new内核文件:

提示:关于gzip格式文件的解压,既可以使用 gzip 命令也可以使用 gunzip 命令,都一样。有关 gzip/gunzip 命令的参数使用说明,如下: $ gzip -h Usage: gzip [OPTION]... [FILE]... Compress or uncompress FILEs (by default, compress FILES in-place).

Mandatory arguments to long options are mandatory for short options too.

-c, --stdout      write on standard output, keep original files unchanged   -d, --decompress  decompress   -f, --force       force overwrite of output file and compress links   -h, --help        give this help   -k, --keep        keep (don‘t delete) input files   -l, --list        list compressed file contents   -L, --license     display software license   -n, --no-name     do not save or restore the original name and time stamp   -N, --name        save or restore the original name and time stamp   -q, --quiet       suppress all warnings   -r, --recursive   operate recursively on directories   -S, --suffix=SUF  use suffix SUF on compressed files   -t, --test        test compressed file www.lenk8888.cn       integrity   -v, --verbose     verbose mode   -V, --version     display version www.chuangshi88.cn number   -1, --fast        compress faster   -9, --best        compress www.lieqibiji.com/ better   --rsyncable       Make rsync-friendly www.yghrcp88.cn archive

With no FILE, or when FILE is -, read standard input.

Report bugs to <[email protected]>.

关于gzip文件格式的说明和源码的解析可以参考 gzip文件格式解析及源代码分析,进行深入的研究和学习。

0x3.Android内核文件的逆向修改 将解压后的Android内核文件 kernel_new  拖入到IDA Pro 中进行分析,设置处理器类型为ARM Little-endian。

在 ROM start address 和Loading address 处填上 0xc0008000,然后点击 OK 。

IDA显示效果如下图所示,没有函数名,不方便定位代码,显示不友好需要添加Android内核的内核符号。

为了要获取Android内核中所有的内核符号信息,可以通过在root权限下,修改Andriod设备中的/proc/sys/kernel/kptr_restrict 的值来实现,去掉Android内核符号的信息屏蔽。 adb shell  su    # 查看默认值  cat /proc/sys/kernel/kptr_restrict    # 关闭内核符号屏蔽  echo 0 > /proc/sys/kernel/kptr_restrict     # 查看修改后的值  cat /proc/sys/kernel/kptr_restrict    cat /proc/kallsyms

关闭Android设备的内核符号的屏蔽以后,再次执行 cat /proc/kallsyms ,发现被隐藏的内核符号信息都显示出来了。

在root权限下,将Android设备中的内核符号信息dump出来,导出到 /home/androidcode/AndroidDevlop/modifyNexus5Boot/syms.txt文件中。因此,Android内核文件的内核符号信息都保存在syms.txt文件中了。 # cat /proc/kallsyms > /sdcard/syms.txt    # exit  $ exit    $ adb pull /sdcard/syms.txt syms.txt

我们已经将Androd内核文件中的内核符号信息都dump出来,下面将大有用武之地。因此,向IDA中导入之前提取出来的内核符号信息就可以看到对应的函数名称了。需要用到下面的Python脚本: ksyms = open("C:\Users\Fly2016\Desktop\Android内核的提取和逆向\syms.txt")  for line in ksyms:      addr = int(line[0:8],16)      name = line[11:]      idaapi.set_debug_name(addr,name)      MakeNameEx(addr,name,SN_NOWARN)      Message("%08X:%sn"%(addr,name))

在IDA的 File->Script Command中运行上述python脚本,之后就可以在IDA中成功添加内核符号信息使IDA显示出正确的系统调用的函数名称来。

Android内核中隐藏的系统函数调用的名称在IDA中显示出来了。

现在来聊一聊修改Android的内核文件绕过反调试,很多的Android加固都会通过查看当前进程的 /proc/pid/status 的状态信息,来进行判断当前进程是否被调试的依据。如果当前进程被调试器所调试,那么cat /proc/self/status 显示的状态如下图所示,比较常见的Android反调试也就是通过 TracerPid 的值在调试状态和非调试状态的不同且非调试状态该值为0而调试状态为非0,来判断是否被调试器所调试。

这里修改Android内核绕过反调试也就只是考虑  TracerPid 的值不同的这种情况,真真的也过掉这些检测的反调试还是需要从具体的Android加固的检测逻辑代码入手,没准现在有些Android加固还会检测State的值的不同呢!修改Android内核绕过Android加固的反调试,其实还是要依赖具体的开源的Android内核代码来进行对照着分析,否则根本不知道哪个地方是 /proc/pid/status 的值根据调试状态改变的代码位置,因此这里通过修改Android内核文件绕过反调试还是基于Android内核源码文件 /kernel/msm/fs/proc/array.c 中 的代码实现进行对照着修改的。 /kernel/msm/fs/proc/array.c文件中,检测调试修改TracerPid的值的Android内核源码: /*  * The task state array is a strange "bitmap" of  * reasons to sleep. Thus "running" is zero, and  * you can test for combinations of others with  * simple bit tests.  */ static const char * const task_state_array[] = {  "R (running)",  /*   0 */  "S (sleeping)",  /*   1 */  "D (disk sleep)", /*   2 */  "T (stopped)",  /*   4 */  "t (tracing stop)", /*   8 */  "Z (zombie)",  /*  16 */  "X (dead)",  /*  32 */  "x (dead)",  /*  64 */  "K (wakekill)",  /* 128 */  "W (waking)",  /* 256 */ };

static inline const char *get_task_state(struct task_struct *tsk) {  unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state;  const char * const *p = &task_state_array[0];

BUILD_BUG_ON(1 + ilog2(TASK_STATE_MAX) != ARRAY_SIZE(task_state_array));

while (state) {   p++;   state >>= 1;  }  return *p; }

static inline void task_state(struct seq_file *m, struct pid_namespace *ns,     struct pid *pid, struct task_struct *p) {  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)    // 逆向Android内核文件需要关注的地方    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,   cred->uid, cred->euid, cred->suid, cred->fsuid,   cred->gid, cred->egid, cred->sgid, 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 < min(group_info->ngroups, NGROUPS_SMALL); g++)   seq_printf(m, "%d ", GROUP_AT(group_info, g));  put_cred(cred);

seq_putc(m, ‘\n‘); }

因此,通过上面的Android内核源码的实现可以知道,如图所示的位置是我们应该修改的地方:

通过对Android内核源码的研究知道了我们在Android内核文件中修改的地方,在IDA中通过字符串搜索 TracerPid 即查找上面提到的特征字符串组。

在IDA中通过对特征字符串的引用功能可以定位到我们需要关注的代码的位置。

通过IDA的F5功能分析Android内核根据检测调试状态修改TracerPid值的代码位置。

通过IDA具体细致的看下,我们需要关注的代码位置处的ARM汇编指令。

通过逆向分析代码的流程可以知道,只要 ROM:C02BA5C0 EC FE FF 0A   BEQ  Jmp_C02BA178  处改为直接跳转到地址C02BA178处执行,没有机会执行下面的代码既可以绕过反调试检测。通过IDA的二进制修改的功能,实现了ARM汇编代码的修改,修改后的代码如下图:

Android内核文件kernel_new在修改前后的代码的对比结果示意图:

0x4.将逆向修改的Android内核刷回Android设备 对修改后的Android内核文件 kernel_new 进行gzip的压缩处理得到压缩文件 kernel_new.gz 。 # -n, --no-name     do not save or restore the original name and time stamp # -f, --force       force overwrite of output file and compress links # -9, --best        compress better gzip -n -f -9 kernel_new

使用WinHex工具将kernel_new.gz文件的二进制数据覆盖到原来的zImage文件的 1F 8B 08 00 处的位置开始到结束的地方(新的kernel_new.gz文件必须比原kernel_new.gz文件小,并且回写回去时不能改变原zImage文件的大小及修改原zImage文件中后面的内容,否则会很麻烦),这时得到了zImage文件。 上面这句话,可能不太好理解,但是也很好理解,可以参考一下作者 lcweik 给出的理解的例子:

通过WinHex工具查看kernel_new.gz文件的大小为 0x6AB190,zImage文件中 1F 8B 08 00 处的位置起始偏移为0x48B4,因此在zImage文件中kernel_new.gz文件的起始位置偏移为0x48B4,结束位置偏移为0x6AFA43。使用WinHex工具先将zImage文件中0x48B4~0x6AFA43处的数据删除,然后将kernel_new.gz文件中的数据全部拷贝到0x48B4~0x6AFA43的范围中,即zImage文件中偏移0x48B3后面的位置开始覆盖。

使用abootimg打包工具,重新对解包的boot.img的文件进行打包处理。 abootimg --create myboot.img -f bootimg.cfg -k zImage -r initrd.img

将修改后重新打包的 myboot.img镜像 文件,更新到Android设备上。 adb reboot bootloader fastboot flash boot myboot.img

0x5.手机刷成砖的还原 直接修改Android内核的二进制文件比较危险,很容易导致Android设备变砖的。如果不幸Android设备变砖了,只需要将前面的步骤中备份的原始boot.img镜像文件重新输入Android设备即可。 adb reboot bootloader fastboot flash boot boot.img

0x6.逆向修改Android内核的总结。 这篇博文主要是参考:逆向修改手机内核,绕过反调试,原文的作者方法说的很详细,但是我的操作步骤有些地方和原作者的不同。 1.找目标代码和目标函数的方法不同,原作者通过关闭Android设备中内核符号屏蔽然后拿到关键函数 proc_pid_status_和proc_pid_status_(获取调试器进程的pid)的系统调用的地址,在IDA进行查找定位到需要逆向分析的关键代码的位置。

2.在修改二进制代码绕过反调试的方法上,我和原作者修改的地方稍有一处不同,原作者的修改如下图。

3.按照作者的操作步骤,修改Andorid内核成功绕过反调试耳朵检测,但是我按照自己改进后的操作,修改Android内核成功但是刷机重启直接变砖,哈哈。说实话,这么逆向修改Android内核绕过反调试只是提供一种思路吧,实际干活是吃力不讨好而且要真的绕过这种反调试的检测还需要修改其他的地方,而且其他的检测位置修改也不方便。这种open 情况下的反调试检测,其实手动patch内存过掉也是很简单的事情。

0x7.关于ARM汇编BL指令的计算 ARM汇编下BL类指令的修改以及偏移的计算具体可以参考:【求助】arm指令BL指令对应的机器码问题、ARM中跳转指令BL/BLX偏移值计算规则 ,由于在前面的操作步骤中涉及到B类跳转指令的修改,特此提到一下。提醒两点:1.一定要善于利用IDA能够显示ARM指令机器码的特点,2.在内存中ARM指令的存放是按小尾端存放的。

时间: 2024-10-10 05:00:32

修改Android手机内核,绕过反调试的相关文章

逆向手机内核,添加调试支持和反调试

0x00前言 一个安卓应用可以被调试的条件是应用AndroidManifest.xml显示指定android:debuggable="true",如果没有设置android:debuggable的值,则默认android:debuggable="false",所以发布的应用大部分都是不可调试的,如果要调试,则需要解包,改属性然后重打包,这样非常麻烦,而且效率低.第二个条件是内核配置文件default.prop的属性ro.debuggable=1,这样就不用管应用里面

源码编译绕过反调试

参考师弟的贴子修改的, 基本我一次就弄好了, 没有遇到啥问题, 下面我主要是补充下他的帖子 http://bbs.pediy.com/showthread.php?t=213481 一. 环境搭建 (1). 环境介绍: 手机:nexus 5 ubuntu版本:15.10 android版本:4.4.4 android源码官网(这里有很多相关的资料,基本上按照官网的来就可以编译想编译的任何版本了) http://source.android.com/source/ (2). 环境配置 配置环境得好

android 手机连电脑usb调试 adb devices 显示 unauthorized ,eclipse DDMS offline

android 手机连电脑打开usb调试, adb devices 显示 unauthorized,eclipse中的DDMS中的设备一直是offline状态 你是否已经拔插了手机数据线无数次, 重启过无数遍手机与电脑, 电脑上无数次运行adb kill-server adb start-server, adb shell , 或者用豌豆荚,或者用各种手机连接助手, 还删除了手机上/data/misc/adb/adb_keys, 手机上就是从来都没有弹出过确认key fingerprint 的确

修改android手机文件权限

修改android手机文件权限 默认情况下,一个应用肯定是读取不了另外一个应用的数据的,因为权限不够.但是我们一定要读,怎么办? 修改我们要读取文件的权限. Android是基于Linux的,所以修改权限的方法和Linux大致一样. 只不过手机被生产出来之后,生产商限制了手机的权限,给我们用的是普通用户,比如这样我们就不能删除手机的原生应用.修改手机文件的权限等等操作. 不过我们可以将手机ROOT一下,获取手机的最高权限. 拿到最高权限之后,我们以超级用户的方式进入手机终端. 进入终端之后我们就

修改Android手机的“虚拟机堆大小”和android:largeHeap来防止APP内存溢出问题

使用“RAM Manager”修改“虚拟机堆大小”为某一个阀值 xxMB大小 修改 AndroidManifest.xml 里的 Application 标签的属性 android:largeHeap="true"Attribute "largeHeap" is only used in API level 11(Android 3.0.x) and higher 重启手机,才能生效. adb.exe shell getprop dalvik.vm.heapsize

关于修改Android手机的音量

首先,必须要获取系统的声音服务权限 <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> 然后就是声明控制声音的变量 //通话音量 AudioManager mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 之后就可以通过AudoManager的get和set方法来获得声音以及设置声音了

mac 无法连接android手机进行调试 解决方案

第一步: 查看usb设备信息 在 终端输入:system_profiler SPUSBDataType     可以查看连接的usb设备的信息 比如我的usb信息如下(部分内容): Android: Product ID: 0x2769              Vendor ID: 0x22d9              Version: 2.31              Serial Number: 6e5d48a4              Speed: Up to 480 Mb/sec

mac 下真机调试 android 手机

第一步: 查看usb设备信息 在 终端输入:system_profiler SPUSBDataType     可以查看连接的usb设备的信息 比如我的usb信息如下(部分内容): Spreadtrum phone: Product ID: 0x5d04 Vendor ID: 0x1782 Version:  2.29 Serial Number: 19761202 Speed: Up to 480 Mb/sec Manufacturer: Spreadtrum Location ID: 0xf

MAC下如何配置Android手机调试(将测试手机加入到Mac系统的调试列表中)

第一步: 查看usb设备信息 在 终端输入:system_profiler SPUSBDataType     可以查看连接的usb设备的信息 比如我的usb信息如下(部分内容): 查看到我的Android手机的Vendor ID: 0x04e8,这个id是该手机关联该电脑的一个唯一标识. 第二步: 创建.修改adb_usb.ini文件 输入: vi ~/.android/adb_usb.ini 命令,在打开的 adb_usb.ini文件中添加第一步中的Vendor ID:0x04e8, 然后保