7.IDA-创建结构体
結(jié)構(gòu)體分類
結(jié)構(gòu)體的一個顯著特點在于,結(jié)構(gòu)體中的數(shù)據(jù)字段是通過名稱訪問,而不是像數(shù)組那樣通過索引訪問。不好的是,字段名稱被編譯器轉(zhuǎn)換成了數(shù)字偏移量。結(jié)果,在反匯編代碼清單中,訪問結(jié)構(gòu)體字段的方式看起來與使用常量索引訪問數(shù)組元素的方式極其相似。
注意的是,結(jié)構(gòu)體中有個內(nèi)存對齊規(guī)則,所以不要認為編譯器會利用所需的最小空間來分配結(jié)構(gòu)體。默認情況下,編譯器會設(shè)法將結(jié)構(gòu)體字段與內(nèi)存地址對齊,以最有效地讀取和寫入這些字段
1. 全局分配的結(jié)構(gòu)體
和全局分配的數(shù)組一樣,編譯器在編譯時可獲知全局分配的結(jié)構(gòu)體的地址。這使得編譯器能夠在編譯時計算出結(jié)構(gòu)體中每個成員的地址,而不必在運行時進行任何計算。
如下代碼:
[cpp]?view plaincopy對應(yīng)匯編為:可以看到,在這個反匯編代碼清單中,訪問結(jié)構(gòu)體成員不需要任何算術(shù)計算,如果沒有源代碼,你根本無法斷定這個程序使用了結(jié)構(gòu)體。
[cpp]?view plaincopy
2. 棧分配的結(jié)構(gòu)體
同樣,僅僅根據(jù)棧布局,同樣很難識別出棧分配的結(jié)構(gòu)體。
[cpp]?view plaincopy對應(yīng)匯編為:同樣,訪問結(jié)構(gòu)體中的字段不需要進行任何算術(shù)計算,因為在編譯時,編譯器能夠確定棧幀內(nèi)每個字段的相對偏移量
[cpp]?view plaincopy3. 堆分配的結(jié)構(gòu)體
由于結(jié)構(gòu)體的地址在編譯時未知,編譯器別無選擇,只有生成代碼來計算每個字段在結(jié)構(gòu)體中的正確偏移量。
如果一個結(jié)構(gòu)體在堆中分配,那么對編譯器來說,引用該結(jié)構(gòu)體的唯一線索就是指向該結(jié)構(gòu)體起始地址的指針。
[cpp]?view plaincopy對應(yīng)匯編為:
[cpp]?view plaincopy4. 結(jié)構(gòu)體數(shù)組
[cpp]?view plaincopy反匯編沒什么區(qū)別:與上面相比,每項加了24
[cpp]?view plaincopy
創(chuàng)建結(jié)構(gòu)體
IDA之所以在分析階段無法識別結(jié)構(gòu)體,可能源于兩個原因。首先,雖然IDA了解某個結(jié)構(gòu)體的布局,但它并沒有足夠的信息,能夠判斷程序確實使用了結(jié)構(gòu)體。其次,程序中的結(jié)構(gòu)體可能是一種IDA對其一無所知的非標準結(jié)構(gòu)體。在這兩種情況下,問題都可以得到解決,且首先從Structures窗口下手
1.添加結(jié)構(gòu)體
Structures窗口的前4行文本用于提醒用戶該窗口中可能進行的操作。
使用熱鍵INSERT啟動
指定結(jié)構(gòu)體的名稱并單擊OK按鈕后,IDA將在Structures窗口中創(chuàng)建一個空結(jié)構(gòu)體定義
2.編輯結(jié)構(gòu)體成員
(1)要給結(jié)構(gòu)體添加新字段,將光標放在結(jié)構(gòu)體定義的最后一行(包含ends的那一行)并按下D鍵。新字段的大小取決于你在數(shù)據(jù)轉(zhuǎn)盤上選擇的第一個大小
(2)如果需要修改字段的大小,首先將光標放在新字段的名稱上,然后重復按下D鍵,使數(shù)據(jù)轉(zhuǎn)盤上的數(shù)據(jù)類型開始循環(huán),從而為新字段選擇正確的數(shù)據(jù)大小。另外,你還可以使用Options?Setup Data Types來指定一個在數(shù)據(jù)轉(zhuǎn)盤上不存在的大小。如果新字段是一個數(shù)組,右擊其名稱并在上下文菜單中選擇Array
(3)要更改一個結(jié)構(gòu)體字段的名稱,單擊字段名稱并按下N鍵,或者右擊該名稱并在上下文菜單中選擇ReName,然后在輸入框中輸入一個名稱即可。
?
以下是幫助說明:
??一個字段的字節(jié)偏移量在Structures窗口的左側(cè)顯示。
?
??結(jié)構(gòu)體的新大小會在結(jié)構(gòu)體定義的第一行及時更新出來。
?
? ?可以給一個結(jié)構(gòu)體字段添加注釋,就像給任何反匯編行添加注釋一樣。?
?
? ?只有當一個字段是結(jié)構(gòu)體中的最后一個字段時,使用U鍵才能刪除該字段。對于其他字段,按下U鍵只是變成undefined,可以通過D鍵恢復
?
??IDA并不區(qū)分已壓縮和未壓縮的結(jié)構(gòu)體。為將字段適當對齊,如果你需要填補字節(jié),那么你必須負責添加這些字節(jié)。填補字節(jié)最好作為適當大小的啞字段添加。在添加額外的字段后,你可以選擇取消或保留這些字段的定義。
?
?分配到結(jié)構(gòu)體中間的字節(jié)只有在取消關(guān)聯(lián)字段的定義后(undefined狀態(tài)),Edit?Shrink Struct Type(縮小結(jié)構(gòu)體類型)即可刪除被取消定義的字節(jié)。
?
??也可以在結(jié)構(gòu)體的中間添加新的字節(jié):選擇新字節(jié)后面的一個字段,然后使用Edit?Expand Struct Type(擴大結(jié)構(gòu)體類型)在選中的字段前插入一定數(shù)量的字節(jié)。
?如果知道結(jié)構(gòu)體的大小,而不了解它的布局,你需要創(chuàng)建兩個字段。第一個字段為一個數(shù)組,它的大小為結(jié)構(gòu)體的大小減去1個字節(jié)(size-1);第二個字段應(yīng)為1個字節(jié)。創(chuàng)建第二個字段后,取消第一個(數(shù)組)字段的定義。這樣,結(jié)構(gòu)體的大小被保留下來,隨后,當你進一步了解該結(jié)構(gòu)體的布局后,你可以回過頭來定義它的字段及其大小。
創(chuàng)建完成后如下:
?
3.折疊打開結(jié)構(gòu)體
可以選擇結(jié)構(gòu)體中的任何字段并按下數(shù)字鍵盤中的減號鍵,將結(jié)構(gòu)體的定義折疊成一行摘要,雙擊結(jié)構(gòu)體名稱也可以打開該定義。(或ctrl+/ctrl-)
?
使用結(jié)構(gòu)體模板
1.右擊中,可在上下文菜單上看到Structure offset選項:很明確的說明了它可以是stTest.f5+24(pst[1].f5 = 50.0;)
2.另一種方法,可以將棧和全局變量格式化成整個結(jié)構(gòu)體,雙擊該變量,打開詳細棧幀視圖,然后使用Edit?Struct Var(ALT+Q)命令顯示一組已知的結(jié)構(gòu)體
之后上面結(jié)構(gòu)體數(shù)組匯編變?yōu)?#xff1a;重新格式化之后,IDA認識到,任何對分配給var_8的24個字節(jié)塊的內(nèi)存引用,都必須引用該結(jié)構(gòu)體中的一個字段。如果IDA發(fā)現(xiàn)這樣一個引用,它會盡一切努力,將這個內(nèi)存引用與結(jié)構(gòu)體變量中的一個已定義的字段關(guān)聯(lián)起來
[cpp]?view plaincopy將全局變量格式化成結(jié)構(gòu)體的過程與格式化棧變量所使用的過程幾乎完全相同
導入新的結(jié)構(gòu)體
在創(chuàng)建新結(jié)構(gòu)體方面,IDA確實提供了一些捷徑。IDA能夠解析C(而非C++)數(shù)據(jù)聲明,以及整個C頭文件,并自動為在這些聲明或頭文件中定義的結(jié)構(gòu)體創(chuàng)建對應(yīng)的IDA結(jié)構(gòu)體。如果你碰巧擁有你正進行逆向工程的二進制文件的源代碼,或者至少是頭文件,那么,你就可以讓IDA直接從源代碼中提取出相關(guān)結(jié)構(gòu)體,從而節(jié)省大量時間。
使用View?Open Subviews?Local Types(查看?打開子窗口?本地類型)命令,可以打開Local Types子窗口,其中列出了所有解析到當前數(shù)據(jù)庫中的類型。
通過INSERT鍵或上下文菜單中的Insert選項解析:
?------------->
?
?解析C頭文件
要解析頭文件,可以使用File?Load File?Parse C HeaderFile(文件?加載文件?解析C頭文件)選擇你想要解析的頭文件。如果一切正常,IDA會通知你Compilation successful(編譯完成)。如果解析器遇到任何問題,IDA將會在輸出窗口中顯示錯誤消息
IDA會將所有被成功解析的結(jié)構(gòu)體添加到當前數(shù)據(jù)庫的標準結(jié)構(gòu)體列表中(具體地說,是列表的末尾)。如果新結(jié)構(gòu)體的名稱與現(xiàn)有結(jié)構(gòu)體的名稱相同,IDA會用新結(jié)構(gòu)體布局覆蓋原有結(jié)構(gòu)體定義。除非你明確選擇添加新的結(jié)構(gòu)體,否則,新結(jié)構(gòu)體不會出現(xiàn)在Structures窗口中
?默認情況下,解析器會建立4字節(jié)對齊的結(jié)構(gòu)體,當然,你可以使用pack改變它
??解析器只能理解C標準數(shù)據(jù)類型。但是,解析器還能夠理解預處理器define指令和C typedef語句。因此,如果解析器之前遇到過適當?shù)膖ypedef,它將能夠正確解析unit32_t之類的類型。
??如果你沒有源代碼,那么你會發(fā)現(xiàn),使用文本編輯器以C表示法迅速定義一個結(jié)構(gòu)體布局,并解析得到的頭文件或把聲明粘貼為一個新的本地類型,會比使用IDA煩瑣的手動結(jié)構(gòu)體定義工具更加方便。
添加到Local Types(本地類型)窗口中的數(shù)據(jù)類型不會立即在Structures(結(jié)構(gòu)體)最簡單的方法是在類型上單擊鼠標右鍵,并選擇Synchronize to idb。
使用標準結(jié)構(gòu)體
IDA能夠識別大量與各種庫和API函數(shù)有關(guān)的數(shù)據(jù)結(jié)構(gòu)。當IDA在反匯編代碼清單中操縱結(jié)構(gòu)體時,它會在Structures窗口中添加相應(yīng)的結(jié)構(gòu)體定義。因此,Structures窗口中顯示的是應(yīng)用于當前二進制文件的已知結(jié)構(gòu)體的子集。除了創(chuàng)建自定義結(jié)構(gòu)體外,你還可以從IDA的已知結(jié)構(gòu)體列表中提取出其他標準結(jié)構(gòu)體,并將其添加到Structures窗口中。
首先,在Structures窗口中按下IN-SERT鍵。在Create structure/ union對話框中,包含一個Add standard structure(添加標準結(jié)構(gòu)體)按鈕。單擊這個按鈕,IDA將顯示與當前編譯器(在分析階段檢測出來)和文件格式有關(guān)的結(jié)構(gòu)體主列表。這個結(jié)構(gòu)體主列表中還包含通過解析C頭文件添加到數(shù)據(jù)庫中的結(jié)構(gòu)體。
示例:(分析文件頭)
默認情況下,在創(chuàng)建后,文件頭不會立即加載到數(shù)據(jù)庫中。但是,如果你在最初創(chuàng)建數(shù)據(jù)庫時選擇Manual load(手動加載)選項,就可以將文件頭加載到數(shù)據(jù)庫中。加載文件頭可確保只有與這些頭部有關(guān)的數(shù)據(jù)類型才出現(xiàn)在數(shù)據(jù)庫中。多數(shù)情況下,文件頭不會以任何形式被格式化,因為通常程序并不會直接引用它們自己的文件頭。因此,分析器也沒有必要對文件頭應(yīng)用結(jié)構(gòu)體模板。
?
對一個PE二進制文件進行一番研究后,你會發(fā)現(xiàn),PE文件的開頭部分是一個名為IMAGE_DOS_HEADER的MS-DOS頭部結(jié)構(gòu)體。另外,IMAGE_DOS_HEADER中的數(shù)據(jù)指向一個IM-AGE_NE_HEADER結(jié)構(gòu)體的位置。
第一步是添加標準的IMAGE_DOS_HEADER結(jié)構(gòu)體(你可以在打開IMAGE_NT_HEADER結(jié)構(gòu)體的同時添加該結(jié)構(gòu)體)。
第二步是使用Edit?Struct Var(ALT+Q),將從_Image-Base開始的字節(jié)轉(zhuǎn)換成一個IMAGE_DOS_HEADER結(jié)構(gòu)體
?
總結(jié)
以上是生活随笔為你收集整理的7.IDA-创建结构体的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5.IDA-文本搜索、二进制搜索(16进
- 下一篇: 8.IDA-数据与代码、函数互相转换