如何對一個可執(zhí)行程序進(jìn)行攔截和包裝?
文中一共討論了3種方法,來實(shí)現(xiàn)對【函數(shù)】進(jìn)行攔截:
- 在編譯階段插樁;
- 在鏈接階段插樁;
- 在執(zhí)行階段插樁;
昨天一個網(wǎng)友提了另外一個問題:如何對一個可執(zhí)行程序進(jìn)行攔截?
他提出了一個實(shí)際的示例:
Ubuntu 18.04操作系統(tǒng)中,重啟指令/sbin/reboot是一個軟鏈接,鏈接到可執(zhí)行程序/bin/systemctl,那么是否可以在執(zhí)行systemctl之前,做一些其它的事情(例如:保持一些應(yīng)用程序的狀態(tài)數(shù)據(jù))?
- Ubuntn18.04 中使用 systemd 來管理系統(tǒng)的所有 Service;
- 除了 reboot 指令,還有其它幾個指令也是軟鏈接到 /bin/systemctl;
這里就引出一個問題了:
既然上面這6個命令都鏈接到systemctl,那么當(dāng)systemctl被執(zhí)行的時候,它是如何知道它是被哪一個命令調(diào)用的呢?
看一下源碼就知道了:通過參數(shù) argv[0] 來獲得的。
我們知道,main函數(shù)通過argc和argv[]來獲取所有的參數(shù),如下:
// 測試文件:test1.c
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("argc = %d \n", argc);
for (int i = 0; i < argc; i++)
printf("argv[%d] = %s \n", i, argv[i]);
return 0;
}
編譯、執(zhí)行一下:
$ gcc test1.c -o test1
$ ./test1 aaa bbb
argc = 3
argv[0] = ./test1
argv[1] = aaa
argv[2] = bbb
可以看到:argv[0] = ./test1,因為我們是在命令行直接調(diào)用test可執(zhí)行程序的,這很容易理解。
那么:如果test是被一個軟鏈接調(diào)用的呢?
測試一下,創(chuàng)建軟鏈接:
$ ln -s test1 link1
執(zhí)行一下:
此時,argv[0] = ./link1。
也就是說:第一個參數(shù)存放的是軟鏈接文件路徑,systemctl 的道理也是如此!
知道了這個原理,那我們就可以在reboot與systemc之間橫叉一刀,增加一個中間可執(zhí)行文件:
為了便于描述,我們把這個中間文件創(chuàng)建為腳本pre_systemctl.sh,然后把root軟鏈接到這個腳本。
注意:在理解原理之前,建議不要直接用 reboot 等系統(tǒng)命令進(jìn)行操作,可以自己寫一些測試程序,例如上面的 test。
操作如下:
$ cd /sbin
$ sudo rm root
$ sudo touch pre_systemctl.sh
$ sudo chmod +x pre_systemctl.sh
$ sudo ln -s pre_systemctl.sh reboot
創(chuàng)建了pre_systemctl.sh腳本之后,并且把reboot軟鏈接到它,在腳本中輸入如下內(nèi)容:
此時,在命令行中執(zhí)行reboot命令,就會執(zhí)行這個腳本,并且這個腳本也能夠正確的把/sbin/root作為第0個參數(shù)傳遞給/bin/systemctl,如下圖所示:
在這個腳本中,可以在執(zhí)行systemctl之前,做任何需要關(guān)機(jī)前需要處理的一些事情。
問題似乎是解決了,但是好像還有一個問題:
如果用戶在執(zhí)行命令時輸入了一些其它的參數(shù),這個腳本程序也應(yīng)該透明的把這些參數(shù)傳遞給 systemctl 才可以!
為了便于觀察,我們在腳本中多打印個參數(shù),并通過exec來啟動systemctl,并且強(qiáng)制把參數(shù)$0設(shè)置為systemctl的第0個參數(shù):
這個腳本文件中的重點(diǎn)是最后一條命令:
exec -a $0 /bin/systemctl $*
此時,在命令行中執(zhí)行reboot指令,輸出如下:
如此調(diào)用systemctl,就解決了剛才提出的問題,而且通過 $*,可以把任意多個參數(shù)透明的傳遞下去。
這里的關(guān)鍵還是 exec 的參數(shù) -a ,看一下它的指令說明:
exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
這里還有一個更詳細(xì)的說明: