首页
社区
课程
招聘
[原创]仅使用NDK构建内核模块的实验
发表于: 2天前 1059

[原创]仅使用NDK构建内核模块的实验

2天前
1059

原理分析见原创文章《把.o变为.ko》系列,以下是速通版:

知己知彼,方能百战不殆 —— 当我们使用insmod安装内核模块时,linux内核到底校验了什么?

这个问题只能从内核源码得到详细答案,由于分析过长,这里只给出摘要。

内核到底期望什么二进制?可执行、动态库 or 对象文件?

这里让我们来看.ko加载时期待的section:

毫无疑问,最接近这种格式要求的ELF文件格式就是.o对象文件,但是二者存在差异:

为了解决这个问题,我们引入一阶段修补工程 —— KPatcher,它负责(KPatcher.cpp:46-136):

注:namespace — Linux 把内核导出符号分了"房间",普通驱动不能随便访问内部符号。-N VFS_internal_... 就是告诉内核"我获得了这个房间的访问授权"。

这是fixup需要填充的第一个地方,fixup根据真机上已经存在的.ko文件,提取vermagic,回填到KPatcher留下的位置(vermagic占位符)。

注:其实android内核不是很在意内核版本,比如5.10.198-android12-9-g12345678 SMP preempt mod_unload aarch64,android内核实际关注的是SMP preempt mod_unload aarch64,内核版本5.10.198-android12-9-g12345678则无所谓。猜测这与android的内核映像和设备高度绑定不能更新,但是系统可以更新有关。

即使内核版本相同,编译选项不同也会导致 struct module 内部的字段偏移不同。内核用 __versions 里的 CRC 校验这个。fixup将从真机上的.ko提取其CRC值并填入__versions。

为了解决2、3,我们引入了fixup负责通过真机上的.ko文件修补KPatcher生成的模块.ko:

Android 内核开启了 CFI(Control Flow Integrity,控制流完整性),在真的执行你的代码之前,验证你的函数指针没有被篡改。它的逻辑很简单:

但是5.x内核和6.x内核的具体检验策略是不同的,想象机场安检:

技术上:

关键区别:

内核调用 init_module() 时会被拦下,然后调用模块注册的 __cfi_check 问"这个函数安全吗?"。这里我们手工构造函数:

翻译成人话:"不管来什么 hash,只要目标地址是我认识的,就通过。"

跳转表结构:

但跳转表结构不只是给 5.x 用的。那 4 字节 hash 同时服务了 6.x。

6.x 不用跳转表了,它直接读函数地址前 4 字节。问题是:这 4 字节必须是 clang 亲自生成的,不能自己编。于是有了偷 hash 的流水线。

生成汇编文件:

linker_dual.lds 把前两步产物按精确顺序排进 .text section。内存布局最终变成:

6.x 内核的 kCFI 检查读 cleanup_module - 4 = 跳转表 hash(0xA540670C),匹配,通过。

注:细看会发现 kcfi_prefix.cleanup 里的 hash 和跳转表里的 hash 完全一样。但内核只读跳转表 hash。原因是 kcfi_prefix.cleanup 紧贴在 cleanup_module.cfi 这个符号前面——而内核从不间接调用 cleanup_module.cfi,它只被 cleanup_module: 以 b 指令直接跳转。直接跳转不触发 kCFI。所以这个 hash 实际上从未被读取——它是第一版开发时留下的冗余保险,属于不影响功能的历史残留。

前面解决的只是 loader.ko 自身通过 insmod 时的 CFI 检查。但 loader 加载的 KPM(内核补丁模块)是用普通 clang 编译的,没有任何 CFI 保护。loader 通过函数指针调用 KPM 代码时,还是会撞 CFI。

于是 loader 在内核内部做了一次"安检豁免权申请"——hook 内核的 CFI 基础设施:

同时把 CFI 失败的处理从 crash 降级为 warn:

这样一来 loader.ko 自身的 insmod 走编译时伪造的跳转表 / kCFI hash,而它加载的 KPM 走运行时的 check 豁免,两套机制各司其职。

我也不知道这个技术有什么用,也许可以用于RootKit,目前只在5.10.198和6.13.4内核进行过测试。
下面介绍实践的成品:

项目地址:KPatcher

他负责将NDK编译生成的.o文件修补为待填充的.ko文件。

项目地址:NDK_Kernel_Module

相当于工程模板,很多构建配置已经设置了好了,也提供了有用的头文件,loader文件夹内是一个使用这套工程模板的实例。
在设备上使用时需要配合fixup_ko,用于获取设备上正常内核模块来完成对于待填充的.ko文件的最后修补(注:如果设备不支持kprobe,每次重启需要未修补的Loader.ko使用fixup_ko重新修补)。
loader本身是使用它构建的kpm加载器(但是兼容不全),依靠448号syscall与用户空间通信,且带有根据文件中配置的uid来关闭指定app的seccomp的功能(不然没办法用448号syscall)。

项目地址:NDK_SIMPLE_KPM

loader支持的kpm构建模板,已装好构建配置和头文件。
目前src目录下是我修改的pte无痕hook实现作为使用示例,可以改成别的东西。


[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!

最后于 2天前 被孤木落编辑 ,原因:
收藏
免费 28
打赏
分享
最新回复 (10)
雪    币: 82
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
都是熬夜大神
2天前
0
雪    币: 4765
活跃值: (5710)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
6
2天前
0
雪    币: 566
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
66666
2天前
0
雪    币: 104
活跃值: (8829)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
tql
1天前
0
雪    币: 160
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
666
1天前
0
雪    币: 109
活跃值: (345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
111
1天前
0
雪    币: 95
活跃值: (3167)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
666666666666
1天前
0
雪    币: 206
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
66666666666666
1天前
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
666
1天前
0
雪    币: 6307
活跃值: (11322)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
1天前
0
游客
登录 | 注册 方可回帖
返回