簡體   English   中英

如何找到C程序的“退出”

[英]How to find the “exit” of a C program

該測試是在32-bit x86 Linux上進行的。

因此,基本上,我試圖通過在匯編代碼中插入檢測指令來記錄已執行的基本塊的信息。

我的策略是這樣的:在globl數組中寫入已執行的基本塊的索引,並在數組已滿(16M)時將其從內存刷新到磁盤。

這是我的問題。 當檢測的二進制文件執行結束時,即使它沒有達到16M邊界,我也需要將數組刷新到磁盤。 但是,我只是不知道在哪里可以找到assembly程序的出口。

我嘗試了這個:

  1. grep exit從目標匯編程序grep exit ,並在call exit指令之前刷新內存。 但是根據一些調試經驗,目標C程序(例如md5sum二進制文件)在完成執行后不會調用exit

  2. main功能末尾刷新內存。 但是,在匯編代碼中,我只是不知道main函數的確切結尾在哪里。 我可以采取保守的方法,例如,查找所有ret指令,但是在我看來,並非所有main功能都以ret指令結尾。

所以這是我的問題,如何識別assembly code的確切執行端,並在其中插入一些檢測指令? 掛鈎一些庫代碼對我來說很好。 我了解使用不同的輸入,二進制可以在不同的位置退出,所以我想我需要一些保守的估計。 我清楚嗎? 謝謝!

我相信您通常無法做到這一點。 首先,如果main正在返回某些代碼,則它是一個退出代碼(如果main沒有顯式return則最新的C標准要求編譯器添加隱式 return 0; )。 然后,一個函數可以將exit地址存儲在某些數據中(例如,全局函數, struct的字段等),而其他一些函數可以通過函數指針來間接調用它。 實際上,程序可以使用dlopen加載某些插件,並使用dlsym作為"exit"名稱,或者只是在插件內部調用exit ,等等。。。AFAIU完全解決了該問題(從動態意義上尋找實際的exit調用)可以證明等同於停止問題 另請參見賴斯定理

在不要求詳盡無遺的方法的情況下,我會提出其他建議(假設您對使用C或C ++等編碼的程序感興趣,...您可以使用其源代碼)。 您可以使用MELT自定義GCC編譯器,以更改在GCC中處理的基本塊,以調用某些檢測函數。 它不是很簡單,但是是可行的...當然,您需要使用這種定制的GCC重新編譯一些C代碼以對其進行檢測。

(免責聲明,我是MELT的主要作者;請隨時與我聯系以獲取更多信息...)

順便說一句,您了解atexit(3)嗎? 它可能對您的刷新問題有幫助...,並且您還可以使用LD_PRELOAD技巧(有關動態鏈接器的信息 ,請參閱ld-linux(8) )。

atexit()將正確處理95%以上的程序。 您可以修改其已注冊處理程序鏈,也可以像其他模塊一樣對其進行檢測。 但是,某些程序可能會通過使用不調用atexit處理程序的_exit()終止。 可能檢測_exit來調用數據刷新並在其類似BSD的程序上安裝atexit(或on_exit() )處理程序應覆蓋幾乎100%的程序。


附錄:請注意, Linux基本規范 指出 C庫啟動應:

調用初始化函數(* init)()。
用適當的參數調用main()。
使用main()的返回值調用exit()。

每次都應該起作用的方法是創建一個共享內存部分,用於在其中存儲數據。

您還創建了一個子進程,該進程正在等待調試的進程完成。

待調試的進程完成后,子進程將使用共享內存中的數據來完成寫操作。

這應該適用於所有形式的退出,過程中斷(例如Ctrl + C,關閉終端窗口等),或者即使該過程已使用“ kill”殺死。

但是根據一些調試經驗,目標C程序(例如md5sum二進制文件)在完成執行后不會調用exit。

讓我們看一下i686 GNU / Linux系統上的md5sum二進制文件:

在反匯編中( objdump -d /usr/bin/md5sum ),我們有:

Disassembly of section .text:

08048f50 <.text>:
 8048f50:       55                      push   %ebp
 8048f51:       89 e5                   mov    %esp,%ebp
 8048f53:       57                      push   %edi
 8048f54:       56                      push   %esi
 8048f55:       53                      push   %ebx
 8048f56:       83 e4 f0                and    $0xfffffff0,%esp
 8048f59:       81 ec c0 00 00 00       sub    $0xc0,%esp
 8048f5f:       8b 7d 0c                mov    0xc(%ebp),%edi

[ ... ]

 8049e8f:       68 b0 d6 04 08          push   $0x804d6b0
 8049e94:       68 40 d6 04 08          push   $0x804d640
 8049e99:       51                      push   %ecx
 8049e9a:       56                      push   %esi
 8049e9b:       68 50 8f 04 08          push   $0x8048f50
 8049ea0:       e8 4b ef ff ff          call   8048df0 <__libc_start_main@plt>
 8049ea5:       f4                      hlt    

這是所有啟動樣板代碼。 實際程序的main調用在__libc_start_main調用內調用。 如果程序從那里返回,那么,看,有一條hlt指令。 那是你的目標。 查找該hlt指令並將其作為程序的結尾。

您可以嘗試以下方法:

int main() 
bool keepGoing = true;
{
    while(keepGoing) {
        string x;
        cin >> x;
        if(x == "stop") {
            keepGoing = false;
        }
    }
}

即使它是原始的……我可能也砍掉了代碼,但這只是一個概念。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM