如何在无源代码和调试符号的情况下向Linux内核注入代码
想象这样一个场景:你有一个现成的Linux内核二进制文件,用于移动设备或嵌入式ARM64开发板。没有源代码可用,调试信息在构建时被剥离了,而你需要在内核层面拦截系统调用或添加自己的逻辑。
这种情况下,通常的做法既冗长又繁琐。你需要在IDA Pro或Ghidra中对二进制文件进行逆向工程,手动查找函数偏移量,然后编写脆弱的补丁。一位昵称为bmax121的开发者决定简化这个过程,并发布了KernelPatch项目。
为什么需要KernelPatch
KernelPatch是一个框架,用于在只有剥离的内核映像(如vmlinux或boot.img)的情况下修补和hook Linux内核。
当内核在构建时没有包含调试信息时,传统的调试工具(如kprobes)需要额外的变通方法才能工作。KernelPatch自己解决了这个问题。它直接从内核二进制文件中恢复符号表,并提供了一个方便的API来修改可执行代码。
该项目对逆向工程师、Android安全研究人员和嵌入式系统的底层软件开发人员都很有用。
该框架能做什么
开发者在该项目中构建了几个重要的内核操作能力:
- 无需源代码的符号搜索。该工具解析现成映像中的内部
kallsyms结构。它不需要外部的System.map文件或源代码。类似的方法也被用于vmlinux-to-elf工具中,作者借鉴了其中的一些思路。 - 两种代码注入模式。你可以对映像进行静态修补(例如,覆盖磁盘上的
boot.img),也可以在运行时动态加载代码。 - 通过内联hook进行函数拦截。对于ARM64架构,调用拦截基于
android-inline-hook的工作。该框架正确处理相对寻址指令(b、bl、adrp),以免破坏原始代码的执行。 - 系统调用hook。替换
sys_call_table表中的地址只需几条命令。 - 内置SU模块。对于Android设备,该项目包含了自己的超级用户实现。
内部工作原理
为了动态加载用户模块(KPM — Kernel Patch Modules),该框架需要内核内部的内存。为此,KernelPatch包含了一个紧凑的TLSF分配器。它帮助在内核地址空间中直接分配可执行内存页(ROX)。
执行内联hook时,框架读取目标函数的前几条指令,从中形成一个所谓的蹦床(trampoline),然后写入跳转到你的处理程序。当你的代码执行完毕后,控制权通过蹦床返回到原始函数。
+-------------------+ +---------------------+ +-------------------+
| Целевая функция | ---> | Ваш обработчик KPM | ---> | Трамплин (Orig) |
| (инструкция JMP) | | (пользовательский) | | (продолжение) |
+-------------------+ +---------------------+ +-------------------+
系统要求与限制
该项目有明确的适用边界,事先了解很重要:
- 仅支持ARM64架构。尚无x86_64或RISC-V版本。
- 内核版本范围——从Linux 3.18到6.6。
- 主要要求是内核必须启用
CONFIG_KALLSYMS=y标志。
如果固件供应商在构建阶段完全移除了CONFIG_KALLSYMS,KernelPatch将无法自动解析函数地址。幸运的是,大多数Android和ARM设备固件都保留了这个标志。
实际使用示例
假设你需要在一台测试用Android设备上追踪所有通过sys_execve调用启动的进程,但不想从供应商源代码重新构建内核。
以前,你不得不构建一个完整的内核可加载模块(LKM),处理数字签名验证问题,以及内核中缺少模块加载器的问题。
使用KernelPatch,流程就不同了:
- 你编写一小段带有拦截逻辑的C代码。
- 将其编译为KPM格式。
- 将你的
boot.img和编译好的KPM模块提供给KernelPatch工具。 - 获得修改后的
boot.img并通过fastboot刷入。
设备启动后,每次调用execve都会先经过你的处理程序。
该项目的README相当简略,所有详细文档都需要从源代码和doc/文件夹中研究。尽管如此,该项目正在积极开发中,已在GitHub上获得了超过14,000颗星。
顺便说一句,如果你不需要该框架本身进行实验,而是需要基于它构建现成的Android root方案,作者建议查看他的另一个项目——APatch。这是Magisk和KernelSU的高级替代方案,底层使用KernelPatch。
对于安全研究人员和ARM64开发者来说,KernelPatch是一个极好的发现,在分析闭源内核时可以节省数十小时的时间。
相关项目