如何轻松编写rootkit - Trail of Bits技术博客
我们开源了名为KRF的故障注入工具,它利用内核态系统调用拦截技术。您现在就可以用它来发现程序中的错误假设(以及由此产生的bug)。
内核模块拦截系统调用
本文将介绍通过普通内核模块在Linux内核中拦截系统调用的方法。我们将快速回顾系统调用及其拦截价值,然后演示一个拦截read(2)系统调用的基础模块。
与其他故障注入技术的区别
常见故障注入技术主要有:
-
LD_PRELOAD技巧:拦截libc暴露的系统调用包装器
- 缺点:仅适用于动态链接,不适用于Go等静态编译语言
- 系统调用包装器与实际系统调用可能存在差异
-
动态插桩框架:如DynamoRIO或Intel PIN
内核空间故障注入避免了这些问题:直接重写实际系统调用,且几乎无运行时开销。
系统调用基础
系统调用是内核向用户空间暴露资源的函数,包括:
- I/O操作:open(2), close(2), read(2), write(2)
- 进程管理:fork(2), kill(2), exit(2)
- 网络通信:send(2), recv(2)
系统调用比用户空间函数调用代价高得多,涉及CPU中断处理和特权上下文切换。
实现步骤
获取系统调用表
Linux内核自2.5版后不再直接暴露sys_call_table符号。我们使用kallsyms_lookup_name接口获取地址:
1
2
3
4
5
6
7
8
9
10
|
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系统调用拦截:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
static typeof(sys_read) *orig_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);
}
int init_module(void) {
// ...获取sys_call_table...
orig_read = (typeof(sys_read) *)sys_call_table[__NR_read];
// 需要解除CR0写保护
CR0_WRITE_UNLOCK({
sys_call_table[__NR_read] = (void *)phony_read;
});
return 0;
}
void cleanup_module(void) {
CR0_WRITE_UNLOCK({
sys_call_table[__NR_read] = (void *)orig_read;
});
}
|
处理x86写保护
x86架构下需要通过修改CR0寄存器解除写保护:
1
2
3
4
5
6
7
8
9
10
11
|
#define CR0_WRITE_UNLOCK(x) \
do { \
unsigned long __cr0; \
preempt_disable(); \
__cr0 = read_cr0() & (~X86_CR0_WP); \
write_cr0(__cr0); \
x; \
__cr0 = read_cr0() | X86_CR0_WP; \
write_cr0(__cr0); \
preempt_enable(); \
} while (0)
|
进阶应用
我们可以扩展phony_read实现更多功能:
- 直接返回错误:
- 针对特定用户:
1
2
3
|
if (current_uid().val == 1005) {
return -ENOSYS;
}
|
- 返回伪造数据:
1
2
3
4
|
unsigned char kbuf[1024];
memset(kbuf, 'A', sizeof(kbuf));
copy_to_user(buf, kbuf, sizeof(kbuf));
return sizeof(kbuf);
|
总结
本文介绍了内核空间系统调用拦截的基础知识。我们的KRF工具实现了更高级的功能:
- 按可执行文件精确拦截
- 支持整个系统调用"配置文件"
- 实时故障注入能力
其他相关技术包括:
- syscall_intercept:通过LD_PRELOAD实现
- ptrace(2):用户空间拦截子进程系统调用
查看完整KRF工具源码