写点什么

如何轻松编写 Rootkit:Linux 内核系统调用拦截技术解析

作者:qife122
  • 2025-08-14
    福建
  • 本文字数:1449 字

    阅读完需:约 5 分钟

如何轻松编写 Rootkit - Trail of Bits 技术博客

我们开源了名为 KRF 的故障注入工具,它利用内核态系统调用拦截技术。您现在就可以使用它来发现程序中的错误假设(及由此产生的漏洞)。快来试试吧!


本文介绍如何通过普通内核模块在 Linux 内核中拦截系统调用。我们将快速回顾系统调用及其拦截意义,然后演示拦截 read(2)系统调用的基础模块实现。

与其他故障注入策略有何不同?

其他故障注入工具主要依赖以下技术:


  1. LD_PRELOAD 技巧:拦截 libc 暴露的系统调用包装函数。存在明显缺陷:

  2. 仅适用于动态链接场景(Go 等新语言和静态编译趋势使其适用性降低)

  3. 包装函数与实际系统调用可能存在显著差异(如 open()可能调用 openat(2))

  4. 动态插桩框架(如 DynamoRIO/Intel PIN):

  5. 允许在函数或机器码级别检测系统调用

  6. 带来显著的运行时开销


内核态故障注入规避了上述问题:直接重写实际系统调用,且几乎不增加运行时开销。

系统调用基础

系统调用是内核向用户空间暴露资源的接口,涉及:


  • 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)
复制代码

高级应用场景

  1. 强制故障注入


asmlinkage long phony_read(int fd, char __user *buf, size_t count) {  return -ENOSYS;}
复制代码


  1. 用户定向故障


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);}
复制代码


  1. 数据篡改


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 工具具备:


  • 按可执行文件精确拦截能力

  • 支持完整系统调用"配置文件"操作

  • 实时故障注入能力

  • 完全兼容静态链接程序

替代方案对比

  1. syscall_intercept:基于 LD_PRELOAD 但使用 capstone 反汇编 libc

  2. ptrace(2):用户空间子进程监控方案,存在调试器冲突和性能问题

  3. eBPF/kprobes:仅支持参数记录,无法实现实际拦截


技术说明:本文介绍的 CR0 写解锁机制源自 PaX/grsecurity 的"rare write"实现更多精彩内容 请关注我的个人公众号 公众号(办公 AI 智能小助手)公众号二维码

办公AI智能小助手
用户头像

qife122

关注

还未添加个人签名 2021-05-19 加入

还未添加个人简介

评论

发布
暂无评论
如何轻松编写Rootkit:Linux内核系统调用拦截技术解析_Linux内核_qife122_InfoQ写作社区