如何轻松编写Rootkit - Trail of Bits技术博客
我们开源了名为KRF的故障注入工具,它利用内核态系统调用拦截技术。您现在就可以使用它来发现程序中的错误假设(及由此产生的漏洞)。快来试试吧!
本文介绍如何通过普通内核模块在Linux内核中拦截系统调用。我们将快速回顾系统调用及其拦截意义,然后演示拦截read(2)系统调用的基础模块实现。
与其他故障注入策略有何不同?
其他故障注入工具主要依赖以下技术:
LD_PRELOAD技巧:拦截libc暴露的系统调用包装函数。存在明显缺陷:
- 仅适用于动态链接场景(Go等新语言和静态编译趋势使其适用性降低)包装函数与实际系统调用可能存在显著差异(如open()可能调用openat(2))
动态插桩框架(如DynamoRIO/Intel PIN):
- 允许在函数或机器码级别检测系统调用带来显著的运行时开销
内核态故障注入规避了上述问题:直接重写实际系统调用,且几乎不增加运行时开销。
系统调用基础
系统调用是内核向用户空间暴露资源的接口,涉及:
- I/O操作(open/close/read/write)进程管理(fork/kill/wait)网络通信(send/recv)特殊操作(ioctl)
与传统函数调用不同,系统调用需要昂贵的上下文切换(x86通过int 80h/sysenter指令触发)。
系统调用表定位技术
Linux内核通过sys_call_table数组管理系统调用,但自2.6版本后不再直接导出该符号。我们使用kallsyms_lookup_name接口可靠获取其地址:
static unsigned long *sys_call_table;int init_module(void) { sys_call_table = (void *)kallsyms_lookup_name("sys_call_table"); if (!sys_call_table) { printk(KERN_ERR "查找sys_call_table失败\n"); return -1; } return 0;}系统调用注入实现
基础拦截示例(以read为例):
asmlinkage long phony_read(int fd, char __user *buf, size_t count) { printk(KERN_INFO "拦截到read调用:fd=%d, %lu字节\n", fd, count); return orig_read(fd, buf, count);}x86架构需处理CR0寄存器的写保护位:
#define CR0_WRITE_UNLOCK(x) \ do { \ unsigned long __cr0; \ preempt_disable(); \ __cr0 = read_cr0() & (~X86_CR0_WP); \ BUG_ON(unlikely(__cr0 & X86_CR0_WP)); \ write_cr0(__cr0); \ x; \ __cr0 = read_cr0() | X86_CR0_WP; \ BUG_ON(unlikely(!(__cr0 & X86_CR0_WP))); \ write_cr0(__cr0); \ preempt_enable(); \ } while (0)高级应用场景
- 强制故障注入:
asmlinkage long phony_read(int fd, char __user *buf, size_t count) { return -ENOSYS;}- 用户定向故障:
asmlinkage long phony_read(int fd, char __user *buf, size_t count) { if (current_uid().val == 1005) return -ENOSYS; return orig_read(fd, buf, count);}- 数据篡改:
asmlinkage long phony_read(int fd, char __user *buf, size_t count) { unsigned char kbuf[1024]; memset(kbuf, 'A', sizeof(kbuf)); copy_to_user(buf, kbuf, sizeof(kbuf)); return sizeof(kbuf);}KRF工具特性
我们开发的KRF工具具备:
- 按可执行文件精确拦截能力支持完整系统调用"配置文件"操作实时故障注入能力完全兼容静态链接程序
替代方案对比
- syscall_intercept:基于LD_PRELOAD但使用capstone反汇编libcptrace(2):用户空间子进程监控方案,存在调试器冲突和性能问题eBPF/kprobes:仅支持参数记录,无法实现实际拦截
技术说明:本文介绍的CR0写解锁机制源自PaX/grsecurity的"rare write"实现
