7.IDA-建立結構體

來源:互聯網
上載者:User
結構體分類

結構體的一個顯著特點在於,結構體中的資料欄位是通過名稱訪問,而不是像數組那樣通過索引訪問。不好的是,欄位名稱被編譯器轉換成了數字位移量。結果,在反組譯碼代碼清單中,訪問結構體欄位的方式看起來與使用常量索引訪問數組元素的方式極其相似。

注意的是,結構體中有個記憶體對齊規則,所以不要認為編譯器會利用所需的最小空間來分配結構體。預設情況下,編譯器會設法將結構體欄位與記憶體位址對齊,以最有效地讀取和寫入這些欄位 1. 全域分配的結構體

和全域分配的數組一樣,編譯器在編譯時間可獲知全域分配的結構體的地址。這使得編譯器能夠在編譯時間計算出結構體中每個成員的地址,而不必在運行時進行任何計算。

如下代碼:

struct stTest{int    f1;short  f2;char   f3;int    f4;double f5;};stTest g_st;void fun(){g_st.f1 = 10;g_st.f2 = 20;g_st.f3 = 30;g_st.f4 = 40;g_st.f5 = 50.0;}

對應彙編為:可以看到,在這個反組譯碼代碼清單中,訪問結構體成員不需要任何算術計算,如果沒有原始碼,你根本無法斷定這個程式使用了結構體。

push    ebpmov     ebp, espmov     dword_403018, 10 ; int f1 = 10mov     eax, 20mov     word_40301C, ax ; word f2 = 20mov     byte_40301E, 30 ; byte f3 = 30mov     dword_403020, 40 ; int f4 = 40fld     ds:dbl_4020E0   ; f5 = dbl_403028 = 50.0fstp    dbl_403028pop     ebpretn

2. 棧分配的結構體

同樣,僅僅根據棧布局,同樣很難識別出棧分配的結構體。

void fun(){stTest l_st;l_st.f1 = 10;l_st.f2 = 20;l_st.f3 = 30;l_st.f4 = 40;l_st.f5 = 50.0;}

對應彙編為:同樣,訪問結構體中的欄位不需要進行任何算術計算,因為在編譯時間,編譯器能夠確定棧幀內每個欄位的相對位移量

000 push    ebp004 mov     ebp, esp004 sub     esp, 18h01C mov     [ebp+var_18], 1001C mov     eax, 14h01C mov     [ebp+var_14], ax01C mov     [ebp+var_12], 3001C mov     [ebp+var_10], 4001C fld     ds:dbl_4020E001C fstp    [ebp+var_8]01C mov     esp, ebp004 pop     ebp000 retn
3. 堆分配的結構體

由於結構體的地址在編譯時間未知,編譯器別無選擇,只有產生代碼來計算每個欄位在結構體中的正確位移量。

如果一個結構體在堆中分配,那麼對編譯器來說,引用該結構體的唯一線索就是指向該結構體起始地址的指標。

void fun(){stTest* pst = new stTest;pst->f1 = 10;pst->f2 = 20;pst->f3 = 30;pst->f4 = 40;pst->f5 = 50.0;}

對應彙編為:

push    ebpmov     ebp, espsub     esp, 8push    24              ; unsigned intcall    ??2@YAPAXI@Z    ; operator new(uint)add     esp, 4mov     [ebp+var_8], eax ; eax為pst指標,var_8也是mov     eax, [ebp+var_8]mov     [ebp+var_4], eaxmov     ecx, [ebp+var_4] ; ecx = pstmov     dword ptr [ecx], 10 ; (int*)pst = 10mov     edx, 20mov     eax, [ebp+var_4] ; eax = pstmov     [eax+4], dx     ; (word*)(pst+4) = 20mov     ecx, [ebp+var_4] ; ecx = pstmov     byte ptr [ecx+6], 30 ; (byte*)(pst+6)=30,驗證了第二個是word位元組mov     edx, [ebp+var_4] ; edx = pstmov     dword ptr [edx+8], 40 ; (int*)(pst+8) = 40mov     eax, [ebp+var_4] ; eax = pstfld     ds:dbl_4020F0fstp    qword ptr [eax+10h] ; (dq)(pst+16)=50.0mov     esp, ebppop     ebpretn
4. 結構體數組
void fun(){stTest* pst = new stTest[2];pst[1].f1 = 10;pst[1].f2 = 20;pst[1].f3 = 30;pst[1].f4 = 40;pst[1].f5 = 50.0;}

反組譯碼沒什麼區別:與上面相比,每項加了24

push    ebpmov     ebp, espsub     esp, 8push    48              ; 48/2 = 24call    ??2@YAPAXI@Z    ; operator new(uint)add     esp, 4mov     [ebp+var_8], eaxmov     eax, [ebp+var_8]mov     [ebp+var_4], eaxmov     ecx, [ebp+var_4]mov     dword ptr [ecx+24], 10 ; pst+24,注意+24mov     edx, 20mov     eax, [ebp+var_4]mov     [eax+1Ch], dxmov     ecx, [ebp+var_4]mov     byte ptr [ecx+30], 30 ; pst+24+6mov     edx, [ebp+var_4]mov     dword ptr [edx+32], 40 ; pst+24+8mov     eax, [ebp+var_4]fld     ds:dbl_4020F0fstp    qword ptr [eax+40] ; pst+14+16mov     esp, ebppop     ebpretn

建立結構體

IDA之所以在分析階段無法識別結構體,可能源於兩個原因。首先,雖然IDA瞭解某個結構體的布局,但它並沒有足夠的資訊,能夠判斷程式確實使用了結構體。其次,程式中的結構體可能是一種IDA對其一無所知的非標準結構體。在這兩種情況下,問題都可以得到解決,且首先從Structures視窗下手 1.添加結構體

Structures視窗的前4行文本用於提醒使用者該視窗中可能進行的操作。

使用熱鍵INSERT啟動

指定結構體的名稱並單擊OK按鈕後,IDA將在Structures視窗中建立一個空結構體定義

2.編輯結構體成員

(1)要給結構體添加新欄位,將游標放在結構體定義的最後一行(包含ends的那一行)並按下D鍵。新欄位的大小取決於你在資料轉盤上選擇的第一個大小

(2)如果需要修改欄位的大小,首先將游標放在新欄位的名稱上,然後重複按下D鍵,使資料轉盤上的資料類型開始迴圈,從而為新欄位選擇正確的資料大小。另外,你還可以使用OptionsSetup Data Types來指定一個在資料轉盤上不存在的大小。如果新欄位是一個數組,右擊其名稱並在操作功能表中選擇Array

(3)要更改一個結構體欄位的名稱,單擊欄位名稱並按下N鍵,或者右擊該名稱並在操作功能表中選擇ReName,然後在輸入框中輸入一個名稱即可。

 

以下是協助說明:

 •一個欄位的位元組位移量在Structures視窗的左側顯示。

 

 •結構體的新大小會在結構體定義的第一行及時更新出來。

 

  •可以給一個結構體欄位添加註釋,就像給任何反組譯碼行添加註釋一樣。 

 

  •只有當一個欄位是結構體中的最後一個欄位時,使用U鍵才能刪除該欄位。對於其他欄位,按下U鍵只是變成undefined,可以通過D鍵恢複

 

 •IDA並不區分已壓縮和未壓縮的結構體。為將欄位適當對齊,如果你需要填補位元組,那麼你必須負責添加這些位元組。填補位元組最好作為適當大小的啞欄位添加。在添加額外的欄位後,你可以選擇取消或保留這些欄位的定義。

 

•分配到結構體中間的位元組只有在取消關聯欄位的定義後(undefined狀態),EditShrink Struct Type(縮小結構體類型)即可刪除被取消定義的位元組。

 

 •也可以在結構體的中間添加新的位元組:選擇新位元組後面的一個欄位,然後使用EditExpand Struct Type(擴大結構體類型)在選中的欄位前插入一定數量的位元組。

•如果知道結構體的大小,而不瞭解它的布局,你需要建立兩個欄位。第一個欄位為一個數組,它的大小為結構體的大小減去1個位元組(size-1);第二個欄位應為1個位元組。建立第二個欄位後,取消第一個(數組)欄位的定義。這樣,結構體的大小被保留下來,隨後,當你進一步瞭解該結構體的布局後,你可以回過頭來定義它的欄位及其大小。

建立完成後如下:

  3.摺疊開啟結構體

可以選擇結構體中的任何欄位並按下數字鍵台中的減號鍵,將結構體的定義摺疊成一行摘要,雙擊結構體名稱也可以開啟該定義。(或ctrl+/ctrl-)

  使用結構體模板

1.右擊中,可在操作功能表上看到Structure offset選項:很明確的說明了它可以是stTest.f5+24(pst[1].f5 = 50.0;)

2.另一種方法,可以將棧和全域變數格式化成整個結構體,雙擊該變數,開啟詳細棧幀視圖,然後使用EditStruct Var(ALT+Q)命令顯示一組已知的結構體

之後上面結構體數組彙編變為:重新格式化之後,IDA認識到,任何對分配給var_8的24個位元組塊的記憶體引用,都必須引用該結構體中的一個欄位。如果IDA發現這樣一個引用,它會盡一切努力,將這個記憶體引用與結構體變數中的一個已定義的欄位關聯起來

var_8= stTest ptr -8push    ebpmov     ebp, espsub     esp, 8push    48              ; unsigned intcall    ??2@YAPAXI@Z    ; operator new(uint)add     esp, 4mov     [ebp+var_8.f1], eaxmov     eax, [ebp+var_8.f1]mov     dword ptr [ebp+var_8.f2], eaxmov     ecx, dword ptr [ebp+var_8.f2]mov     dword ptr [ecx+(size stTest)], 10mov     edx, 20mov     eax, dword ptr [ebp+var_8.f2]mov     [eax+1Ch], dxmov     ecx, dword ptr [ebp+var_8.f2]mov     byte ptr [ecx+30], 30mov     edx, dword ptr [ebp+var_8.f2]mov     [edx+(stTest.f4+18h)], 40mov     eax, dword ptr [ebp+var_8.f2]fld     ds:dbl_4020F0fstp    [eax+(stTest.f5+18h)]mov     esp, ebppop     ebpretn

將全域變數格式化成結構體的過程與格式化棧變數所使用的過程幾乎完全相同 匯入新的結構體

在建立新結構體方面,IDA確實提供了一些捷徑。IDA能夠解析C(而非C++)資料聲明,以及整個C標頭檔,並自動為在這些聲明或標頭檔中定義的結構體建立對應的IDA結構體。如果你碰巧擁有你正進行逆向工程的二進位檔案的原始碼,或者至少是標頭檔,那麼,你就可以讓IDA直接從原始碼中提取出相關結構體,從而節省大量時間。

使用ViewOpen SubviewsLocal Types(查看開啟子視窗本地類型)命令,可以開啟Local Types子視窗,其中列出了所有解析到當前資料庫中的類型。

通過INSERT鍵或操作功能表中的Insert選項解析:

 ------------->

   解析C標頭檔

要解析標頭檔,可以使用FileLoad FileParse C HeaderFile(檔案負載檔案解析C標頭檔)選擇你想要解析的標頭檔。如果一切正常,IDA會通知你Compilation successful(編譯完成)。如果解析器遇到任何問題,IDA將會在輸出視窗中顯示錯誤訊息

IDA會將所有被成功解析的結構體添加到當前資料庫的標準結構體列表中(具體地說,是列表的末尾)。如果新結構體的名稱與現有結構體的名稱相同,IDA會用新結構體布局覆蓋原有結構體定義。除非你明確選擇添加新的結構體,否則,新結構體不會出現在Structures視窗中

•預設情況下,解析器會建立4位元組對齊的結構體,當然,你可以使用pack改變它

 •解析器只能理解C標準資料類型。但是,解析器還能夠理解前置處理器define指令和C typedef語句。因此,如果解析器之前遇到過適當的typedef,它將能夠正確解析unit32_t之類的類型。

 •如果你沒有原始碼,那麼你會發現,使用文字編輯器以C標記法迅速定義一個結構體布局,並解析得到的標頭檔或把聲明粘貼為一個新的本地類型,會比使用IDA煩瑣的手動結構體定義工具更加方便。

添加到Local Types(本地類型)視窗中的資料類型不會立即在Structures(結構體)最簡單的方法是在類型上單擊滑鼠右鍵,並選擇Synchronize to idb。 使用標準結構體

IDA能夠識別大量與各種庫和API函數有關的資料結構。當IDA在反組譯碼代碼清單中操縱結構體時,它會在Structures視窗中添加相應的結構體定義。因此,Structures視窗中顯示的是應用於當前二進位檔案的已知結構體的子集。除了建立自訂結構體外,你還可以從IDA的已知結構體列表中提取出其他標準結構體,並將其添加到Structures視窗中。

首先,在Structures視窗中按下IN-SERT鍵。在Create structure/ union對話方塊中,包含一個Add standard structure(添加標準結構體)按鈕。單擊這個按鈕,IDA將顯示與當前編譯器(在分析階段檢測出來)和檔案格式有關的結構體主列表。這個結構體主列表中還包含通過解析C標頭檔添加到資料庫中的結構體。

樣本:(分析檔案頭)

預設情況下,在建立後,檔案頭不會立即載入到資料庫中。但是,如果你在最初建立資料庫時選擇Manual load(手動載入)選項,就可以將檔案頭載入到資料庫中。負載檔案頭可確保只有與這些頭部有關的資料類型才出現在資料庫中。多數情況下,檔案頭不會以任何形式被格式化,因為通常程式並不會直接引用它們自己的檔案頭。因此,分析器也沒有必要對檔案頭應用結構體模板。

 

對一個PE二進位檔案進行一番研究後,你會發現,PE檔案的開頭部分是一個名為IMAGE_DOS_HEADER的MS-DOS頭部結構體。另外,IMAGE_DOS_HEADER中的資料指向一個IM-AGE_NE_HEADER結構體的位置。

第一步是添加標準的IMAGE_DOS_HEADER結構體(你可以在開啟IMAGE_NT_HEADER結構體的同時添加該結構體)。

第二步是使用EditStruct Var(ALT+Q),將從_Image-Base開始的位元組轉換成一個IMAGE_DOS_HEADER結構體

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.