前一章节我们了解如何编译内核及如何进行模拟调试, 除了完成了模拟环境的搭建, 其中有个重要的环节就是使用kptools进行内核Patch.
kptools就像一个拿手术刀的医生, 小心解剖着内核文件, 往身体里面塞入了kpimg和kpatch这两个外挂器官, 同时心脏搭桥(hook), 血液循环, 让外挂器官寄生在内核里面, 这神乎奇迹的刀法, 上次大约还是在周星驰的007里面见过.
可想而知, 这个医生绝对需要对内核文件了如指掌, 才能完成这样绝妙的工作. 而我们作为一个见习医生, 也需要对内核文件略知一二才行:
这个小结,看完后需要掌握知识点:
- vmlinux.lds是什么, 对应的作用是什么?
- 内核入口文件Head.S 是什么?
- System.map 是什么, 对应的作用?
在Android真机上面使用kptools进行patch:
1 2 | cd / data / data / me.bmax.apatch / patch 或者 run - as me.bmax.apatch
. / kptools - p - i Image - S a12345678 - k kpimg - o Image2 - K kpatch
|
当然在linux环境下也可以使用kptools-linux直接进行patch, 我当时在手机上面折腾的, 绕了个远路, 怎么说呢,有得有失.
00.kptools做了哪些事情呢?
简单来说kptools以Image(未压缩内核镜像)作为输入, 嵌入kpimg 和 kpatch到文件尾部. 同时对内核进行inlinehook和填充对应启动参数. 这里通过一张图说明kptools做了什么
01. 起步阶段(先建立感官认知)
如果我们想知道kptools如何做到修改内核执行顺序, 要解答这个问题, 我们需要对内核有一些最基础的了解, 了解啥呢?
- 先知道Image到底是什么? 从底层观察一下?
- 对Patch前后做一些差异对比?
1.1 了解一个文件到底是什么, 最好的方式就是打开文件看看.
对应工具: rehex
对应文件: KernelImage
从文件本身观察特征有个ARMd, 我们通过一些网上的资料也可以了解到内核文件本身就是一个可执行文件.
1.2 通过IDA打开文件看看
IDA并不能识别对应架构和文件被加载到内存后对应的虚拟内存布局, 我们需要手动填写, 具体填什么内容呢???
1.3 Patch前后文件对比
通过文件前后对比, 我们可以最简单的发现kptools对前4个字节做了对应修改, 这4个字节的含义是什么呢? 是不是问号越来越多了??? 他是我们学习的突破口, 三言2语也很难说清楚, 后面慢慢解答.
02. 深究细节(前4个字节到底是什么?)
内核(操作系统)作为程序员3专研对象好之一, 依然没有逃离计算机编译原理, 会经历预处理->编译->链接三阶段. 其中链接阶段可以通过.lds进行手动编排(细心的朋友会发现kpimg也通过kpimg.lds进行了编排)
2.1 内核文件通过arch/arm64/kernel/vmlinux.lds进行布局编排
上面的文件我们可以得知两个信息:
- 内核文件在虚拟内存空间,起始位置0xffff000008080000
- .head.text会被编排在起始位置
1 2 3 4 5 6 | Python 3.10 . 12 (main, Nov 20 2023 , 15 : 14 : 05 ) [GCC 11.4 . 0 ] on linux
Type "help" , "copyright" , "credits" or "license" for more information.
>>> (((( 0xffffffffffffffff - ( 1 << ( 48 )) + 1 ) + ( 0 )) + ( 0x08000000 ))) + 0x00080000
18446462598867582976
>>> hex ((((( 0xffffffffffffffff - ( 1 << ( 48 )) + 1 ) + ( 0 )) + ( 0x08000000 ))) + 0x00080000 )
'0xffff000008080000'
|
2.2 顺藤摸瓜找到head.S
前面我们知道.head.text是__HEAD对应的宏定义, 我们代码搜索就能找到head.S文件. 通过代码+注释我们可以获得大概的信息, 这部分是Image header, 被bootloader使用. 同时代码通过CONFIG_EFI进行控制,UEFI一般常用于PC方法启动,手机并不是配置为UEFI。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | __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
.ascii "ARM\x64" / / Magic number
. long pe_header - _head / / Offset to the PE header.
pe_header:
__EFI_PE_HEADER
. long 0 / / reserved
|
_kernel_offset_le,_kernel_size_le, _kernel_flags_le分别使用小端的方式存储内核启动偏移,内核文件大小. 这里重点讲解_kernel_flags_le
1 2 3 4 5 6 7 8 9 10 11 12 13 | (__HEAD_FLAG_PAGE_SIZE << 1 ) | \
(__HEAD_FLAG_PHYS_BASE << 3 ))
/ *
* These will output as part of the Image header, which should be little - endian
* regardless of the endianness of the kernel. While constant values could be
* endian swapped in head.S, all are done here for consistency.
* /
DEFINE_IMAGE_LE64(_kernel_size_le, _end - _text); \
DEFINE_IMAGE_LE64(_kernel_offset_le, TEXT_OFFSET); \
DEFINE_IMAGE_LE64(_kernel_flags_le, __HEAD_FLAGS);
|
通过宏定义我们可以知晓__HEAD_FLAGS包含了内核本身使用大端还是小端进行存储, 以及PAGE_SIZE, PHYS_BASE. 这里kptools对flags进行了读取分析。这样你就可以映射上阅读对应的代码了。
2.3 终极解答前4个字节到底是什么?
对的就是一个b跳转
让疑惑来得更猛烈些吧?
kptools这个神医把它桥接到什么地方去了呢? 预知后事如何, 关注我, 收听下会分解.