2013-02-07 70 views
5

我有一個應用程序創建了.text段轉儲的win32進程。然後它將代碼分爲基本塊。基本程序塊是一組接一個地執行的指令(跳轉總是這些基本程序塊的最後一條指令)。下面是一個例子:如何將反彙編的C代碼分解爲函數?

Basic block 1 
    mov ecx, dword ptr [ecx] 
    test ecx, ecx 
    je 00401013h 

Basic block 2 
    mov eax, dword ptr [ecx] 
    call dword ptr [eax+08h] 

Basic block 3 
    test eax, eax 
    je 0040100Ah 

Basic block 4 
    mov edx, dword ptr [eax] 
    push 00000001h 
    mov ecx, eax 
    call dword ptr [edx] 

Basic block 5 
    ret 000008h 

現在我想將這些基本塊組合在函數中 - 說哪些基本塊形成函數。算法是什麼?我必須記住,在一個函數內可能有很多ret指令。如何檢測fast_call的功能?

回答

6

用於分組塊到功能將是最簡單的算法:

  1. 記下所有的地址到電話如果這樣的地址後的第一個塊與ret,你結束與call some_address說明
  2. 做使用函數完成,否則
  3. 跟隨塊中的跳轉到另一個塊等等,直到您遵循所有可能的執行路徑(記住關於條件跳轉,其中每個都將一條路徑分成兩條)並且所有路徑都有完成與ret。你需要認識到,組織循環跳躍所以你的程序本身不通過輸入一個無限循環

問題掛起:

  1. 可以間接地進行呼叫的數量通過讀取內存的函數指針,例如你有call [some_address]代替call some_address
  2. 一些間接的電話到計算的地址進行
  3. 函數返回前調用其他函數可能有jump some_address代替call some_address緊接着ret
  4. call some_address可以用組合來模擬的push some_address + ret OR push some_address + jmp some_other_address
  5. 一些功能可以在它們的端部共用碼(例如它們具有不同的入口點,但是一個或多個出口點是相同的)

您可以使用某些試探法以確定通過尋找最常見的序言指令序列,其中功能開始:

push ebp 
mov ebp, esp 

再次,這可能無法正常工作,如果功能與抑制幀指針編譯(即他們會使用esp而不是ebp訪問堆棧中的參數,這是可能的)。

編譯器(例如MSVC++)也可以用指令填充指令間的功能空間,這也可以作爲即將到來的函數開始的提示。至於區分各種調用約定,它可能是最容易看的符號(當然,如果你有它們)。 MSVC++ generates different name prefixes and suffixes, e.g.

  • _function - CDECL
  • _function @號 - STDCALL
  • @函數@號 - FASTCALL

如果您不能提取符號信息,你必須分析代碼,看看參數如何傳遞給函數以及函數或調用者是否將它們從堆棧中移除。

3

您可以使用enter表示函數的開頭,或certain code which sets up a frame

push ebp 
mov ebp, esp 
sub esp, (bytes for "local" stack space) 

後來你會發現一個調用之前相反的代碼(或leave)到ret

mov esp, ebp 
pop ebp 

您還可以使用的字節數爲局部堆棧空間,以確定局部變量。

Identifying thiscall, fastcall等將對call之前的代碼進行一些分析,該代碼使用初始位置和對使用/清理的寄存器的評估。

1

看看像windasm或ollydbg這樣的軟件。 callret操作表示函數調用。但是,代碼不會按順序運行,並且可以在整個地方進行跳轉。 call dword ptr [edx]取決於edx寄存器,因此除非您執行運行時調試,否則您將無法知道它的位置。

要識別快速調用函數,您必須查看參數如何傳遞。 Fastcall會將前兩個指針大小的參數放在edx和ecx寄存器中,其中stdcall會將它們推入堆棧。有關說明,請參閱this article