使用Ptrace跟蹤進程收到的異常信號(信號SIGTRAP是經過traceme後wait獲得的)

如下是使用Ptrace跟蹤進程收到的異常信號的正文內容,本文是網友投稿或本站會員創做,不表明本站觀點: html

最初的想法,咱們在調試程序的時候,程序有時會出現進程收到SIGSEGV信號,異常退出。若是使用GDB,經過攔截該信號,並打印堆棧來實現,但在嵌入式的環境裏,須要在嵌入式設備註入gdb server,來進行調試,比較麻煩。 linux

個人一個想法,可以直接寫一個程序,跑在設備裏,攔截到特殊信號後,就打印堆棧呢? 函數

有兩個難點: 測試

一、 怎麼樣實現信號的攔截,發現信號後,開始打印堆棧。 google

二、 打印堆棧,最好可以連帶調用函數時的參數值。 調試

信號的攔截 server

一開始我想到了使用ptrace,由於gdb是以它爲基礎的,gdb可以作到,它也可以作到。但經過查資料,ptrace主要是攔截系統調用,或者進行單步跟蹤,沒有談及怎麼樣跟蹤信號。那麼在主程序,fork出子程序後,註冊一個信號處理函數,在信號處理函數中打印出堆棧是否可行呢? htm

通過測試發現,在exec以後,起來的進程會沖掉原來該子進程的代碼段,從而註冊的信號處理事件失效。 進程

這條路是否到頭了呢? 事件

繼續想到,既然exec會沖掉原來的代碼段,那麼我是否能夠採用把程序起來後,經過黑客的手段注入代碼,並執行來實現呢?

理論上來說,應該可行,但難度比較大。

峯迴路轉……

在linux內核分析的時候,發現這麼一段話:

在do_signal中,首先它檢查current接收進程是否正受某一進程的監控:既然這樣,do_signal就調用notify_parent()和schedule讓監控進程知道進行的信號處理。

這說明signal的信號處理確定是可以被監控的。

經過google發現,strace是可以攔截signal的。我經過分析strace的源代碼,發現了strace是怎麼樣監控信號。

Child=fork()

If(child==0)

{

ptrace(PTRACE_TRACEME, 0, NULL, NULL);

Exec //執行待監控的進程

}

Else

{

pid = wait4(-1, &status, wait4_options, cflag ? &ru : NULL);

if (WIFSIGNALED(status)) //檢查是否由於信號掛起

if (WIFEXITED(status))

if (!WIFSTOPPED(status))

if (WSTOPSIG(status) != SIGTRAP) //注意SIGSEGC信號就是經過這裏處理的。

也就是說,對於信號的攔截,它是經過標記爲trace以後,經過wai的status來判斷的。而不是象通常針對系統調用的跟蹤。

既然找到了地方,接下來的就很容易了,打印堆棧:

long ebp=0,eip=0,i=0;

struct user_regs_struct regs;

ptrace(PTRACE_GETREGS, pid,NULL,®s);

tprintf("EIP: %lx ----EBP:%lx/n",regs.eip,regs.ebp); //首先得到當前的ebp寄存器中的地址,該地址指向了堆棧中的幀。

ebp = regs.ebp;

eip = regs.eip;

for(;;)

{

i++;

tprintf("%d: eip:%lx ebp:%lx/n",i,eip,ebp);

eip = ptrace(PTRACE_PEEKDATA,pid,ebp+4,NULL); //根據幀的結構,eip的地址在ebp地址+4,把來把其解析成函數,具體的文本就全靠它了。

ebp = ptrace(PTRACE_PEEKDATA,pid,ebp,NULL); //注意先後兩行的順序,這個命令使ebp指向堆棧中的上一個幀

if( 0 == ebp)

break;

}

這樣,就可以把堆棧中,各個函數所運行的地址拿到。

你可使用objdump –D 反編譯你所跟蹤的進程,來得到其對應的函數。

由於打印堆棧不是在所跟蹤的進程內,因此不可使用backtrace_symbols來對其進行解析,怎麼樣把其解析成直觀的函數調用,目前我考慮本身實現反彙編來編寫,打印出函數的名稱。

採用backtrace_symbols有個侷限,在編譯程序時必須加上-rdynamic才行,按理說經過objdump反編譯能夠看到,應該更可靠一些。

本文連接:http://www.linux-cn.com/html/test/20070412/2709.shtml

相關文章
相關標籤/搜索