1.跳轉到地址 使用JumpJump to Address命令或在處於活動狀態的反組譯碼視窗中按下熱鍵G,均可以開啟Jump to Address對話方塊,如果把這個對話方塊看成Go對話方塊,可能有助於你記住相關的熱鍵。
IDA會記住你在這個對話方塊中輸入的值,並通過一個下拉式清單顯示,以方便你隨後使用
2.導覽按鈕(導航曆史) 導覽按鈕,每個按鈕旁邊還有一個記錄下拉式清單,你可以迅速訪問導航歷程清單中的任何位置,而不必遍曆整個歷程清單
3.棧幀 調用一個函數時的詳細操作步驟:
(1)調用方將被調用函數所需的任何參數放入到該函數所採用的呼叫慣例指定的位置。如果參數被放到運行時棧上,該操作可能導致程式的棧指標發生改變。 (2)調用方將控制權轉交給被調用的函數,然後,返回地址被儲存到程式棧或CPU寄存器中。 (3)如有必要,被調用的函數會配置一個棧指標(EBP) ,並儲存調用方希望保持不變的任何寄存器值。 (4)被調用的函數為它可能需要的任何局部變數分配空間。一般,通過調整程式棧指標在運行時棧上保留空間來完成這一任務。 (5)被調用的函數執行其操作,可能產生一個結果。在執行操作的過程中,被調用的函數可能會訪問調用函數傳遞給它的參數。如果函數返回一個結果,此結果通常被放置到一個特定的寄存器中,或者放置到函數返回後調用方可立即訪問的寄存器中。 (6)函數完成其操作後,任何為局部變數保留的棧空間將被釋放。通常,逆向執行第(4)步中的操作,即可完成這個任務。 (7)如果某個寄存器的值還為調用方儲存(第(3)步)著,那麼將其恢複到原始值。這包括恢複調用方的幀指標寄存器。 (8)被調用的函數將控制權返還給調用方。根據所使用的呼叫慣例,這一操作可能還會從程式棧中清除一個或多個參數。 (9)調用方一旦重新獲得控制權,它可能需要刪除程式棧中的參數。這時可能需要對棧進行調整,以將程式棧指標恢複到第(1)步以前的值。
第(3)步和第(4)步通常在進入函數時執行,它們共同稱為該函數的序言。同樣,第(6)步到第(8)步一般在函數結束時執行,它們共同構成該函數的尾聲。而第(5)步則代表函數的主體,它們是調用一個函數時執行的全部操作。
4.呼叫慣例
4.1.C呼叫慣例 調用方按從右至左的順序將函數參數放入棧中,在被調用的函數完成其操作時,調用方(而不是被呼叫者)負責從棧中清除參數。
從右至左在棧中放入參數的一個結果是,如果函數被調用,
最左邊的(第一個)參數將始終位於棧頂。這樣,無論該函數需要多少個參數,我們都可輕易找到第一個參數。 如果函數能夠接受變參,則調用方非常適於進行這種調整,因為它清楚地知道,它向函數傳遞了多少個參數,因而能夠輕鬆做出正確的調整。而被調用的函數事先無法知道自己會收到多少個參數,因而很難對棧做出必要的調整,所以如下測試:
答案:__stdcall不適用於傳入變參,如果在__stdcall函數中傳入變參,系統會自動視為C調用,以下為反組譯碼
4.2.__stdcall呼叫慣例 使用stdcall呼叫慣例的區別在於:函數結束執行時,應由被調用的函數負責刪除棧中的函數參數。
要完成這個任務,它必須清楚知道棧中有多少個參數,這就決定了只有固定參數。因此,printf這種變參的函數不能使用stdcall呼叫慣例。
根據慣例,微軟對所有由共用庫(DLL)檔案輸出的參數數量固定的函數使用stdcall約定
4.3.x86 fastcall約定 fastcall約定是stdcall約定的一個變體,它向CPU寄存器(而非程式棧)最多傳遞兩個參數,前兩個參數(左邊的兩個)將分別位於ECX和EDX寄存器中。剩餘的其他參數則以類似於stdcall約定的方式從右至左放入棧上
如:
int __fastcall fast(int a,int b,int c,int d){return 0;}int _tmain(int argc, _TCHAR* argv[]){int a = fast(1,2,3,4);由於有兩個參數被傳遞到寄存器中,只需要retn 8即可,如下彙編
4.4.C++呼叫慣例
C++類需要使用this指標,該指標必須由調用方提供,因此,它被作為參數提供。C++語言標準並未規定應如何向非靜態成員函數傳遞this指標,因此,不同編譯器使用不同的技巧來傳遞this指標,Microsoft Visual C++提供thiscall呼叫慣例,它將this傳遞到ECX寄存器中,並且和在stdcall中一樣,它要求函數清除棧中的參數
GNU g++編譯器將this看成是任何非靜態成員函數的第一個隱含參數,而在所有其他方面與使用cdecl約定相同。因此,對使用g++編譯的代碼來說,在調用非靜態成員函數之前,this被放置到棧頂,且調用方負責在函數返回時刪除棧中的參數(至少有一個參數) 4.5.系統調用 通常,系統調用會造成狀態轉換,由使用者模式進入核心模式,x86作業系統一般使用sysenter指令,系統調用的參數位於運行時棧上,並在啟動系統調用之前,在EAX寄存器中放入一個系統調用編號
5.局部變數布局 存在規定如何向函數傳遞參數的呼叫慣例,但不存在規定函數的局部變數布局的約定~即通過檢查函數的原始碼,通常無法確定函數的局部變數布局。
編譯器的第一個任務是,計算出函數的局部變數所需的空間。( 以下的計算都不是真實的,因為編譯器會額外分配更多的棧空間,可能是因為對齊,或者是因為緩衝區大小) 以下面代碼為例:
int fun(int a,int b,int c){int x;char buf[64];int y;int z;return 0;}int _tmain(int argc, _TCHAR* argv[]){fun(1,2,3);如果沒有使用幀指標寄存器(即ESP為幀指標) 可直接這樣寫:
sub esp, 76
其中的“位移量”欄顯示的是引用棧幀中的任何局部變數或參數所需的基址+位移地址:
但是,在X86中,EBP寄存器通常專門用作棧幀指標,得到的棧幀布局如圖:
6.IDA棧視圖 IDA會根據變數相對於被儲存的返回地址的位置,為變數取名。局部變數位於被儲存的返回地址之上,而函數參數則位於被儲存的返回地址之下。
0.局部變數預設命名 局部變數名稱以var_為首碼,後面跟一個表示變數與被儲存的幀指標之間距離(以位元組為單位)的十六進位尾碼。 如局部變數var_C是一個4位元組(dword)變數,它位於所儲存的幀指標之上,距離為12位元組([ebp-oCh])。
1.函數參數預設命名 函數參數名則以arg_為首碼,後面跟一個表示其與最頂端的參數之間的相對距離的十六進位尾碼 因此,最頂端的4位元組參數名為arg_0,而隨後的參數則分別為arg_4、arg_8、arg_C
arg_0對應ebp+8, 依次類推 我們可以右擊彙編中的arg_0(不是摘要中的,是彙編代碼中的),菜單中就顯示了arg_0的實際位置:
DA只會為那些在函數中直接引用的棧變數自動產生名稱。如果DA無法確定任何對[ebp+8](第一個參數的位置)的記憶體引用,那麼arg_0就不會在摘要棧視圖中列出
特別注意的是: var_局部變數的定義順序和你在代碼中的聲明循序關聯性不大,你必須通過反組譯碼視窗中的一系列線索,將IDA產生的變數名稱與原始碼中使用的名稱對應起來。 注意觀察的和傳入參數的關係來關聯局部變數
2.雙擊變數,會跳轉到變數的詳細視圖
顯示的兩個特殊值分別為s和r(前面均帶有空格)。這些偽變數是IDA表示被儲存的返回地址(r)和被儲存的寄存器值(s僅代表EBP)的特殊方法