Linux Rootkit是一类针对Linux操作系统设计的恶意工具集,它通过多种方式隐藏自身和攻击活动,使得攻击者能够绕过安全防御机制,在受害主机上非法维持控制权限,因此常被喻为黑客的“隐身斗篷”。本文将分析Linux系统中常见的Rootkit手法,特别是内核态Rootkit,并简要讨论对应的检测思路。
Linux Rootkit根据运行层次分为用户态Rootkit和内核态Rootkit。用户态Rootkit通过篡改系统命令、动态链接库劫持等相对简单的手法实现隐藏,易被检出;内核态Rootkit可深入操作系统内核,通过LKM Rootkit、eBPF Rootkit等技术实现,具备更强的隐蔽性和控制能力。下图归纳了本文主要介绍的几种Linux Rootkit手法。

替换系统命令如ps、top、ls、lsof、ss、netstat等,达到隐藏进程、文件、网络连接的目的。下图为被替换的ps命令。![]()
修改或添加环境变量shell脚本如/etc/profile、/etc/profile.d/[恶意脚本:gateway.sh]、/etc/bashrc等,劫持环境变量,覆盖常见的系统命令如ps、netstat、dir、ls、find等,使得不展示恶意进程、文件、网络连接,并在用户登录时加载生效。下图为实际入侵事件中的恶意环境变量脚本。
运行如下命令,将进程虚拟目录挂载到隐藏目录或空目录,达到隐蔽效果。下图为挂载至空目录示例。
mount [绑定选项:-o bind、--bind] [挂载目录:/tmp/.hidden、/tmp/empty] /proc/[pid]![]()
首先编译生成恶意.so库,劫持系统调用或其它库函数。以libprocesshider.so(链接:https://github.com/gianlucaborello/libprocesshider)为例,下图展示了其劫持libc库函数readdir()并过滤process_to_filter变量定义的恶意进程的逻辑。
然后再通过以下7种常见手法加载恶意.so库:
动态链接器文件完整性校验;
检测全局和系统服务的$LD_PRELOAD、$LD_LIBRARY_PATH或$PATH环境变量是否配置可疑.so库或可疑.so查找路径;
检测/etc/ld.so.preload、/etc/ld.so.conf或/etc/ld.so.conf.d/*.conf文件是否配置可疑.so库或可疑.so查找路径;
yara规则或病毒引擎扫描运行进程加载的.so库、环境变量和ld相关配置文件配置加载的.so库、以及系统默认.so库存放路径(如/usr/lib64/);
利用busybox静态链接编译工具,辅助排查是否有隐藏进程、文件、网络连接的情况。
io_uring是Linux内核自5.1版引入的一种绕过传统系统调用的异步I/O机制,它包含ubmission Queue (SQ)和Completion Queue (CQ)两个用户和内核空间共享的环形缓冲区,应用提交I/O请求至SQ并通知内核,内核异步处理完请求后把结果放进CQ,等待应用主动去取或轮询或等待通知,目前已支持处理60+种操作请求。由于无需依赖系统调用,所以监控系统调用的安全工具无法检测仅依赖io_uring的Rootkit,需要注意的是io_uring的隐藏效果不包括恶意进程/文件/网络连接本身。以curing为例(链接:https://github.com/armosec/curing),它利用io_uring实现client端与server端通信并执行C2指令,以绕过某些安全检测工具达到一定的隐藏效果。下图分析了curing client与server端建立通信的逻辑。
下图分析了curing client端隐蔽执行写文件C2指令的逻辑。
利用KRSI(Kernel Runtime Security Instrumentation,内核运行时安全检测),它允许eBPF程序附加到LSM(Linux Security Module,Linux安全模块)钩子上,提供比系统调用监控钩子更为与安全相关的内核级事件;
寻找非系统调用的替代插桩点,再利用Kprobes或eBPF技术监控,如找到一个不同内核版本在进行文件写入时每次都能触发的HOOK点。
通过修改系统调用表中的系统调用函数地址来劫持系统调用。以diamorphine.ko(链接:https://github.com/m0nad/Diamorphine)为例,下图展示了其修改系统调用表中getdents()、getdents64()、kill()三个系统调用函数地址的逻辑。
下图分析了其HOOK kill系统调用函数实现隐藏进程的关键代码逻辑。
若恶意内核模块隐藏是通过删除内核模块链表结点实现,使得lsmod命令不可枚举,那么可以通过/sys/module/目录对比查找出隐藏的内核模块;
查看dmesg内核环缓冲区日志,过滤module关键字提取出加载模块,可通过内核模块名特征匹配或 /sys/module/目录比对隐藏模块,发现恶意内核模块;使用rootkit扫描工具(如chkrootkit、rkhunter);
通过编写自定义内核模块,打印系统调用表中所有系统调用函数实际地址和名称,其中地址与其它函数差异大或地址与通过/boot/system.map-$(uname -r)符号地址计算出的不一致(即“可疑函数实际地址!= (正常函数实际地址-正常函数符号地址)+可疑函数符号地址”)的系统调用函数,可能已经被劫持;
通过检查/proc/kallsyms内容,如根据kallsyms获取sys_call_table实际地址,编写内核模块从内存读取系统调用表中可疑系统调用号对应的指针,反查kallsyms获取该指针地址对应的函数名,若不是正常的系统调用函数名,则可能已经被劫持。
VFS(Virtual File System,虚拟文件系统)是类Unix操作系统内核的关键组成部分,它作为操作系统内核中的一个抽象层,为上层应用和内核自身提供统一的文件操作接口(如open、read、write等),并屏蔽了各种底层实际文件系统(如ext4、nfs等)的差异。可通过修改VFS接口函数指针的方式HOOK指定接口函数;针对内存文件系统和虚拟文件系统(如tmpfs、procfs),也可通过HOOK simple_dir_operations结构中文件遍历接口函数指针,或篡改VFS目录项(包含目录和文件)结构的子目录/文件链表(即将指定目录或文件从对应的链表中脱链)实现隐藏。
低版本内核一般HOOK readdir接口函数和篡改dentry->d_child链表,高版本内核一般HOOK iterate/iterate_shared接口函数和篡改parent->d_subdirs链表。
以adore-ng.ko(链接:https://github.com/yaoyumeng/adore-ng)为例,下图展示了其修改接口函数readdir()、iterate()函数指针的关键代码逻辑。
恶意的adore_root_readdir和adore_root_iterate最终调用adore_root_filldir,下图分析了其HOOK iterate接口函数实现隐藏文件的关键代码逻辑。
通过遍历VFS相关结构(如file_operations),获取这些对象的*operations(如file->f_op)结构体指针,进而获取实际的接口函数指针,判断指针指向地址是否处于合法空间,可与其它接口函数指针、/boot/system.map、未入侵类似系统对应的接口函数指针比对,进行指针校验。
通过遍历可疑目录磁盘inode表或内存中的dentry/inode链表,与用户空间系统命令查询的结果比对,进行完整性检查。
另外,编写自定义内核模块检查/proc/和内存中task_struct链表实际内容是否匹配,可帮助排查内核级隐藏进程。
Ftrace(Function Tracer)是Linux内核中的内置跟踪框架,提供了用于收集和分析内核行为和性能的不同类型的运行时信息的工具和基础设施,旨在帮助开发人员和系统设计人员定位内核内部发生的情况。Ftrace允许注册回调,可在内核导出函数入口自动运行指定的回调,这些回调不但可用于监控、实时打补丁,也可用于HOOK。这种无侵入式的HOOK,隐蔽性高于上面两种传统的HOOK。
以ftrace_hook.ko(链接:https://github.com/ilammy/ftrace-hook)为例,下图展示了其添加回调HOOK clone()和execve()函数的关键代码逻辑。
以其中HOOK sys_execve系统调用为例,下图分析了HOOK跳转至的恶意函数fh_sys_execve的实现逻辑。
下图分析了Ftrace回调函数fh_ftrace_thunk的实现逻辑。需要注意的是,fh_sys_execve实际上调用原生的sys_execve,而调用sys_execve又会触发Ftrace回调跳转到fh_sys_execve,目前处理这种递归调用的方式有两种,一种是依赖新编译器选项在内核函数入口插入的mcount插桩点指令直接跳过后续Ftrace回调,另一种是Ftrace回调检查调用是否来自当前模块,如果不是才调用恶意函数。
还能够通过编写自定义内核模块查找所有注册的Ftrace回调,判断是否来自可信内核模块,或者检查ftrace_set_filter_ip设置的过滤表结构,判断是否只包含合法的追踪目标;
还能够进行基于动态行为的分析,如沙箱检测、基于Kprobes和eBPF技术监控内核函数行为(如监控内核模块加载、文件创建等),再通过行为规则匹配的方式发现系统中可能存在的rootkit。
Kprobe(Kernel Probe,内核探针)是Linux内核提供的一种动态调试和追踪机制,相当于内核级别的“断点”或“调试钩子”,允许开发者在无需修改和重启内核的情况下,动态地在特定内核地址/指令/函数插入自定义代码,旨在用于内核调试、性能分析、安全取证等场景。
相比于Ftrace,它的插桩点更丰富灵活,但性能和稳定性略低,可能导致内核崩溃。因其无侵入式动态插桩、插桩点灵活的特性,已被广泛应用于rootkit。
以reptile.ko(链接:https://github.com/f0rb1dd3n/Reptile)为例,Reptile引入了两个开源项目,分别是KHOOK内核函数HOOK引擎(链接:https://github.com/milabs/khook)和Matryoshka内核模块加载器(链接:https://github.com/milabs/kmatryoshka),reptile.ko不直接作为LKM,而是由加载器解密包含在数据段中的内核模块后,然后使用sys_init_module系统调用加载,下图展示了加载LKM的关键代码逻辑,其中用到了Kprobes技术绕过符号解析限制。
Reptile通过注册kprobe/kretprobe并将symbol_name设置为目标内核函数、将pre_handler/ post_handler/fault_handler等设置为恶意回调函数的方式实现HOOK,包括HOOK ip_rcv()实现端口敲门,HOOK fillonedir()、filldir()、filldir64()等实现文件/目录隐藏,HOOK find_task_by_vpid()、vfs_statx()、next_tgid()、load_elf_binary()等实现进程隐藏,HOOK tcp4_seq_show() 、udp4_seq_show()实现网络连接隐藏,HOOK vfs_read()实现文件内容隐藏的功能。下图分析了文件/目录隐藏的逻辑。
针对Kprobes Rootkit,还可以通过/sys/kernel/debug/kprobes/list检查已注册的kprobes/kretprobes列表及其属性信息是否异常;
或者遍历内核中kprobe_table数据表,该数据结构维护已注册的kprobes信息,查看这些信息(kprobes数量、目标函数、回调函数指针、结合回调函数指针和kallsyms获取的回调函数名等)是否异常;
或者借助现有的内核完整性检测工具(LKRG)、内存取证工具(LiME+Volatility)查看是否有异常kprobe注册;
也可利用eBPF技术监控register_kprobe/ register_kretprobe或其它相关内核函数和结构体,观察是否有异常变化。
eBPF(extended Berkeley Packet Filter,扩展伯克利数据包过滤器)是Linux内核中用于高效、安全执行 “沙箱程序(小型、受限的虚拟机字节码程序)”的通用基础设施,用户空间将写好的eBPF程序编译成字节码加载到内核,经内核校验合法的eBPF程序可动态HOOK到下图所示的多种HOOK点(如系统调用、Kprobes、Uprobes、内核tracepoint、网络包处理、性能事件、cgroup hook、LSM hook等)。
相比于Kprobes,eBPF更隐蔽(非LKM、非明显Kprobes表、少有日志记录、加载命令可混淆),更稳定(校验器保护),更灵活(HOOK点多、内核提供多种eBPF辅助函数、加载程序不受handler逻辑限制),已被越来越多地用于实现rootkit。以隐藏指定进程pid的eBPF程序(链接:https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/24-hide)为例,下图展示了嵌入内核部分的隐藏进程的关键代码逻辑。


利用eBPF技术动态HOOK到不同HOOK点,可以实现各种不同的隐藏和攻击效果。
例如,HOOK kill/waitpid/pidfd_open等以pid入参的系统调用实现rootkit进程隐藏或免杀;HOOK bpf系统调用和kprobe/bpf_map_new_fd实现rootkit bpf组件隐藏;HOOK vfs_open/vfs_read/sys_getdents64实现隐藏或篡改文件(如隐藏恶意文件、篡改ssh auth_keys、篡改/etc/passwd、篡改计划任务等、篡改/dev/kmsg日志)达到持久化或规避检测目的,HOOK uprobe/md5_crypt_verify绕过数据库认证达到持久化连接数据库目的;HOOK XDP ingress/ ingress_dispatch/http_action劫持并篡改HTTP请求(在执行恶意C2指令后替换请求为正常心跳包请求)达到隐蔽地执行C2命令,若进一步HOOK XDP egress劫持并篡改HTTP返回为执行信息收集指令的结果则可达到隐蔽地进行数据渗漏的目的;由于Kprobes和tracepoint不受cgroups和命名空间的限制,因此还可用ebpf HOOK它们提供的挂钩点进行容器逃逸,可通过HOOK read系统调用劫持并篡改pipe中内容达到跨容器执行恶意指令的目的,可通过HOOK uprobe/ParseNormalizedNamed将Pause容器镜像替换为恶意容器镜像达到打破资源隔离的目的。
(可参考项目:https://github.com/Gui774ume/ebpfkit、https://github.com/pathtofile/bad-bpf)
利用工具(bpftool、bcc)或/proc、/sys接口或监控bpf系统调用枚举系统已加载的所有eBPF程序,查看是否有可疑的,但对于带隐藏自身功能的eBPF失效;
静态分析所有eBPF字节码,检查目标调用的HOOK点,搜索敏感eBPF helper调用及参数特征;
监控eBPF活动并观察是否可疑,包括审计bpf系统调用,审计eBPF内核辅助函数调用,高版本内核还能够编写eBPF LSM hook进行监控;
利用ebpfkit-monitor工具(链接:https://github.com/Gui774ume/ebpfkit-monitor),该工具可静态分析eBPF字节码或在运行时监控可疑的eBPF活动,但该工具仅针对ebpfkit。
本文通过介绍Linux系统中常见的Rootkit手法及其检测思路,不难发现,随着技术的不断发展,Linux Rootkit的实现方式日益多样、隐蔽性和对抗性持续增强,给安全检测与防御工作带来了新的挑战。应对这一趋势,检测手法也需要不断创新和升级,结合静态分析、动态监测、多层防护等多维度方法,才能有效提升对新型Rootkit的检测与防护能力,保障系统安全。