在介绍原理之前,先来看一下实战效果,模拟挖矿程序在系统运行,当管理员登入后,停止运行,管理员退出时启动挖矿脚本,说干就干。
建一个名为 miner.sh
的脚本,模拟一个挖矿程序
#!/bin/bash
# 模拟一个长时间运行的挖矿程序
while true; do
echo "Mining..."
sleep 5
done
添加执行权限
chmod +x miner.sh
我们可以直接写入到配置文件中,但是为了演示清晰,还是单独弄一个脚本
stop_miner.sh
#!/bin/bash
# 获取 miner.sh 的 PID 并终止它
PID=$(pgrep -f miner.sh)
if [ -n "$PID" ]; then
echo "Stopping miner.sh (PID: $PID)"
kill $PID
else
echo "miner.sh is not running"
fi
添加执行权限
chmod +x stop_miner.sh
start_miner.sh
#!/bin/bash
# 启动 miner.sh
echo "Starting miner.sh"
./miner.sh &
添加执行权限
chmod +x start_miner.sh
我们修改 root
的配置文件,之后使用普通用户来观察挖矿程序执行情况
EXIT
信号是 Bash 内部的一种机制,用于表示脚本或会话的正常结束
/root/.bashrc
使用普通用户观察挖矿程序的进程情况
此时使用普通用户查看相关挖矿进程
并不存在我们设置的挖矿进程
可以看到,root 退出登录后,挖矿病毒开始运行
可以看到挖矿进程又没了(进程终止了)
挖矿进程再次出现,成功完成了与管理员的躲猫猫。
trap
命令是 Unix 和 Linux 系统中用于处理信号(signals)的一个命令。信号是由操作系统或其他程序发给进程的一种通知,用于请求进程执行某些操作或进行某种处理。trap
命令允许在接收到特定信号时执行自定义的命令或脚本,从而实现灵活的信号处理机制。
trap 'commands' signals
commands
:在接收到指定信号后要执行的命令或脚本。
signals
:要捕获和处理的信号列表。信号可以用名字(如 SIGHUP
、SIGINT
)或对应的信号编号表示
trap
能够处理哪些信号呢?
trap
一共就两个参数,其中 -l
可以显示支持处理的信号
trap -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
这其中比较常见的信号如下:
SIGHUP
(1):挂起信号,通常在终端关闭时发送。
SIGINT
(2):中断信号,通常由 Ctrl+C 组合键发送。
SIGTERM
(15):终止信号,用于请求程序正常终止。
SIGKILL
(9):强制终止信号,无法捕获或忽略。
SIGQUIT
(3):退出信号,通常由 Ctrl+\ 组合键发送。
通过 trap
将命令(command)绑定在特定的一些信号上,当接收到对应信号后,将会执行绑定的命令
查看已经绑定的命令:
trap -p
在 trap
命令的 command
部分,可以使用几乎任何有效的 Shell 命令或一组命令。以下是一些常见形式的内容:
单一命令
trap 'echo "SIGINT received"' SIGINT
多个命令
可以使用分号(;
)或逻辑运算符(如 &&
或 ||
)连接多个命令
trap 'echo "SIGINT received"; cleanup && exit' SIGINT
Shell 函数
可以定义一个函数,然后在 trap
中调用这个函数
cleanup() {
echo "Cleaning up..."
# 清理操作
}
trap cleanup SIGINT
复杂命令块
可以使用花括号 {}
包含一组命令,形成一个命令块
trap '{ echo "SIGINT received"; cleanup; exit; }' SIGINT
执行脚本
你可以调用外部脚本来处理信号。例如:
trap '/path/to/your_script.sh' SIGINT
命令替换
你可以使用命令替换(command substitution)来动态生成要执行的命令
trap "$(echo 'echo SIGINT received; exit')" SIGINT
条件语句
可以在 trap
命令中使用条件语句(如 if
,case
)
trap 'if [ -f /tmp/flag ]; then echo "Flag is set"; else echo "No flag"; fi' SIGINT
忽略语句
可以将命令设置为空字符串,这样信号就会被忽略,什么都不做(影响系统默认对信号的处理,也就是真正意义上的什么都不做)
trap '' SIGINT
恢复默认
trap - SIGINT
通过 trap -
可以将信号处理恢复为默认行为
trap
作用范围是怎样的呢?
系统级?
用户级?
进程树级?
进程级?
在 bash 中进行如下配置,设置 trap
命令捕获 SIGINT 信号(Ctrl+C
)
trap 'echo "SIGINT received"' SIGINT
之后输入 Ctrl+C
观察一下效果
退出当前会话,再次登录测试
可以看到,再次登录相同系统、相同用户后,之前设置的 trap 就不见了,也没有任何效果了
因此 trap 的作用范围不是系统级,也不是用户级,接下来测试在已经设置了 trap 的 bash 进程中再开启一个 bash ,看看父进程的 trap 会不会传递给子进程
实验证明,trap
的作用范围就是在进程级,不会传递给子进程
默认情况下,我们 Ctrl+C
是传递 SIGINT
信号,终止进程,那如果我们设置 Trap
,默认的终止进程的效果还存在吗?
我们使用一个脚本来进行测试
#!/bin/bash
trap 'echo "SIGINT received, but I will not exit!"' SIGINT
echo "Running script. PID: $$"
while true; do
sleep 1
done
脚本捕获 Ctrl+C
信号,并输出特定信息,脚本一直循环,我们看一下我们执行后,是否可以通过 Ctrl+C
进行终止
可以看到,脚本执行后,无法通过 Ctrl+C
进行终止
但可以通过 kill pid
的方式终止掉,因为此时发出的信号是 SIGTERM(15),SIGTERM 是请求进程正常终止的信号
trap
被放在 ATT&CK
中权限维持模块的一部分,不知道是不是指文中提出的这种用法呢?与人斗还是比与机器斗有意思一些。