近年,Android平台的对抗愈发复杂,逆向工作者为了实现四两拨千斤的效果开始在内核层面使用某些功能或拦截用户态交互信息进行降维打击,同时安全工作者也开始利用内核的各种功能进行安全检测以及设备指纹收集,因此对内核的学习已经成为了Android安全工作者的重要选项。但编译内核需要消耗大量时间,且厂商开源的内核代码可能难以编译或较为陈旧,为了能够在不重新编译内核的情况下使用开源的工具如krhook
,以及修改内核实现规避某些检测,特开此文记录学习过程。
本文所指的内核为 Android 系统使用的 Linux Kernel,它为Android提供了操作系统支持。
操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体,它们互相依赖,不可分割。计算机的硬件,含有外围设备、处理器、内存、硬盘和其他的电子设备组成计算机的发动机。但是没有软件来操作和控制它,自身是不能工作的。完成这个控制工作的软件就称为操作系统,在Linux的术语中被称为“内核”,也可以称为“核心”。Linux内核的主要模块(或组件)分以下几个部分:存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通信,以及系统的初始化(引导)、系统调用等
想要逆向内核首先要提取到内核的二进制文件
这里参照 Ylarod大佬的帖子 https://bbs.pediy.com/thread-271179.htm 提取Boot.img
其中 magiskboot
的获取可以下载https://github.com/topjohnwu/Magisk的Release,在压缩包x86
目录下找到magiskboot
文件
注意该文件是ELF32
类型的,可以在x86(_64)
的Linux发行版上运行,亦可使用WSL2
或参照 https://github.com/nforest/droidimg 使用 imgtool / imjtool进行解包
最终我们可以获取到一个kernel文件( imgtool 解包出的文件名为 kernelimage )
拿到这个文件,直接扔进IDA的话并不会被识别,这里参照 https://github.com/nforest/droidimg 操作使用了脚本,但该脚本未能正确识别出信息(可能需要修改)
以二进制文件载入,选择ARM小端代码,此时IDA要求我们提供地址信息
这里有一个知识点,Linux存在一个内核符号表,其中保存了所有Linux中定义的内核符号地址,那么如果可以利用这个符号表的内容,分析起kernel来将事半功倍
查看kallsyms的方法
STEP1.关闭内核指针限制
echo 0 > /proc/sys/kernel/kptr_restrict
STEP2.读取kallsyms文件
/proc/kallsyms
但是该符号表的内容是内存地址,如何将该符号表对应到kernel文件呢?
这里了解一下kernel文件的前世今生并分析一下如何加载该文件
https://source.android.com/devices/bootloader/partitions-images#kernel-images中提到boot分区下的kernel虚拟分区为标准 Linux 格式
那么magiskboot最后提取到的kernel文件就是vmlinux
那么vmlinux应该是什么格式的呢?
vmlinux实际上是由链接后的文件使用objcopy删去一些内容得到的
在arch\arm64\kernel\vmlinux.lds.S
文件中定义了SECTIONS
信息
其中头部为
TEXT_OFFSET 在Makefile中定义为0x80000
HEAD_TEXT 是一个宏,定义在https://elixir.bootlin.com/linux/v4.9.186/source/include/asm-generic/vmlinux.lds.h#L523
https://elixir.bootlin.com/linux/v4.9.186/source/include/linux/init.h#L94 中可以找到
而__HEAD在arch\arm64\kernel\head.S
中被使用
对比kernel文件头完全符合。
同时在前面找到了_text = KIMAGE_VADDR + TEXT_OFFSET,也就是说kernel在内存中的地址对应_text
的地址
又因为代码段的文件偏移与内存偏移是一致的,所以可以从内核符号表中获取_text
的地址提供给IDA作为起始地址(网上有一些方法设置了文件偏移量是为了跳过前面的对齐数据)
(用这个方法也可以将其他节的地址获取到并定义,如果有这个雅兴)
确认后IDA会提醒我们加载的是一个二进制文件,要提供入口点进行分析
这里我们从内核符号表中获取start_kernel
函数的地址并在IDA中跳转到该地址按C转成代码
可以看到IDA已经分析出很多函数了
看一下start_kernel的汇编
对应启动日志是符合的,地址正确
这时我们可以导出完整的符号表并用脚本载入IDA
导出符号表:cat /proc/kallsyms > /sdcard/kallsyms.txt
adb pull到电脑上后在ida python执行如下脚本
(自动创建代码会很卡,没有需求可以注释掉)
有符号加持基本满足分析内核代码的需求了
这样就可以patch内核来实现某些不可告人的目的了(
参考链接:
绕过Android内核模块加载验证
Android系统内核提取及逆向
Kernel initialization
[Linux内核剖析(五)Linux内核的构建过程]
Android Kernel 逆向分析准备
https://github.com/xiaokanghub/Android-Kenerl-boot.img
纸上得来终觉浅,绝知此事要躬行。
初学内核,如有错误敬请斧正,若有指点感激不尽。
.
=
KIMAGE_VADDR
+
TEXT_OFFSET;
.head.text : {
_text
=
.;
HEAD_TEXT
}
.
=
KIMAGE_VADDR
+
TEXT_OFFSET;
.head.text : {
_text
=
.;
HEAD_TEXT
}
__HEAD
_head:
/
*
*
DO NOT MODIFY. Image header expected by Linux boot
-
loaders.
*
/
/
*
*
This add instruction has no meaningful effect
except
that
*
its opcode forms the magic
"MZ"
signature required by UEFI.
*
/
add x13, x18,
b stext
b stext
/
/
branch to kernel start, magic
.
long
0
/
/
reserved
le64sym _kernel_offset_le
/
/
Image load offset
from
start of RAM, little
-
endian
le64sym _kernel_size_le
/
/
Effective size of kernel image, little
-
endian
le64sym _kernel_flags_le
/
/
Informative flags, little
-
endian
.quad
0
/
/
reserved
.quad
0
/
/
reserved
.quad
0
/
/
reserved
.byte
0x41
/
/
Magic number,
"ARM\x64"
.byte
0x52
.byte
0x4d
.byte
0x64
.
long
pe_header
-
_head
/
/
Offset to the PE header.
.word
0
/
/
reserved
__HEAD
_head:
/
*
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2022-5-23 16:27
被DiamondH编辑
,原因: 修改内容