IDA PRO 靜態反彙編與OllyDbg動態調試實戰技巧彙總
**********************************
案例一: 使用IDA PRO+OllyDbg+PEview 追蹤windows API 動態鏈接庫函數的調用過程。
首先用文本編輯器寫一個C++源程序名爲StackFrame.cpp ,代碼如下:
#include "stdio.h"
long add(long a, long b)
{
long x = a, y = b;
return (x + y);
}
int main(int argc, char* argv[])
{
long a = 1, b = 2;
printf("%d\n", add(a, b));
return 0;
}
使用visual C++ IDE 對該源文件執行編譯,彙編,鏈接一系列的操作後,最終生成的二進制可執行文件(PE格式)名爲StackFrame.exe
回顧上述源碼,main函數調用了標準C庫函數printf打印信息,我們想要知道,
visual C++ 編譯器是如何實現printf庫函數的,並且StackFrame.exe採用的是靜態鏈接還是動態鏈接,如果是後者,那麼printf又調用了哪些位於動態鏈接庫中的windows API函數?抑或是“動靜”兼用?
要解答這些疑問,首先使用IDA PRO打開StackFrame.exe ,它會自動查找程序入口點,這是由編譯器自動生成的啓動代碼,用於初始化我們編寫的main函數執行前的環境,以及執行main函數退出後的收尾工作。
一般而言,只要反彙編的對象不是經過加殼或者插入了模糊代碼,IDA PRO可以輕鬆識別程序入口點與main函數,這裏爲了簡化分析流程,我們直接進入main函數的反彙編代碼段,如下所示:
我們通過上面一系列的IDA PRO截圖可以看到,地址 0x00404063處的call指令想要調用EnterCriticalSection函數,後者的地址嘗試以call的操作數0x0040A018給出,但是這個地址處的值,即EnterCriticalSection函數的最終地址,需要操作系統加載器加載StackFrame.exe文件時才能確定,因此我們對磁盤上的文件反彙編時,無法確定動態鏈接庫中函數的地址。
下面以PEview工具查看StackFrame.exe在磁盤上的“真實”面貌(而不是由IDA PRO “模擬”的運行時地址空間面貌)
之所以要先使用PEview工具,是因爲後面的動態調試中,會與此處的信息對比,來加深理解程序在磁盤上與在內存中的異同。
使用PEview工具打開StackFrame.exe ,我們的重點是定位到PE文件中的導入表,因爲無論是操作系統加載器,反彙編器,還是動態調試器,都依賴該表來解析導入的動態鏈接庫與其中的函數。
我們嘗試在原始PE文件中,計算並找出call ds:EnterCriticalSection 指令對應的字節序列,其實這沒有想象中困難,而且在這裏提出計算方法的原因是,後面會用同樣的計算手法來判斷Ollydbg中追蹤到的EnterCriticalSection函數的實際地址,
該地址究竟屬於kernel32.dll還是ntdll.dll?(因爲有時Ollydbg給出的信息並不十分準確)屆時會用到下面的計算方法。
首先,回到IDA PRO,打開StackFrame.exe的程序段窗口,如下所示:
前面指出,地址 0x00404063處的call指令想要調用EnterCriticalSection函數,因此計算 404063 - 401000 = 3063 , 3063 + 400 = 3463
3463這個值就是該指令字節碼在StackFrame.exe的位置,下面驗證:
接下來,使用Ollydbg打開StackFrame.exe進行動態調試,我們的目標在於定位EnterCriticalSection函數的入口處,並且單步跟進,查看其中的機器碼,然後使用PEview工具,以上面的計算方法,驗證Ollydbg給出的該函數所屬的動態鏈接庫文件信息是否準確,如下截圖所示:
上面驗證了StackFrame.exe 在運行時調用的共享庫函數,與它在磁盤文件上的導入表中描述的行爲一致。
最後,我們驗證EnterCriticalSection這個 windows API函確實位於ntdll.dll這個動態鏈接庫中,作爲本案例的結尾。
首先,可以訪問微軟MSDN站點,查找關於EnterCriticalSection函數的信息:
https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms682608(v=vs.85).aspx
最後總結一下:
stack.exe使用“部分”靜態鏈接,其中多數的代碼爲庫代碼,
這包含由“編譯器庫”添加的,處理程序初始化的啓動代碼與程序退出善後的結束代碼,
以及程序中調用的庫函數,如printf等函數的代碼。
由於 printf 函數需要在屏幕打印信息,涉及更底層的系統I/O操作,因此它需要調用封裝這些系統功能的 windows API 函數,
除了windows API 函數所在的DLL(kernel32.dll,ntdll.dll)作爲動態鏈接庫在運行時加載以外,
所有其它被調用函數的二進制目標代碼都被鏈接器複製一份副本,然後鏈接到最終的可執行文件stack.exe中,
因此,stack.exe包含的庫代碼數量遠多於程序員自行編寫的代碼數量。
使用靜態鏈接的程序,很容易通過IDA PRO的全局函數調用拓撲圖識別出來,如下所示:
上面只是拋磚引玉,類似的將IDA PRO,Ollydbg,PEview,甚至WinHex,PEiD等工具結合起來應用,交叉驗證的例子不勝枚舉,通過熟練使用這些工具,不僅能提高逆向工程的效率與準確度,更重要的是,我們對處理器指令集體系結構,操作系統內存管理,以及動態鏈接的機制,編譯器,鏈接器的運行原理等等系統底層機理的認識又提升了一個檔次。
最後,限於個人知識水平有限,文中若有錯誤以及誤導之處,還請提出指正,不勝感激。