对比发现隐藏进程
用户态工具输出的进程列表是可以被伪造的:rootkit 可以挂在内核层过滤 ps、top 的结果,或者直接替换这些二进制。内核提供的 /proc 目录则难以整体隐藏,因此把 ps 输出的 PID 集合与 /proc 下的数字目录集合做差集,只在 /proc 里出现、ps 里没有的 PID,就是被过滤掉的隐藏进程。同理可以把 ss 的连接列表与 /proc/net/tcp 的内容做对比。
基本原理
| 对比对象一 | ps -eo pid= 与 ls /proc 的数字目录 |
|---|---|
| 对比对象二 | ss -antp 的连接与 /proc/net/tcp 记录 |
| 对比对象三 | 文件系统列表与 ls -la 的目录内容(用于发现被隐藏的文件) |
| 判据 | 两条命令的集合差集,稳定出现的差异才可信 |
| 噪声来源 | 线程(不占 PID 目录)、短命进程、正在退出的进程 |
| 权限要求 | 读取 /proc 目录本身无需特权,取细节需要 root |
典型使用场景
- 怀疑主机被植入 rootkit,常规 ps 输出与实际进程数不符
- 确认告警里提到的进程为什么在 ps 里找不到
- 清理前核对进程全景,避免遗漏仍在运行的后门
- 与网络连接对比,找出有连接但没有可见进程的异常情况
- 验证应急工具(busybox ps)与系统自带 ps 的差异
两次采集后取差集
两次采集后取差集
ps -eo pid= | tr -d ' ' | sort -n > /tmp/ps_pids.txt # 用户态视角
ls /proc | grep -E '^[0-9]+$' | sort -n > /tmp/proc_pids.txt # 内核视角
comm -13 /tmp/ps_pids.txt /tmp/proc_pids.txt | head -n 30 # 只在 /proc 里出现的 PID
ls -d /proc/[0-9]* | wc -l; ps -eo pid= | wc -l # 数量对照,一眼看出差距
for p in $(comm -13 /tmp/ps_pids.txt /tmp/proc_pids.txt | head -n 5); do
echo "== $p"; ls -l /proc/$p/exe /proc/$p/cwd; tr '\0' ' ' < /proc/$p/cmdline; echo; done # 逐个画像对比维度
| 维度 | 用户态命令 | 内核视角 | 差异含义 |
|---|---|---|---|
| 进程 | ps -eo pid= | ls /proc 数字目录 | ps 缺失即被过滤 |
| 连接 | ss -antp | /proc/net/tcp | ss 缺失可能是工具被替换 |
| 打开文件 | lsof 输出 | /proc/<PID>/fd | lsof 结果异常时以此为准 |
| 监听端口 | ss -lntup | /proc/net/tcp 中的 0A 状态 | 内核表里存在即确有监听 |
| 文件内容 | ls -la 目录 | 目录自身与 stat 结果 | 两者不一致提示文件被隐藏 |
差集出现 PID 后先别急着终止:用 ls -l /proc/<PID>/{exe,cwd,cmdline} 做一次完整画像,把样本、连接与启动时间记录下来,再决定处置顺序,否则会丢掉唯一的现场。
噪声要控制:两次采集之间间隔越久,短命进程造成的差集越大。建议采集间隔控制在几秒内,并对同一 PID 连续两轮都出现才算稳定命中。