使用history
命令可以查看在shell中直接執行過的命令,但是無法查看間接執行過的命令,或者說啓動過的子進程。舉個例子,shell
腳本或者make
命令都會啓動一些子進程,這些子進程並不會顯示在history
命令的輸出中,那麼如何監控他們啓動過哪些子進程呢?
首先需要明確的是,shell
腳本靠得也是sh
可執行程序加載執行,./foo.sh
的效果一般等同於sh ./foo.sh
,因此監控shell
腳本啓動過的子進程和監控其他可執行程序啓動過的子進程是一樣的。
假設我們有以下Makefile
:
all: main
main: main.cpp
g++ -o main main.cpp
clean:
rm main
現在我們來想辦法監控當我們執行make
和make clean
時有哪些子進程被啓動了。
auditd
如果你有root
權限的話,那利用現成的auditd
工具可以輕鬆的完成這項任務。auditd
能監控的不只是單個進程啓動過哪些子進程,它能監控整個系統中啓動過哪些進程。除此之外,它還能監控哪些進程調用過我們指定監控的系統調用,以及哪些進程讀/寫/執行過我們指定監控的文件或目錄,功能非常強大,有興趣的可以參考一下auditd
的文檔。
安裝好auditd
後,按照如下方式配置監控規則:
auditctl -a task,always
然後執行make
和make clean
。
最後執行以下命令查看監控日誌:
ausearch -i -sc execve
對輸出結果過濾一下,我們得到以下列表:
type=EXECVE msg=audit(2019年07月21日 10:40:45.562:213) : argc=1 a0=make
type=EXECVE msg=audit(2019年07月21日 10:40:45.762:318) : argc=4 a0=g++ a1=-o a2=main a3=main.cpp
type=EXECVE msg=audit(2019年07月21日 10:40:45.846:424) : argc=18 a0=/usr/lib/gcc/x86_64-linux-gnu/7/cc1plus a1=-quiet a2=-imultiarch a3=x86_64-linux-gnu a4=-D_GNU_SOURCE a5=main.cpp a6=-quiet a7=-dumpbase a8=main.cpp a9=-mtune=generic a10=-march=x86-64 a11=-auxbase a12=main a13=-fstack-protector-strong a14=-Wformat a15=-Wformat-security a16=-o a17=/tmp/ccgkuFBX.s
type=EXECVE msg=audit(2019年07月21日 10:41:01.427:5798) : argc=5 a0=as a1=--64 a2=-o a3=/tmp/ccr9a4fi.o a4=/tmp/ccgkuFBX.s
type=EXECVE msg=audit(2019年07月21日 10:41:02.671:6006) : argc=47 a0=/usr/lib/gcc/x86_64-linux-gnu/7/collect2 a1=-plugin a2=/usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so a3=-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper a4=-plugin-opt=-fresolution=/tmp/cccPuGlF.res a5=-plugin-opt=-pass-through=-lgcc_s a6=-plugin-opt=-pass-through=-lgcc a7=-plugin-opt=-pass-through=-lc a8=-plugin-opt=-pass-through=-lgcc_s a9=-plugin-opt=-pass-through=-lgcc a10=--sysroot=/ a11=--build-id a12=--eh-frame-hdr a13=-m a14=elf_x86_64 a15=--hash-style=gnu a16=--as-needed a17=-dynamic-linker a18=/lib64/ld-linux-x86-64.so.2 a19=-pie a20=-z a21=now a22=-z a23=relro a24=-o a25=main a26=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o a27=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o a28=/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o a29=-L/usr/lib/gcc/x86_64-linux-gnu/7 a30=-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu a31=-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib a32=-L/lib/x86_64-linux-gnu a33=-L/lib/../lib a34=-L/usr/lib/x86_64-linux-gnu a35=-L/usr/lib/../lib a36=-L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. a37=/tmp/ccr9a4fi.o a38=-lstdc++ a39=-lm a40=-lgcc_s a41=-lgcc a42=-lc a43=-lgcc_s a44=-lgcc a45=/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o a46=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
type=EXECVE msg=audit(2019年07月21日 10:41:03.107:6183) : argc=47 a0=/usr/bin/ld a1=-plugin a2=/usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so a3=-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper a4=-plugin-opt=-fresolution=/tmp/cccPuGlF.res a5=-plugin-opt=-pass-through=-lgcc_s a6=-plugin-opt=-pass-through=-lgcc a7=-plugin-opt=-pass-through=-lc a8=-plugin-opt=-pass-through=-lgcc_s a9=-plugin-opt=-pass-through=-lgcc a10=--sysroot=/ a11=--build-id a12=--eh-frame-hdr a13=-m a14=elf_x86_64 a15=--hash-style=gnu a16=--as-needed a17=-dynamic-linker a18=/lib64/ld-linux-x86-64.so.2 a19=-pie a20=-z a21=now a22=-z a23=relro a24=-o a25=main a26=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o a27=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o a28=/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o a29=-L/usr/lib/gcc/x86_64-linux-gnu/7 a30=-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu a31=-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib a32=-L/lib/x86_64-linux-gnu a33=-L/lib/../lib a34=-L/usr/lib/x86_64-linux-gnu a35=-L/usr/lib/../lib a36=-L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. a37=/tmp/ccr9a4fi.o a38=-lstdc++ a39=-lm a40=-lgcc_s a41=-lgcc a42=-lc a43=-lgcc_s a44=-lgcc a45=/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o a46=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
type=EXECVE msg=audit(2019年07月21日 10:41:15.275:8033) : argc=2 a0=make a1=clean
type=EXECVE msg=audit(2019年07月21日 10:41:15.279:8138) : argc=2 a0=rm a1=main
type=EXECVE msg=audit(2019年07月21日 10:41:28.632:8227) : argc=4 a0=ausearch a1=-i a2=-sc a3=execve
可以看出來,除了顯而易見的make
、g++
和rm
命令外,還有cc1plus
、as
、collect2
和ld
命令被執行過。
阻擋我們使用auditd
最大的不利因素在於它需要root
權限,作爲普通用戶,有沒有方法呢?
strace
strace
在我之前的博客中已經提到過了,是一個用來跟蹤程序系統調用的工具。如果我們跟蹤的系統調用是execve
,就能得知程序啓動過哪些子進程。
執行以下命令,其中-e用來指定需要跟蹤的系統調用,-f表示我們需要對子進程也發起跟蹤:
strace -e execve -f make
strace -e execve -f make clean
將輸出結果過濾後,有以下內容:
execve("/usr/bin/make", ["make"], 0x7ffc75c3baa8 /* 65 vars */) = 0
[pid 3523] execve("/usr/bin/g++", ["g++", "-o", "main", "main.cpp"], 0x561d6e982060 /* 70 vars */) = 0
[pid 3524] execve("/usr/lib/gcc/x86_64-linux-gnu/7/cc1plus", ["/usr/lib/gcc/x86_64-linux-gnu/7/"..., "-quiet", "-imultiarch", "x86_64-linux-gnu", "-D_GNU_SOURCE", "main.cpp", "-quiet", "-dumpbase", "main.cpp", "-mtune=generic", "-march=x86-64", "-auxbase", "main", "-fstack-protector-strong", "-Wformat", "-Wformat-security", "-o", "/tmp/ccr71lJH.s"], 0x14506d0 /* 75 vars */) = 0
[pid 3525] execve("/usr/bin/as", ["as", "--64", "-o", "/tmp/ccsBGiqv.o", "/tmp/ccr71lJH.s"], 0x14506d0 /* 75 vars */) = 0
[pid 3526] execve("/usr/lib/gcc/x86_64-linux-gnu/7/collect2", ["/usr/lib/gcc/x86_64-linux-gnu/7/"..., "-plugin", "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "-plugin-opt=/usr/lib/gcc/x86_64-"..., "-plugin-opt=-fresolution=/tmp/cc"..., "-plugin-opt=-pass-through=-lgcc_"..., "-plugin-opt=-pass-through=-lgcc", "-plugin-opt=-pass-through=-lc", "-plugin-opt=-pass-through=-lgcc_"..., "-plugin-opt=-pass-through=-lgcc", "--sysroot=/", "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "--as-needed", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-pie", "-z", "now", "-z", "relro", "-o", "main", "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], 0x1450be0 /* 77 vars */) = 0
[pid 3527] execve("/usr/bin/ld", ["/usr/bin/ld", "-plugin", "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "-plugin-opt=/usr/lib/gcc/x86_64-"..., "-plugin-opt=-fresolution=/tmp/cc"..., "-plugin-opt=-pass-through=-lgcc_"..., "-plugin-opt=-pass-through=-lgcc", "-plugin-opt=-pass-through=-lc", "-plugin-opt=-pass-through=-lgcc_"..., "-plugin-opt=-pass-through=-lgcc", "--sysroot=/", "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "--as-needed", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-pie", "-z", "now", "-z", "relro", "-o", "main", "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "/usr/lib/gcc/x86_64-linux-gnu/7/"..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], 0x7ffe13bbc388 /* 77 vars */) = 0
execve("/usr/bin/make", ["make", "clean"], 0x7ffc66153780 /* 65 vars */) = 0
[pid 3533] execve("/bin/rm", ["rm", "main"], 0x56437f262a90 /* 70 vars */) = 0
結果和使用auditd
是一樣的。
使用strace
不需要root
權限,這點非常好。
修改內核
可以在內核中fork
和execve
實現的函數中插入一些printk
語句,也算是一種方法,不過實施起來條件比較苛刻。如果沒有auditd
可用,而你又監控系統範圍內啓動的所有進程,可以試一試這種方法。
上面就是我能想到的幾種監控啓動的子進程的方法,不知道各位讀者還有什麼高招,可以分享一下。