這里說的“后門”并不是教你做壞事,而是讓你做好事,搭建自己的調(diào)試工具更好地進(jìn)行調(diào)試開發(fā)。我們都知道,當(dāng)程序發(fā)生異常錯(cuò)誤時(shí),我們需要定位到錯(cuò)誤,有時(shí)我們還想,我們?cè)诓恍薷某绦虻那疤嵯?,就能通過log來定位錯(cuò)誤呢?有人會(huì)說,我在我的程序里加多點(diǎn)打印就好了,程序每做一步我就加一行打印,到時(shí)一查log就知道程序在哪一步死掉的了。這個(gè)方法在小程序里也許會(huì)行得通,但是,在一個(gè)大型系統(tǒng),每秒的log達(dá)到幾百條,那時(shí)我們?cè)趺茨茉谶@繁多的log里找出我們想要的那條的log的?這工作量大得夸張。工程中的解決方法就是給自己的程序開個(gè)后門專門給開發(fā)人員來調(diào)試程序。
當(dāng)然我們通過后門能做的不僅僅是這些,具體來說,后門就是我們程序眼和跑著的程序交流的一道門。
搭建這么一個(gè)程序后門主要有這么幾個(gè)關(guān)鍵點(diǎn):
在進(jìn)程里開一個(gè)線程用于充當(dāng)debug center
該線程通過fifo接收開發(fā)人員傳給它的命令
解析這些命令
用腳本搭建簡(jiǎn)單的命令行界面
一、創(chuàng)建debug center線程
這個(gè)就沒什么好說了,我使用了上篇文章《Linux編程之自定義消息隊(duì)列》所搭建的消息隊(duì)列框架,將其中的msg_sender1改造為debug_center線程,作為我們的程序后門,我們跟程序交互就是從這里開始的。
if(pthread_create(&debug_thread_id, NULL, (void*)debug_center, NULL)){ MY_LOG(FATAL,"create debug center fail!\n"); return -1;}
二、創(chuàng)建FIFO
為什么要?jiǎng)?chuàng)建FIFO(有名管道)?因?yàn)槲覀冃枰覀兊某绦蜻M(jìn)行通信,我們需要把我們的指令告訴程序,那就需要一個(gè)通信途徑,F(xiàn)IFO就是一個(gè)很好的選擇。我們把我們的指令寫進(jìn)管道,程序?qū)⒅噶顝墓艿莱觯缓髨?zhí)行該指令,這樣子我們程序后門的通信模型就出來了。why解決了,是時(shí)候解決how了。
對(duì)于管道的操作,我是這么做的:
system("rm /vob/ljsdpoenew3/exercise/debug_log"); //每次進(jìn)入debug center我們都將原來的fifo文件刪除,避免影響后面操作rc = mkfifo("/vob/ljsdpoenew3/exercise/debug_log", 0666); //創(chuàng)建fifoif(rc < 0){ MY_LOG(DEBUG, "make fifo fail!\n"); pthread_exit(0);} fp = fopen("/vob/ljsdpoenew3/exercise/debug_log", "r"); //打開fifo,讀取指令if(fp == NULL){ MY_LOG(DEBUG, "open debug_log fail!\n"); pthread_exit(0);}
讀fifo我們解決了,那怎么將我們的指令寫進(jìn)fifo呢?這里我打算使用shell的read指令,文章后面會(huì)解釋如何實(shí)現(xiàn)。
三、解析指令
解析指令又可以分為兩個(gè)步驟:
將從fifo取得數(shù)據(jù)進(jìn)行格式解析,比如我定義了d d的意思是display debug,即顯示現(xiàn)在的debug級(jí)別,那么我們程序就得首先對(duì)原始數(shù)據(jù)進(jìn)行格式處理。
將指令進(jìn)行命令解析,執(zhí)行相應(yīng)操作。
格式處理:
static int get_args(FILE *inputFile){ char tmpBuffer[100]; char *line = tmpBuffer; char separator[] = " ,\n\t"; char *token; int i; char eof; int num = 0; eof = !fgets(line, sizeof(tmpBuffer), inputFile); if (eof) return num; token = strtok(line, separator); while (num < MAX_NUM_ARGS && token) { strcpy(args[num], token); num++; token = strtok(NULL, separator); } for (i = num; i < MAX_NUM_ARGS; i++) args[i][0] = 0; return num;}
命令解析:
switch(args[0][0]) //解析指令,看每個(gè)指令對(duì)應(yīng)哪些意思 { case 'd': //display switch(args[1][0]) { case 'q': //display queue show_MQ(fd); break; case 'd': //display debug show_debug_level(fd); break; default: help_manual(fd); break; } break; case 's': //set switch(args[1][0]) { case 'd': //set debug level n = atoi(args[2]); //將字符串轉(zhuǎn)化為整數(shù) fprintf(fd," debug level change from %d to %d",global.debug_level,n); global.debug_level = n; //更改log級(jí)別 break; default: help_manual(fd); break; } break; default: help_manual(fd); break; }
四、搭建debug界面
先上界面圖:
我們敲的每個(gè)命令都是通過該界面?zhèn)鬟f到程序那頭的,比如“d d”就表示展示出現(xiàn)在系統(tǒng)運(yùn)行時(shí)的log級(jí)別,而“s d”就是設(shè)置我們想要看的log級(jí)別,這樣我們就可以實(shí)現(xiàn)通過程序后門動(dòng)態(tài)修改程序走向了。
那該如何實(shí)現(xiàn)這個(gè)看似簡(jiǎn)單的交互界面呢?實(shí)現(xiàn)該交互界面,有幾個(gè)關(guān)鍵點(diǎn):
我們需將程序的打印輸出重定向到一個(gè)文件里
使用shell腳本讀出文件的內(nèi)容
我們輸入的命令需寫入到fifo中
以上三點(diǎn)就是做成界面的最重要的技術(shù)問題。
重定向輸出
一開始我是打算將標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤都重定向到文件里的額,但是想了想,還是想直接把打印內(nèi)容直接輸出到文件就好了。比如這樣:
fd = fopen("/vob/ljsdpoenew3/exercise/debug_log2", "w+"); if(fd == NULL) { MY_LOG(DEBUG, "open debug_log2 fail!\n"); pthread_exit(0); }fprintf(fd," debug level change from %d to %d",global.debug_level,n);
這樣我們?cè)赿ebug center產(chǎn)生的打印都輸出到文件debug_log2上了。
2.利用腳本讀出內(nèi)容且將內(nèi)容寫入fifo
要做到這點(diǎn)需要shell編程的簡(jiǎn)單知識(shí),我們?cè)趺磳⑽覀兊妮斎敕诺絝ifo呢?我們?cè)趺窗裭og文件的內(nèi)容讀出來顯示在顯示屏呢?我想到首先是再寫個(gè)client,進(jìn)行進(jìn)程間通信嘛,將命令寫到fifo,同時(shí)也讀出log文件的內(nèi)容。但是,我發(fā)現(xiàn)利用shell就可以做到以上的事了,而且只需花費(fèi)幾行代碼。
#!/bin/bash my_inp_fifo=/vob/ljsdpoenew3/exercise/debug_log # 指定fifo文件stty erase ^H while [ true ];do read inp #循環(huán)讀入用戶輸入 if [ "$inp" == "quit" ];then #quit代表結(jié)束該界面 exit 0 fi echo $inp > $my_inp_fifo #寫入fifo cat debug_log2.txt #顯示log的內(nèi)容done
那看看這個(gè)命令行界面跑起來是怎么的吧!
首先我們運(yùn)行server進(jìn)程
sever進(jìn)程不斷產(chǎn)生消息并處理消息。
我們打開另一個(gè)窗口,并執(zhí)行腳本./test.sh,進(jìn)入界面
我們使用了d d命令看到了程序此時(shí)的debug級(jí)別,用d q看出了程序消息隊(duì)列的情況。
我們使用了s d指令將debug level設(shè)置為0,此時(shí)屏幕沒有任何打印輸出,當(dāng)我們?cè)谑褂胹 d指令將level設(shè)置為-1(即將所有位置一),此時(shí)所有打印級(jí)別都打開了,屏幕又開始瘋狂打印了。也就說,我們通過后門操控了程序,這里我們只是僅僅修改了程序的log級(jí)別,當(dāng)然我們還可以做更多的事,只要依照這個(gè)框架往里面加指令,以及對(duì)應(yīng)的處理操作,就可以實(shí)現(xiàn)了。
五、總結(jié)
所謂后門,就是一個(gè)可以操控程序的接口,這個(gè)接口僅僅用于開發(fā)者調(diào)試開發(fā),不會(huì)開放給客戶。所以這個(gè)后門的作用非常巨大,所以是開發(fā)者調(diào)試程序的一大利器。有人會(huì)想,我想用socket來代替fifo進(jìn)行進(jìn)程通信可以不,這樣就可以做到遠(yuǎn)程主機(jī)操控程序了。我覺得是可以的,但是感覺利用telnet到目的主機(jī)再運(yùn)行腳本操作比較安全。
最后給出源代碼框架
1 #include 2 #include
?
評(píng)論
查看更多