Linux中隐藏进程的方法
用户态隐藏
进程名伪装 (prctl结合argv[0])
在网上看到一个 prctl 函数可以修改当前的进程名,使用方法则在当前逻辑添加代码即可
1 |
|
但是好像不起作用
首先我们需要知道的是,ps
和top
的底层逻辑是读取并遍历/proc
目录下的文件来展示信息
我们可以通过进程号来看看prctl
的一些相关信息
/proc/3080/cmdline
1 | ./prctl |
/proc/3080/status
/proc/3080/comm
因此ps
命令应该是读取/proc/3080/cmdline
的结果,以上方法对制定明确进程号(ps -a
ps -p 进程ID
)和top
有效
但我们可以通过修改argv[0]
(第一个元素就是程序的进程名)来改变/proc/[pid]/cmdline
的内容,并结合prctl
这里要注意防止内存溢出
若新名称 ≤ argv[0]
原长度,则我们可以直接覆盖 argv[0]
内容,并将剩余部分填 \0
。因为此时已分配的内存空间是够的
1 |
|
若新名称 > argv[0]
原长度,直接扩展 argv[0]
会覆盖后续的 argv[1...argc-1]
(所有非程序名的参数)或环境变量,导致崩溃或数据损坏
正确做法是申请新内存将argv[1...argc-1]
复制到新区域,并重新设置 argv[0]
https://github.com/smaugx/setproctitle
1 |
|
这种隐藏方式其实还是会有纰漏,但是如果遇到不仔细的管理员也有可乘之机,但是我们可以伪造成已有相似的进程名来混淆
文件系统隔离
我们之前提及到ps
实际上是遍历/proc
目录下的文件,但如果将进程目录文件挂载到其它空目录就可以“隐身”了
1 | mkdir hide_proc |
我们可以cat /proc/mounts
来检测是否有文件系统被挂载到了进程目录下
1 | - 设备路径 :挂载的设备,例如 /dev/sda1 |
通过umount /proc/183335
卸载挂载点
动态库劫持(LD_PRELOAD)
对于查看系统进程命令ps
top
,本质上是通过readdir
去读/proc
,那如果我们能够劫持readdir
等函数调用改变其逻辑那就可以忽略掉我们想要隐藏的进程,实现方式就是跟换掉动态链接库,调用我们自己的逻辑方式,从而到达替换效果 https://github.com/gianlucaborello/libprocesshider
而在Linux中的一个环境变量LD_PRELOAD
允许你定义在程序运行前优先加载的动态链接库(绕过Disable Functions也是这个原理之一)
https://pyer.dev/post/some-record-for-module-and-so-in-linux-738817ac
LD_PRELOAD
系统还存在另外一个环境变量叫做
LD_PRELOAD
,这个文件中我们可以指定预先装载的一些共享库或者目标文件。在LD_PRELOAD
里面指定的文件会在动态链接器按照固定规则搜索共享库之前装载,它比LD_LIBRARY_PATH
里面所指定的目录中的共享库还要优先。无论程序是否依赖于它们,LD_LIBRARY_PATH
里面指定的共享库或目标文件都会被装载。由于全局符号介入这个机制的存在,
LD_PRELOAD
里面指定的共享库或目标文件中的全局符号就会覆盖后面加载的同名全局符号,这使得我们可以很方便地做到改写标准 C 库中的某个或某几个函数而不影响其他函数,对于程序的调试或测试非常有用。(
/etc/ld.so.preload
)等效于这个环境变量。
选择需要隐藏的进程后,开始编译
1 | make |
但是修改/etc/ld.so.preload
的前提是root用户或者sudo权限
若想要找到这种恶意文件的话就需要查找异常的网络连接,或者直接检测/etc/ld.so.preload
,要么就老老实实去/proc
中一个个找
内核态隐藏
Rootkit技术(LKM模块)
eBPF隐藏
插个眼,以后找个时间单独学习,涉及到Linux内核的确实一时半会不好搞
参考文章: