第85章 恶意软件分析(五):Rootkit分析、内核模块隐藏技术

Rootkit,这词儿听着就让人头疼。说实话,我入行那会儿第一次碰到Rootkit,差点把系统重装了。后来才发现,它不过是一套精心设计的「隐身术」——把自己藏进内核里,让你看不见、摸不着。

这一章,我们就来扒一扒Rootkit的老底。重点看内核模块是怎么隐藏的。嗯,准备好了吗?

85.1 Rootkit到底是什么?

简单说,Rootkit就是一组恶意工具。它拿到系统最高权限(root)后,就开始隐藏自己。隐藏进程、隐藏文件、隐藏网络连接……你看到的系统,都是它想让你看到的。

我个人习惯把Rootkit分成两类:

  • 用户态Rootkit:hook系统调用,比如劫持open()read()。相对好查。
  • 内核态Rootkit:直接修改内核数据结构,或者加载恶意内核模块。这才是硬骨头。

我在项目中遇到过最狠的一个,它把自己编译进了内核镜像里。你重装系统都没用,除非换内核版本。你说恶不恶心?

85.2 内核模块隐藏的核心思路

Linux内核里,所有已加载的模块都维护在一个链表中。这个链表叫modules,你执行lsmod时,内核就是遍历这个链表给你看结果。

那恶意模块怎么藏?说白了就两招:

  1. 从链表里摘除自己——让lsmod看不到它。
  2. 伪造链表节点——把自己伪装成别的模块。

你想想看,如果模块不在链表里,内核遍历时就找不到它。但模块的代码还在内存里跑着,功能一点不受影响。这就是典型的「隐身术」。

核心要点:隐藏的本质是「欺骗遍历机制」。只要内核遍历不到你,你就「不存在」。

85.3 实战:从链表摘除模块

这是最基础的隐藏手法。我直接上代码,你一看就明白。

// 恶意模块隐藏自身
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>

static int __init evil_init(void)
{
    struct module *mod = THIS_MODULE;
    
    // 关键操作:从内核模块链表中删除自己
    list_del_init(&mod->list);
    
    printk(KERN_INFO "Rootkit: 我已从链表消失\n");
    return 0;
}

static void __exit evil_exit(void)
{
    // 注意:这里不需要做清理
    // 因为模块已经不在链表里了
    printk(KERN_INFO "Rootkit: 再见\n");
}

module_init(evil_init);
module_exit(evil_exit);
MODULE_LICENSE("GPL");

这段代码的核心就一行:list_del_init(&mod->list)。执行完这行,你再跑lsmod,就看不到这个模块了。

警告:模块从链表摘除后,rmmod也无法卸载它。因为内核找不到这个模块了。我曾经在测试机上忘了这茬,重启才解决。所以,调试时一定要留后门。

85.4 进阶:隐藏内核模块的更多手法

光摘链表还不够。高级的Rootkit会用更隐蔽的方式。我整理了几种常见手法:

隐藏手法 原理 检测难度
链表摘除 从modules链表删除自身节点
sysfs隐藏 删除/sys/module/下的对应目录
kobject隐藏 从kobject层次结构中移除
内存覆盖 用合法模块信息覆盖自身内存 极高

我个人觉得,内存覆盖是最难查的。它直接把恶意模块的内存区域改成另一个合法模块的样子。你拿cat /proc/modules看,全是正经模块。但实际跑着的代码是恶意的。

85.5 检测与对抗:怎么揪出隐藏模块?

好,现在你是防守方了。怎么找到这些藏起来的家伙?

我常用的方法有这几个:

  • 内存扫描:直接扫描内核内存区域,找可疑的module结构体。虽然模块从链表摘了,但结构体还在内存里。
  • 系统调用表检查:很多Rootkit会hook系统调用。检查sys_call_table的完整性,看有没有被篡改。
  • 内核代码段校验:计算内核代码段的哈希值,跟原始值对比。有差异就说明被动了手脚。

小技巧:我曾经写过一个内核模块,专门遍历整个内核内存空间,找所有符合module结构体特征的内存块。效果还不错,能查出90%以上的隐藏模块。但缺点是会误报,需要人工确认。

85.6 知识体系总览

下面这张图,把Rootkit隐藏与检测的核心逻辑串起来了。你看一遍,心里就有谱了。

Rootkit隐藏与检测知识体系 攻击方:隐藏手法 链表摘除 sysfs隐藏 kobject隐藏 内存覆盖 防守方:检测方法 内存扫描 系统调用表检查 代码段哈希校验 内核结构体遍历 核心对抗逻辑:遍历机制 vs 欺骗遍历 攻击方想方设法让内核遍历不到自己 防守方则用多种独立路径交叉验证

85.7 避坑指南

最后,分享几个我踩过的坑:

  • 别在生产环境直接测试——我曾经在客户服务器上试隐藏模块,结果rmmod卸不掉,只能重启。客户差点投诉我。
  • 注意内核版本差异——不同内核版本,module结构体的字段偏移可能不同。我吃过这个亏,代码在5.4上跑得好好的,换到5.10就崩了。
  • 隐藏不等于安全——模块藏起来了,但它的代码还在内存里。高手可以通过内存dump找到你。所以,隐藏只是第一步,代码本身也要做混淆和反调试。

嗯,这一章的内容就到这儿。Rootkit分析是个大话题,我们后面还会继续深入。记住一句话:能藏起来的,就一定能被找到。只是时间问题。


公众号:蓝海资料掘金营,微信deep3321