linux下監控shell腳本或可執行程序啓動過的子進程

使用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

現在我們來想辦法監控當我們執行makemake clean時有哪些子進程被啓動了。

auditd

如果你有root權限的話,那利用現成的auditd工具可以輕鬆的完成這項任務。auditd能監控的不只是單個進程啓動過哪些子進程,它能監控整個系統中啓動過哪些進程。除此之外,它還能監控哪些進程調用過我們指定監控的系統調用,以及哪些進程讀/寫/執行過我們指定監控的文件或目錄,功能非常強大,有興趣的可以參考一下auditd的文檔。

安裝好auditd後,按照如下方式配置監控規則:

auditctl -a task,always

然後執行makemake 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 

可以看出來,除了顯而易見的makeg++rm命令外,還有cc1plusascollect2ld命令被執行過。

阻擋我們使用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權限,這點非常好。

修改內核

可以在內核中forkexecve實現的函數中插入一些printk語句,也算是一種方法,不過實施起來條件比較苛刻。如果沒有auditd可用,而你又監控系統範圍內啓動的所有進程,可以試一試這種方法。

上面就是我能想到的幾種監控啓動的子進程的方法,不知道各位讀者還有什麼高招,可以分享一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章