IDA PRO 靜態反彙編與OllyDbg動態調試實戰技巧彙總


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函數的反彙編代碼段,如下所示:


wKioL1TbbbPBGLcqABAIPg_RLYg481.jpg


wKioL1Tbcivwf3UoAAlj6kCCqdI043.jpg


wKiom1TbdTTgqzIgAAvlvsmTEvU125.jpg


wKioL1Tbfyywlv9FAA8qq8cSFTo648.jpg


wKioL1Tbgh_jMZe2AAt1eR1_4Nc772.jpg



我們通過上面一系列的IDA PRO截圖可以看到,地址 0x00404063處的call指令想要調用EnterCriticalSection函數,後者的地址嘗試以call的操作數0x0040A018給出,但是這個地址處的值,即EnterCriticalSection函數的最終地址,需要操作系統加載器加載StackFrame.exe文件時才能確定,因此我們對磁盤上的文件反彙編時,無法確定動態鏈接庫中函數的地址。

下面以PEview工具查看StackFrame.exe在磁盤上的“真實”面貌(而不是由IDA PRO “模擬”的運行時地址空間面貌)

之所以要先使用PEview工具,是因爲後面的動態調試中,會與此處的信息對比,來加深理解程序在磁盤上與在內存中的異同。

使用PEview工具打開StackFrame.exe ,我們的重點是定位到PE文件中的導入表,因爲無論是操作系統加載器,反彙編器,還是動態調試器,都依賴該表來解析導入的動態鏈接庫與其中的函數。


wKioL1TbiofwxVjtAAmeNfpB1Z0302.jpg


wKiom1TbjLnRGqHrAAw-GjmL46A946.jpg

我們嘗試在原始PE文件中,計算並找出call ds:EnterCriticalSection 指令對應的字節序列,其實這沒有想象中困難,而且在這裏提出計算方法的原因是,後面會用同樣的計算手法來判斷Ollydbg中追蹤到的EnterCriticalSection函數的實際地址,

該地址究竟屬於kernel32.dll還是ntdll.dll?(因爲有時Ollydbg給出的信息並不十分準確)屆時會用到下面的計算方法。


首先,回到IDA PRO,打開StackFrame.exe的程序段窗口,如下所示:


wKiom1Tbkq3DyKNDAAVvE6vU8kQ650.jpg


wKioL1Tbldri-CmrAAffX-nW7UQ395.jpg


前面指出,地址 0x00404063處的call指令想要調用EnterCriticalSection函數,因此計算 404063 - 401000 = 3063 , 3063 + 400 = 3463

3463這個值就是該指令字節碼在StackFrame.exe的位置,下面驗證: 


wKioL1Tbmr-B2lGwAA3vB71tAAA572.jpg


接下來,使用Ollydbg打開StackFrame.exe進行動態調試,我們的目標在於定位EnterCriticalSection函數的入口處,並且單步跟進,查看其中的機器碼,然後使用PEview工具,以上面的計算方法,驗證Ollydbg給出的該函數所屬的動態鏈接庫文件信息是否準確,如下截圖所示: 


wKiom1TbnqnAh3gkAAow_3pB2IY289.jpg


wKiom1TboaWzelH7AAzXqvho7qE832.jpg


wKioL1TbpVah8efOAAfQRIkbYmQ235.jpg


wKiom1TbqgrzKxN2AA3RefVz9Mc758.jpg


wKioL1TbsGSw9c-_ABKkav4RGXM533.jpg


上面驗證了StackFrame.exe 在運行時調用的共享庫函數,與它在磁盤文件上的導入表中描述的行爲一致。

最後,我們驗證EnterCriticalSection這個 windows API函確實位於ntdll.dll這個動態鏈接庫中,作爲本案例的結尾。

首先,可以訪問微軟MSDN站點,查找關於EnterCriticalSection函數的信息:

https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms682608(v=vs.85).aspx


wKiom1Tbsr-zx68cAAh_aDz-bPY562.jpg



wKioL1TbuN_zrYvDABBgkv1c-z4322.jpg


wKiom1Tbu2OxbrVlAAn-SR0DdEM199.jpg


wKioL1TbwMaDy4Q6ABJwA3g***g850.jpg


wKioL1TbxKuSVWqmAA2ihTrrXCw406.jpg


最後總結一下: 

stack.exe使用“部分”靜態鏈接,其中多數的代碼爲庫代碼,

這包含由“編譯器庫”添加的,處理程序初始化的啓動代碼與程序退出善後的結束代碼,

以及程序中調用的庫函數,如printf等函數的代碼。

由於 printf 函數需要在屏幕打印信息,涉及更底層的系統I/O操作,因此它需要調用封裝這些系統功能的 windows API 函數,

除了windows API 函數所在的DLL(kernel32.dll,ntdll.dll)作爲動態鏈接庫在運行時加載以外,

所有其它被調用函數的二進制目標代碼都被鏈接器複製一份副本,然後鏈接到最終的可執行文件stack.exe中,

因此,stack.exe包含的庫代碼數量遠多於程序員自行編寫的代碼數量。

使用靜態鏈接的程序,很容易通過IDA PRO的全局函數調用拓撲圖識別出來,如下所示:


wKiom1TcXfvxjG5FABLBV86WfPs750.jpg




上面只是拋磚引玉,類似的將IDA PRO,Ollydbg,PEview,甚至WinHex,PEiD等工具結合起來應用,交叉驗證的例子不勝枚舉,通過熟練使用這些工具,不僅能提高逆向工程的效率與準確度,更重要的是,我們對處理器指令集體系結構,操作系統內存管理,以及動態鏈接的機制,編譯器,鏈接器的運行原理等等系統底層機理的認識又提升了一個檔次。

最後,限於個人知識水平有限,文中若有錯誤以及誤導之處,還請提出指正,不勝感激。


wKioL1VosLezmXm4ABXYIzf4PUQ271.jpg

wKiom1VorzfCRG1wABVrRUjQ7uo310.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章