本文博客链接:http://blog.csdn.net/qq1084283172/article/details/57086486

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 integrity
-v, --verbose verbose mode
-V, --version display version number
-1, --fast compress faster
-9, --best compress better
--rsyncable Make rsync-friendly archive With no FILE, or when FILE is -, read standard input. Report bugs to <bug-gzip@gnu.org>.

关于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指令的存放是按小尾端存放的。

参考资料

逆向修改手机内核,绕过反调试 
<主要参考>

提取Android内核的方法

【求助】arm指令BL指令对应的机器码问题

ARM中跳转指令BL/BLX偏移值计算规则

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

  1. Windows下修改Android手机的hosts

    Windows下修改Android手机的hosts 1.首先,手机是Root过的. 2.连接手机和电脑, adb shell 进入命令行. 3.获取root用户权限: su -root 4.不知道为何 ...

  2. angr进阶(6)绕过反调试

    angr绕过反调试,一个是通过之前的方式,使用从特定位置开始测试的方法,还有一种通过hook进行反调试的方法. 其原理就在于angr能够符号化表示函数tumctf2016_zwiebe p.hook_ ...

  3. 修改android手机文件权限

    修改android手机文件权限 默认情况下,一个应用肯定是读取不了另外一个应用的数据的,因为权限不够.但是我们一定要读,怎么办? 修改我们要读取文件的权限. Android是基于Linux的,所以修改 ...

  4. Android系统移植与调试之------->如何修改Android手机NFC模块,使黑屏时候能够使用NFC

    我们都知道在不修改源代码的情况下,只能是解锁之后才能使用NFC功能.而在锁屏和黑屏2个状态下是没办法用NFC的,但是最近有个客户要求手机在黑屏状态下能够使用NFC,因此我们需要去修改Android源代 ...

  5. 解决Android加固多进程ptrace反调试的思路整理

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53613481 一.Android多进程反调试的原理代码 当ptrace附加目标进程 ...

  6. appium常见问题05_修改Android手机运行环境(adb指令修改hosts)

    自动化测试过程中,手机有时会跳网,怎样保持手机测试的环境稳定性,可以通过adb指令修改android手机hosts,保持手机运行在hosts中配置的环境中: 修改方法如下: 前提条件:已安装andro ...

  7. Android系统移植与调试之------->如何修改Android手机显示的4G信号强度的格子数

    在修改显示的信号强度之前,先了解一下什么是dB,什么是dBm? 1.dB dB是一个表征相对值的值,纯粹的比值,只表示两个量的相对大小关系,没有单位,当考虑甲的功率相比于乙功率大或小多少个dB时, 按 ...

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

    使用“RAM Manager”修改“虚拟机堆大小”为某一个阀值 xxMB大小 修改 AndroidManifest.xml 里的 Application 标签的属性 android:largeHeap ...

  9. hbuilder在android手机里用chrome调试,只显示了设备名称,却没有inspect按钮

    stark 通过“菜单”->“工具”->“检查设备”打开设备检查页面,只显示了设备名称,却没有inspect按钮,要怎么办 1 赞2014-10-09 22:00 ============ ...

随机推荐

  1. 剑指 Offer 60. n个骰子的点数 + 动态规划 + 空间优化

    剑指 Offer 60. n个骰子的点数 Offer_60 题目详情 题解分析 package com.walegarrett.offer; /** * @Author WaleGarrett * @ ...

  2. 一起来学习LiteOS中断模块的源代码

    摘要:本文带领大家一起剖析了LiteOS中断模块的源代码. 本文我们来一起学习下LiteOS中断模块的源代码,文中所涉及的源代码,均可以在LiteOS开源站点https://gitee.com/Lit ...

  3. [BJWC2018] Kakuro

    一.题目 点此看题 二.解法 我一开始一直想不出来,直接刚这个题实在是太复杂了,因为一开始就是不合法的. 下次遇到复杂的题一定要想 调整法 ,我再不往这个方向想我吔屎 好了言归正传,我们先找一组可行的 ...

  4. NET 5.0 Swagger API 自动生成MarkDown文档

    目录 1.SwaggerDoc引用 主要接口 接口实现 2.Startup配置 注册SwaggerDoc服务 注册Swagger服务 引用Swagger中间件 3.生成MarkDown 4.生成示例 ...

  5. Java进阶专题(二十七) 将近2万字的Dubbo原理解析,彻底搞懂dubbo (下)

    ...接上文 服务发现 服务发现流程 整体duubo的服务消费原理 Dubbo 框架做服务消费也分为两大部分 , 第一步通过持有远程服务实例生成Invoker,这个Invoker 在客户端是核心的远程 ...

  6. 【JVM进阶之路】八:性能监控工具-命令行篇

    定位问题的时候,知识.经验是关键基础,数据是依据,工具是运用知识处理数据的手段. 在实际的故障排查.性能监控中,常常是操作系统的工具和Java虚拟机的工具结合使用. 1.操作系统工具 1.1.top: ...

  7. 痞子衡嵌入式:从头开始认识i.MXRT启动头FDCB里的lookupTable

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT启动头FDCB里的lookupTable. 一个MCU内部通常有很多外设模块,这些外设模块是各MCU厂商做差异化产品的本质, ...

  8. 翻译:《实用的Python编程》07_05_Decorated_methods

    目录 | 上一节 (7.4 装饰器) | 下一节 (8 测试和调试) 7.5 装饰方法 本节讨论一些与方法定义结合使用的内置装饰器. 预定义的装饰器 在类定义中,有许多预定义的装饰器用于指定特殊类型的 ...

  9. 死磕Spring之AOP篇 - Spring AOP常见面试题

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  10. Dynamics CRM报表点击自动运行方法

    当我们点击了报表后一般会进入到条件筛选界面,再点击运行报表才可以直接运行报表.有一个方法可以点击报表后直接运行报表. 文本编辑器打开报表的rdl文件 找到如下位置的代码: 把Value部分改为: &l ...