站在巨人的肩膀上——IDA PRO權(quán)威指南閱讀筆記
一漾峡,窗口
view->open subviews ?打開(kāi)/關(guān)閉各種窗口
IDA文本視圖(反匯編視圖)
地址顯示格式 ?區(qū)域名稱(chēng):虛擬地址
箭頭顯示格式 ? ?虛線(xiàn)條件跳轉(zhuǎn)终惑,實(shí)現(xiàn)非條件跳轉(zhuǎn) ?若加粗則
arg_X ?函數(shù)棧幀
;CODE XREF:_XX ? 交叉引用
Names窗口
D:數(shù)據(jù)
A:字符串
F:常規(guī)函數(shù)恒削,非庫(kù)函數(shù)
I:共享庫(kù)導(dǎo)入的函數(shù)名稱(chēng)
L:庫(kù)函數(shù)
未知名稱(chēng):
sub_XXXXX ? 地址XXXX的子例程
loc_XXXXX? 地址XXXX的一個(gè)指令
byte/word/dword_XXXXX? 地址XXXX的8/16/32位數(shù)據(jù)
unk_XXXXX? 地址XXXX的未知數(shù)據(jù)
底部消息窗口
IDA開(kāi)發(fā)腳本插件的輸出窗口
Strings窗口
從二進(jìn)制文件中提取的字符串
右鍵->setup可配置
導(dǎo)出/入窗口
導(dǎo)出窗口列出文件的入口點(diǎn)
導(dǎo)入窗口列出由被分析的二進(jìn)制文件導(dǎo)入的所有函數(shù)
函數(shù)窗口
函數(shù)名稱(chēng),區(qū)域径密,起始位置,長(zhǎng)度坤按,描述函數(shù)的標(biāo)記
<返回調(diào)用方(R),使用ebp(B)寄存器引用局部變量>
結(jié)構(gòu)體窗口
分析數(shù)據(jù)結(jié)構(gòu)弃衍,雙擊數(shù)據(jù)結(jié)構(gòu)名稱(chēng)展開(kāi)惊豺,查看詳細(xì)布局
枚舉窗口 enums
可列舉燎孟,定義枚舉類(lèi)型
段窗口 segmentation
段的簡(jiǎn)單列表
簽名窗口
右鍵->應(yīng)用新簽名
類(lèi)型庫(kù)窗口,函數(shù)調(diào)用窗口(先選中函數(shù))尸昧,問(wèn)題窗口等
二揩页,反匯編導(dǎo)航
雙擊導(dǎo)航
最基本的為雙擊導(dǎo)航,任意可雙擊的地址均能跳轉(zhuǎn)到目標(biāo)
跳轉(zhuǎn)
快捷鍵 G 或Jump->Jump to Address跳轉(zhuǎn)到名稱(chēng)或十六進(jìn)制地址
導(dǎo)航欄前進(jìn) 后退按鈕實(shí)現(xiàn)跳轉(zhuǎn)
棧幀
棧幀
棧幀也叫過(guò)程活動(dòng)記錄烹俗,是編譯器用來(lái)實(shí)現(xiàn)過(guò)程/函數(shù)調(diào)用的一種數(shù)據(jù)結(jié)構(gòu)爆侣。
棧幀就是一個(gè)函數(shù)執(zhí)行的環(huán)境:函數(shù)參數(shù)、函數(shù)的局部變量幢妄、函數(shù)執(zhí)行完后返回到哪里等等兔仰。
實(shí)現(xiàn)上有硬件方式和軟件方式(有些體系不支持硬件棧)
首先應(yīng)該明白,棧是從高地址向低地址延伸的蕉鸳。每個(gè)函數(shù)的每次調(diào)用乎赴,都有它自己獨(dú)立的一個(gè)棧幀,這個(gè)棧幀中維持著所需要的各種信息潮尝。寄存器ebp指向當(dāng)前的棧幀的底部(高地址)榕吼,寄存器esp指向當(dāng)前的棧幀的頂部(低地址)。
注意:EBP指向當(dāng)前位于系統(tǒng)棧最上邊一個(gè)棧幀的底部勉失,而不是系統(tǒng)棧的底部羹蚣。嚴(yán)格說(shuō)來(lái),“棧幀底部”和“棧底”是不同的概念;ESP所指的棧幀頂部和系統(tǒng)棧的頂部是同一個(gè)位置戴质。
棧幀是程序運(yùn)行時(shí)棧中分配的內(nèi)存塊度宦,用于特定函數(shù)的調(diào)用
調(diào)用函數(shù):
1.調(diào)用方將被調(diào)用函數(shù)所需的參數(shù)放入調(diào)用約定指定的位置踢匣。
2.調(diào)用方將控制權(quán)轉(zhuǎn)交給被調(diào)用函數(shù)告匠,即x86 CALL 與 MIPS JAL等指令執(zhí)行戈抄。返回地址被保存到程序棧或CPU寄存器中后专。
3.有時(shí)划鸽,被調(diào)用的函數(shù)會(huì)配置一個(gè)棧指針,并保存調(diào)用方希望保持不變的寄存器的值戚哎。
4.被調(diào)用的函數(shù)為它可能需要的任何局部變量分配空間裸诽,一般通過(guò)調(diào)整程序棧指針在運(yùn)行時(shí)棧上保留空間來(lái)完成這一任務(wù)。
5.被調(diào)用的函數(shù)可能訪(fǎng)問(wèn)調(diào)用函數(shù)傳遞給它的參數(shù)型凳,可能生成一個(gè)結(jié)果丈冬。如果返回一個(gè)結(jié)果,此結(jié)果通常放置在一個(gè)特定的寄存器中甘畅,或放置到函數(shù)返回后調(diào)用方可立即訪(fǎng)問(wèn)的寄存器中埂蕊。
6.函數(shù)完成操作后,任何為函數(shù)保留的検柰伲空間將被釋放蓄氧。通常,逆向執(zhí)行4步驟即可實(shí)現(xiàn)槐脏。
7.如果某個(gè)寄存器的值還為調(diào)用方保存(即3中情況)喉童,那么恢復(fù)原始值。這包括恢復(fù)調(diào)用方的棧指針寄存器顿天。
8.被調(diào)用函數(shù)將控制權(quán)返還給調(diào)用方堂氯。主要指令x86 RET 和 MIPS JR 。根據(jù)調(diào)用約定牌废,還可能會(huì)從程序棧中清除一個(gè)或多個(gè)參數(shù)祖灰。
9.調(diào)用方一旦重新獲得控制權(quán),它可能需要?jiǎng)h除程序棧中的參數(shù)畔规。這是可能需要對(duì)棧進(jìn)行調(diào)整局扶,將程序棧指針恢復(fù)到1步驟之前的值。
3叁扫,4在進(jìn)入函數(shù)時(shí)執(zhí)行三妈,稱(chēng)為序言;6~8一般在函數(shù)結(jié)束是執(zhí)行莫绣,稱(chēng)為尾聲畴蒲;5為函數(shù)主體,表示調(diào)用函數(shù)執(zhí)行的全部操作对室。
調(diào)用約定(常用)
cdecl調(diào)用約定:
調(diào)用方按從右到左的順序?qū)⒑瘮?shù)參數(shù)放入棧中模燥,在被調(diào)用的函數(shù)完成其操作時(shí)咖祭,調(diào)用方負(fù)責(zé)從棧中清除參數(shù)。
stdcall調(diào)用約定:
按從右到左的順序?qū)⒑瘮?shù)參數(shù)放入棧中蔫骂,函數(shù)結(jié)束時(shí)么翰,被調(diào)用方負(fù)責(zé)刪除棧中的參數(shù)。
fastcall調(diào)用約定:
傳遞給函數(shù)的前兩個(gè)參數(shù)分別位于exc,edx(非程序棧)辽旋,剩余其他參數(shù)類(lèi)似stdcall浩嫌,由于有兩個(gè)參數(shù)被傳遞到寄存器中(傳參2以上),被調(diào)用函數(shù)只需從棧中清除8字節(jié)补胚。
c++調(diào)用約定:
this指針指向用于調(diào)用函數(shù)的對(duì)象码耐,c++并未規(guī)定如何向非靜態(tài)成員函數(shù)傳遞this指針,不同編譯器有不同解釋溶其。
棧幀示例:
棧指針骚腥,就是棧頂指針
“序言”部分:
push ebp ? ? ? ? ? ? ? ;儲(chǔ)存調(diào)用者ebp值
mov ebp,esp ? ? ? ? ;使用ebp儲(chǔ)存esp的值
sub esp,76 ? ? ? ? ? ?;給本地變量分配空間
如果希望ebp作為棧指針,修改它之前必須保存ebp的當(dāng)前值瓶逃,并且在返回調(diào)用方時(shí)恢復(fù)ebp(保存其他寄存器如esi或edi也是這樣束铭,或推遲操作),即棧幀中沒(méi)有用于存儲(chǔ)被保存寄存器的標(biāo)準(zhǔn)位置金闽。ebp被保存后纯露,就可以對(duì)其修改,使它指向當(dāng)前的棧位置代芜。
“尾聲”部分:
add ? esp,76 ? ? ? ?;調(diào)整esp到初始
ret ? ? ? ? ? ? ? ? ? ? ? ?;返回
使用一個(gè)專(zhuān)用的棧幀埠褪,所有變量相對(duì)于棧指針寄存器的偏移量都可計(jì)算。許多時(shí)候挤庇,正偏移量用于訪(fǎng)問(wèn)函數(shù)參數(shù)钞速,負(fù)偏移量用于訪(fǎng)問(wèn)局部變量。
調(diào)用:
push sword [ebp-72] ? ? ;push y
push sword [ebp-76]? ? ;push z
call bar?
add esp,8 ? ? ? ? ? ? ? ? ? ? ? ?;cdecl要求調(diào)用者清除參數(shù)
“尾聲”
mov esp,ebp ? ? ? ? ? ;利用重置esp清除局部變量
pop ? ebp ? ? ? ? ? ? ? ? ;還原調(diào)用者ebp值
ret ? ? ? ? ? ? ? ? ? ? ? ? ? ?;彈出返回地址返回給調(diào)用者
由于這項(xiàng)操作十分常見(jiàn)嫡秕,x86體系提供leave指令
leave ? ? ? ? ? ? ? ? ? ;同上兩行
ret ? ? ? ? ? ? ? ? ? ? ?;同上
IDA棧視圖:
從開(kāi)始渴语,IDA提供了一個(gè)摘要棧視圖
局部變量以var_為前綴,跟一個(gè)表示變量與被保存的棧指針之間的距離(以字節(jié)為單位)的十六進(jìn)制后綴
函數(shù)參數(shù)(傳入?yún)?shù))名以arg_為前綴昆咽,后面跟表示其與最頂端的參數(shù)之間的相對(duì)距離的十六進(jìn)制后綴驾凶,因此最頂端4字節(jié)參數(shù)名為arg_0,而隨后為arg_4等
IDA只會(huì)為在函數(shù)中直接引用的棧變量自動(dòng)生成名稱(chēng)
雙擊任一變量掷酗,即可進(jìn)入詳細(xì)視圖
標(biāo)記:dd一個(gè)儲(chǔ)存字節(jié) dw兩個(gè)儲(chǔ)存字節(jié)(也叫字) dd四個(gè)儲(chǔ)存字節(jié)调违,雙字
數(shù)據(jù)庫(kù)搜索:
search->text ? 啟動(dòng)文本搜索
search->sequence of bytes ?搜索字節(jié)序列 : ?搜索十六進(jìn)制序列,應(yīng)將搜索字符串指定為以空格分隔的兩位十六進(jìn)制值組成的列表泻轰;要搜索內(nèi)嵌字符串?dāng)?shù)據(jù)技肩,必須將搜索字符串用引號(hào)括起來(lái)
三,反匯編操作
暫時(shí)不用
四浮声,數(shù)據(jù)類(lèi)型與數(shù)據(jù)結(jié)構(gòu)
數(shù)組成員訪(fǎng)問(wèn):
edit->array ?格式化數(shù)組操作 ? ? ?可使數(shù)組看上去更明顯(按地址順序mov虚婿,全局?jǐn)?shù)組可計(jì)算出地址)
棧分配數(shù)組 ? 沒(méi)有絕對(duì)地址旋奢,編譯器無(wú)法計(jì)算地址,處理方法類(lèi)似全局?jǐn)?shù)組?
堆分配數(shù)組 ?使用動(dòng)態(tài)內(nèi)存分配函數(shù)然痊,所以必須根據(jù)內(nèi)存分配函數(shù)返回的地址值至朗,生成對(duì)數(shù)組的所有引用。如果能確定數(shù)組的總大小和每個(gè)元素的大小玷过,可以計(jì)算該數(shù)組包含的元素?cái)?shù)量爽丹。
只有當(dāng)變量用作數(shù)組的索引時(shí)筑煮,才容易確定數(shù)組的存在辛蚊;要訪(fǎng)問(wèn)數(shù)組中的元素,首先要用索引乘以數(shù)組元素的大小真仲,計(jì)算出相應(yīng)偏移量袋马,與數(shù)組基址相加,得到數(shù)組元素的訪(fǎng)問(wèn)地址秸应。
(常量索引值訪(fǎng)問(wèn)數(shù)組時(shí)虑凛,很少能確定是數(shù)組)
結(jié)構(gòu)體成員訪(fǎng)問(wèn):
特點(diǎn):結(jié)構(gòu)體中數(shù)據(jù)字段通過(guò)名稱(chēng)訪(fǎng)問(wèn),字段名稱(chēng)被編譯器轉(zhuǎn)換成數(shù)字偏移量
默認(rèn)情況软啼,編譯器會(huì)設(shè)法將結(jié)構(gòu)體字段與內(nèi)存地址對(duì)其桑谍,最有效的讀取和寫(xiě)入這些字段。
全局分配結(jié)構(gòu)體:與全局分配數(shù)組一樣祸挪,編譯器編譯時(shí)計(jì)算出結(jié)構(gòu)體中每個(gè)成員的地址(僅從匯編無(wú)法判斷使用了結(jié)構(gòu)體)
push? ? ebp
mov?????ebp,?esp
mov?????dword_403018,?10?;intf1?=?10
mov?????eax,?20
mov?????word_40301C,?ax?;?word?f2?=?20
mov?????byte_40301E,?30?;?byte?f3?=?30
mov?????dword_403020,?40?;intf4?=?40
fld?????ds:dbl_4020E0???;?f5?=?dbl_403028?=?50.0
fstp????dbl_403028
pop?????ebp
retn
棧分配結(jié)構(gòu)體:根據(jù)棧布局很難識(shí)別出棧分配結(jié)構(gòu)體锣披,編譯器能計(jì)算出起始位置與相關(guān)棧幀的幀指針ebp ? ?(其中棧指針:esp和幀指針:ebp)
000 push? ? ebp
004?mov?????ebp,?esp
004?sub?????esp,?18h
01C?mov?????[ebp+var_18],?10
01C?mov?????eax,?14h
01C?mov?????[ebp+var_14],?ax
01C?mov?????[ebp+var_12],?30
01C?mov?????[ebp+var_10],?40
01C?fld?????ds:dbl_4020E0
01C?fstp????[ebp+var_8]
01C?mov?????esp,?ebp
004?pop?????ebp
000?retn
堆分配結(jié)構(gòu)體:由于結(jié)構(gòu)體的地址在編譯時(shí)未知,編譯器別無(wú)選擇贿条,只有生成代碼來(lái)計(jì)算每個(gè)字段在結(jié)構(gòu)體中的正確偏移量雹仿。如果一個(gè)結(jié)構(gòu)體在堆中分配,那么對(duì)編譯器來(lái)說(shuō)整以,引用該結(jié)構(gòu)體的唯一線(xiàn)索就是指向該結(jié)構(gòu)體起始地址的指針胧辽。
push? ? ebp
mov?????ebp,?esp
sub?????esp,?8
push????24??????????????;?unsignedint
call??????2@YAPAXI@Z????;?operatornew(uint)
add?????esp,?4
mov?????[ebp+var_8],?eax?;?eax為pst指針,var_8也是
mov?????eax,?[ebp+var_8]
mov?????[ebp+var_4],?eax
mov?????ecx,?[ebp+var_4]?;?ecx?=?pst
mov?????dword?ptr?[ecx],?10?;?(int*)pst?=?10
mov?????edx,?20
mov?????eax,?[ebp+var_4]?;?eax?=?pst
mov?????[eax+4],?dx?????;?(word*)(pst+4)?=?20
mov?????ecx,?[ebp+var_4]?;?ecx?=?pst
mov?????byte?ptr?[ecx+6],?30?;?(byte*)(pst+6)=30,驗(yàn)證了第二個(gè)是word字節(jié)
mov?????edx,?[ebp+var_4]?;?edx?=?pst
mov?????dword?ptr?[edx+8],?40?;?(int*)(pst+8)?=?40
mov?????eax,?[ebp+var_4]?;?eax?=?pst
fld?????ds:dbl_4020F0
fstp????qword?ptr?[eax+10h]?;?(dq)(pst+16)=50.0
mov?????esp,?ebp
pop?????ebp
retn
結(jié)構(gòu)體數(shù)組:找到堆請(qǐng)求的字節(jié)數(shù),數(shù)組索引數(shù)公黑,加上數(shù)組起始地址mov xxxx ptr[ecx+eax],xx邑商;可判斷數(shù)組大小和元素個(gè)數(shù)
push? ? ebp
mov?????ebp,?esp
sub?????esp,?8
push????48??????????????;?48/2?=?24
call??????2@YAPAXI@Z????;?operatornew(uint)
add?????esp,?4
mov?????[ebp+var_8],?eax
mov?????eax,?[ebp+var_8]
mov?????[ebp+var_4],?eax
mov?????ecx,?[ebp+var_4]
mov?????dword?ptr?[ecx+24],?10?;?pst+24,注意+24
mov?????edx,?20
mov?????eax,?[ebp+var_4]
mov?????[eax+1Ch],?dx
mov?????ecx,?[ebp+var_4]
mov?????byte?ptr?[ecx+30],?30?;?pst+24+6
mov?????edx,?[ebp+var_4]
mov?????dword?ptr?[edx+32],?40?;?pst+24+8
mov?????eax,?[ebp+var_4]
fld?????ds:dbl_4020F0
fstp????qword?ptr?[eax+40]?;?pst+14+16
mov?????esp,?ebp
pop?????ebp
retn
創(chuàng)建結(jié)構(gòu)體:
IDA之所以在分析階段無(wú)法識(shí)別結(jié)構(gòu)體,可能源于兩個(gè)原因凡蚜。首先人断,雖然IDA了解某個(gè)結(jié)構(gòu)體的布局,但它并沒(méi)有足夠的信息番刊,能夠判斷程序確實(shí)使用了結(jié)構(gòu)體含鳞。其次,程序中的結(jié)構(gòu)體可能是一種IDA對(duì)其一無(wú)所知的非標(biāo)準(zhǔn)結(jié)構(gòu)體芹务。在這兩種情況下蝉绷,問(wèn)題都可以得到解決鸭廷,且首先從Structures窗口下手
打開(kāi)structures窗口,edit->add struct type ? 創(chuàng)建結(jié)構(gòu)體
要給結(jié)構(gòu)體添加新字段熔吗,將光標(biāo)放在結(jié)構(gòu)體定義的最后一行(包含ends那一行)并按下D鍵
如果需要修改字段的大小辆床,首先將光標(biāo)放在新字段的名稱(chēng)上,然后重復(fù)按下D鍵桅狠,使數(shù)據(jù)轉(zhuǎn)盤(pán)上的數(shù)據(jù)類(lèi)型開(kāi)始循環(huán)讼载,從而為新字段選擇正確的數(shù)據(jù)大小。另外中跌,你還可以使用Options->Setup Data Types來(lái)指定一個(gè)在數(shù)據(jù)轉(zhuǎn)盤(pán)上不存在的大小咨堤。如果新字段是一個(gè)數(shù)組,右擊其名稱(chēng)并在上下文菜單中選擇Array漩符;要更改一個(gè)結(jié)構(gòu)體字段的名稱(chēng)一喘,可右擊該名稱(chēng)并在上下文菜單中選擇ReName
省略一些詳細(xì)幫助說(shuō)明
可以使用結(jié)構(gòu)體模版等,右擊中嗜暴,可在上下文菜單上看到Structure offset選項(xiàng)對(duì)指令操作數(shù)進(jìn)行格式化凸克;可以將棧和全局變量格式化成整個(gè)結(jié)構(gòu)體,雙擊該變量闷沥,打開(kāi)詳細(xì)棧幀視圖萎战,然后使用Edit?Struct Var顯示一組已知結(jié)構(gòu)體,重新格式化之后IDA將這個(gè)內(nèi)存引用與結(jié)構(gòu)體變量中的一個(gè)已定義的字段關(guān)聯(lián)起來(lái)
var_8= stTest ptr -8
push????ebp
mov?????ebp,?esp
sub?????esp,?8
push????48??????????????;?unsignedint
call??????2@YAPAXI@Z????;?operatornew(uint)
add?????esp,?4
mov?????[ebp+var_8.f1],?eax
mov?????eax,?[ebp+var_8.f1]
mov?????dword?ptr?[ebp+var_8.f2],?eax
mov?????ecx,?dword?ptr?[ebp+var_8.f2]
mov?????dword?ptr?[ecx+(size?stTest)],?10
mov?????edx,?20
mov?????eax,?dword?ptr?[ebp+var_8.f2]
mov?????[eax+1Ch],?dx
mov?????ecx,?dword?ptr?[ebp+var_8.f2]
mov?????byte?ptr?[ecx+30],?30
mov?????edx,?dword?ptr?[ebp+var_8.f2]
mov?????[edx+(stTest.f4+18h)],?40
mov?????eax,?dword?ptr?[ebp+var_8.f2]
fld?????ds:dbl_4020F0
fstp????[eax+(stTest.f5+18h)]
mov?????esp,?ebp
pop?????ebp
retn
將全局變量格式化成結(jié)構(gòu)體的過(guò)程與格式化棧變量所使用的過(guò)程幾乎完全相同
導(dǎo)入新的結(jié)構(gòu)體:
IDA能夠解析C(而非C++)數(shù)據(jù)聲明舆逃,以及整個(gè)C頭文件蚂维,并自動(dòng)為在這些聲明或頭文件中定義的結(jié)構(gòu)體創(chuàng)建對(duì)應(yīng)的IDA結(jié)構(gòu)體。如果你碰巧擁有你正進(jìn)行逆向工程的二進(jìn)制文件的源代碼颖侄,或者至少是頭文件鸟雏,那么,你就可以讓IDA直接從源代碼中提取出相關(guān)結(jié)構(gòu)體
解析C結(jié)構(gòu)體聲明:View?Open Subviews?Local Types览祖,通過(guò)INSERT鍵或上下文菜單中的Insert選項(xiàng)解析
解析C頭文件:可以使用File?Load File?Parse C HeaderFile選擇你想要解析的頭文件孝鹊。如果一切正常,IDA會(huì)通知你Compilation successful(編譯完成)展蒂。如果解析器遇到任何問(wèn)題又活,IDA將會(huì)在輸出窗口中顯示錯(cuò)誤消息。IDA會(huì)將所有被成功解析的結(jié)構(gòu)體添加到當(dāng)前數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)結(jié)構(gòu)體列表中(具體地說(shuō)锰悼,是列表的末尾)柳骄。如果新結(jié)構(gòu)體的名稱(chēng)與現(xiàn)有結(jié)構(gòu)體的名稱(chēng)相同,IDA會(huì)用新結(jié)構(gòu)體布局覆蓋原有結(jié)構(gòu)體定義箕般。除非你明確選擇添加新的結(jié)構(gòu)體耐薯,否則,新結(jié)構(gòu)體不會(huì)出現(xiàn)在Structures窗口中
使用標(biāo)準(zhǔn)結(jié)構(gòu)體:
除了創(chuàng)建自定義結(jié)構(gòu)體外,你還可以從IDA的已知結(jié)構(gòu)體列表中提取出其他標(biāo)準(zhǔn)結(jié)構(gòu)體曲初,并將其添加到Structures窗口中体谒。
首先,在Structures窗口中按下IN-SERT鍵臼婆。在Create structure/ union對(duì)話(huà)框中抒痒,包含一個(gè)Add standard structure(添加標(biāo)準(zhǔn)結(jié)構(gòu)體)按鈕。單擊這個(gè)按鈕颁褂,IDA將顯示與當(dāng)前編譯器(在分析階段檢測(cè)出來(lái))和文件格式有關(guān)的結(jié)構(gòu)體主列表故响。這個(gè)結(jié)構(gòu)體主列表中還包含通過(guò)解析C頭文件添加到數(shù)據(jù)庫(kù)中的結(jié)構(gòu)體。
默認(rèn)情況下颁独,在創(chuàng)建后彩届,文件頭不會(huì)立即加載到數(shù)據(jù)庫(kù)中。但是奖唯,如果你在最初創(chuàng)建數(shù)據(jù)庫(kù)時(shí)選擇Manual load(手動(dòng)加載)選項(xiàng)惨缆,就可以將文件頭加載到數(shù)據(jù)庫(kù)中糜值。加載文件頭可確保只有與這些頭部有關(guān)的數(shù)據(jù)類(lèi)型才出現(xiàn)在數(shù)據(jù)庫(kù)中丰捷。多數(shù)情況下,文件頭不會(huì)以任何形式被格式化寂汇,因?yàn)橥ǔ3绦虿⒉粫?huì)直接引用它們自己的文件頭病往。因此,分析器也沒(méi)有必要對(duì)文件頭應(yīng)用結(jié)構(gòu)體模板
til文件處理
ida中所有的數(shù)據(jù)類(lèi)型和函數(shù)原型都儲(chǔ)存在til文件中骄瓣,types窗口(View->Open subview->Type Libraries)列出了當(dāng)前加載til文件停巷,并可用于加載想要使用的til文件(types窗口中按下INSERT)
還可以共享til文件(tilib)
C++逆向工程基礎(chǔ)
所有非靜態(tài)c++成員函數(shù)都使用this指針,任何時(shí)候調(diào)用這樣一個(gè)函數(shù)榕栏,this都被初始化指向用于調(diào)用該函數(shù)的對(duì)象
vc++將this傳遞到ECX寄存器中畔勤,GUN g++則把this看成非靜態(tài)成員函數(shù)的第一個(gè)(最左邊)參數(shù),并在調(diào)用該函數(shù)前將用于調(diào)用函數(shù)對(duì)象的地址作為最后一項(xiàng)壓入棧中扒磁;也就是說(shuō)庆揪,在調(diào)用函數(shù)之前,將一個(gè)地址轉(zhuǎn)移到ECX意味著可能使用vc++編譯妨托,函數(shù)是一個(gè)成員函數(shù)缸榛,如果同一個(gè)地址被傳遞給兩個(gè)或更多函數(shù),那這些函數(shù)全都屬于同一個(gè)類(lèi)層次結(jié)構(gòu)兰伤。函數(shù)初始化前使用ECX則調(diào)用方必定已經(jīng)初始化ECX
虛函數(shù)内颗,虛表:
虛函數(shù)為了重載和多態(tài)的需要,在基類(lèi)中是由定義的敦腔,即便定義是空均澳,所以子類(lèi)中可以重寫(xiě)也可以不寫(xiě)基類(lèi)中的函數(shù)!
編譯器會(huì)為每個(gè)包含虛函數(shù)的類(lèi)(或通過(guò)繼承得到的子類(lèi))生成一個(gè)表,其中包含指向類(lèi)中每一個(gè)虛函數(shù)的指針找前,這就是虛表(vtable)筒捺,每個(gè)包含虛函數(shù)的類(lèi)都獲得另外一個(gè)數(shù)據(jù)成員(也就是虛表指針),用于在運(yùn)行時(shí)指向適當(dāng)?shù)奶摫怼?/p>
使用虛表指針的后果纸厉,操縱IDA中的類(lèi)時(shí)系吭,必須考慮虛表指針。
下面的例子動(dòng)態(tài)創(chuàng)建了SubClass 的一個(gè)對(duì)象颗品,它的地址保存在BaseClass 的一個(gè)指針中肯尺。然后,這個(gè)指針被傳遞給一個(gè)函數(shù)(ca11_vfunc ),它使用該指針來(lái)調(diào)用vfunc3躯枢。
void call_vfunc(BaseClass *b) {
? ?b->vfunc3() ;
? ?}
int main() ?{
? ?BaseClass *bc= new SubClass() ;
? ?call_vfunc(bc) ;
}
由于vfunc3 是一個(gè)虛函數(shù)则吟,因此,在這個(gè)例子中锄蹂,編譯器必須確保調(diào)用SubC1ass :: vfunc3氓仲,因?yàn)橹羔樦赶蛞粋€(gè)SubC1ass 對(duì)象。下面ca11_vfunc 的反匯編版本說(shuō)明了如何解析虛函數(shù)調(diào)用:
.text :004010A0 call_vfunc ? ? ? proc near
.text :004010A0?
.text :004010A0 b ? ? ? ? ? ? ? ? ? = dword ptr,8
.text :004010A0
.text :004010A0 ? ? ? ? ? ? ? ? ? ? ? ? ?push ? ? ? ? ?ebp
.text :004010A1 ? ? ? ? ? ? ? ? ? ? ? ? ? mov? ? ? ? ? ebp,esp
.text :004010A3 ? ? ? ? ? ? ? ? ? ? ? ? ? mov ? ? ? ? ?[ebp+b]
.text :004010A6 ? ? ? ? ? ? ? ? ? ? ? ? ? mov ? ? ? ? ?edx,[eax]
.text :004010A8 ? ? ? ? ? ? ? ? ? ? ? ? ? mov ? ? ? ecx,[ebp+b]
.text :004010AB ? ? ? ? ? ? ? ? ? ? ? ? ? mov? ? ? ? eax,[edx+8]
.text :004010AE ? ? ? ? ? ? ? ? ? ? ? ? ?call ? ? ? ? eax
.text :004010B0 ? ? ? ? ? ? ? ? ? ? ? ? ??pop ? ? ? ?ebp ?
.text :004010B1 ? ? ? ? ? ? ? ? ? ? ? ? ?retn
.text :004010B1 ?call vfunc ? ? ? endp
在A6處,虛表指針從結(jié)構(gòu)體中讀取出來(lái)得糜,保存在EDX 寄存器中敬扛。由于參數(shù)b 指向一個(gè)SubClass對(duì)象,這里也將是SubClass 的虛表的地址朝抖。在AB處啥箭,虛表被編入索引,將第三個(gè)指針(在本位中為SubClass ::vfunc3 的地址) 讀人EAX 寄存器治宣。最后急侥,在AE處調(diào)用虛函數(shù)。
AB處的虛表索引操作非常類(lèi)似于結(jié)構(gòu)體引用操作侮邀。我們可以定義一個(gè)結(jié)構(gòu)體來(lái)表示一個(gè)類(lèi)的虛表的布局坏怪,然后利用這個(gè)已定義的結(jié)構(gòu)體來(lái)提高反匯編代碼清單的可讀性,如下所示:
00000000 ? SubClass vtable struc ;(sizeof=0x14)
00000000 vfunc1 ? ? ? ? ? ?dd?
00000004 vfunc2 ? ? ? ? ? ?dd?
00000008 vfunc 3 ? ? ? ? ?dd?
0000000C vfunc4 ? ? ? ? ? ? dd?
00000010 vfunc5? ? ? ? ? ? dd?
00000014 SubClass vtable ends
結(jié)構(gòu)體允許將虛表引用操作重新格式化成:
mov ? eax,[edx+SubClass_vtable.vfunc3]
對(duì)象生命周期:對(duì)全局和靜態(tài)分配的對(duì)象绊茧,構(gòu)造函數(shù)中程序啟動(dòng)并進(jìn)入main函數(shù)之前被調(diào)用铝宵;棧分配的對(duì)象的構(gòu)造函數(shù)在對(duì)象進(jìn)入聲明對(duì)象的函數(shù)作用域中時(shí)被調(diào)用。一般對(duì)象一進(jìn)入聲明它的函數(shù)按傅,它的構(gòu)造函數(shù)就被調(diào)用捉超,如果對(duì)象在一個(gè)塊語(yǔ)句中聲明,它的構(gòu)造函數(shù)知道塊被輸入時(shí)才被調(diào)用(確實(shí)被輸入)唯绍。如果對(duì)象在程序堆中動(dòng)態(tài)分配則有二:一拼岳,調(diào)用new操作符分配對(duì)象的內(nèi)存,二况芒,調(diào)用構(gòu)造函數(shù)來(lái)初始化對(duì)象惜纸。vc++與gun g++主要區(qū)別:前者可確保在調(diào)用構(gòu)造函數(shù)之前new的結(jié)果不為null(空)
執(zhí)行一個(gè)構(gòu)造函數(shù)時(shí)叶撒,將會(huì)發(fā)生以下操作。
(1) 如果類(lèi)擁有一個(gè)超類(lèi)耐版,則調(diào)用超類(lèi)的構(gòu)造函數(shù)祠够。
(2) 如果類(lèi)包含任何虛函數(shù),則初始化虛表指針粪牲,使其指向類(lèi)的虛表古瓤。注意,這樣做可能會(huì)覆蓋一個(gè)在超類(lèi)中初始化的虛表指針腺阳,這實(shí)際上是希望的結(jié)果落君。
(3) 如果類(lèi)擁有本身就是對(duì)象的數(shù)據(jù)成員,則調(diào)用這些數(shù)據(jù)成員的構(gòu)造函數(shù)亭引。
(4) 最后绎速,執(zhí)行特定于代碼的構(gòu)造函數(shù)。這些是程序員指定的焙蚓、表示構(gòu)造函數(shù)C++行為的代碼纹冤。
構(gòu)造函數(shù)并未指定返回類(lèi)型,但由Microsoft Visual C++生成的構(gòu)造函數(shù)實(shí)際上返回到EAX寄存器中的thi s 指針购公。無(wú)論如何萌京,這是一個(gè)Visual C+實(shí)現(xiàn)細(xì)節(jié),并不允許C++程序員訪(fǎng)問(wèn)返回值君丁。析構(gòu)函數(shù)基本上按相反的順序調(diào)用枫夺。對(duì)于全局和靜態(tài)對(duì)象,析構(gòu)函數(shù)由在main函數(shù)結(jié)束后執(zhí)行的清理代碼調(diào)用绘闷。棧分配的對(duì)象的析構(gòu)函數(shù)在對(duì)象脫離作用域時(shí)被調(diào)用。堆分配的對(duì)象的析構(gòu)函數(shù)在分配給對(duì)象的內(nèi)存釋放之前通過(guò)delete 操作符調(diào)用较坛。
析構(gòu)函數(shù)執(zhí)行的操作與構(gòu)造函數(shù)執(zhí)行的操作大致相同印蔗,唯一不同的是,它以大概相反的順序執(zhí)行這些操作丑勤。
(1) 如果類(lèi)擁有任何虛函數(shù)华嘹,則還原對(duì)象的虛表指針,使其指向相關(guān)類(lèi)的虛表法竞。如果一個(gè)子類(lèi)在創(chuàng)建過(guò)程中覆蓋了虛表指針耙厚,就需要這樣做。
(2) 執(zhí)行程序員為析構(gòu)函數(shù)指定的代碼岔霸。
(3) 如果類(lèi)擁有本身就是對(duì)象的數(shù)據(jù)成員薛躬,則執(zhí)行這些成員的析構(gòu)函數(shù)。
(4) 最后呆细,如果對(duì)象擁有一個(gè)超類(lèi)型宝,則調(diào)用超類(lèi)的析構(gòu)函數(shù)。
通過(guò)了解超類(lèi)的構(gòu)造函數(shù)和析構(gòu)函數(shù)何時(shí)被調(diào)用,我們可以通過(guò)相關(guān)超類(lèi)函數(shù)的調(diào)用鏈趴酣,跟蹤一個(gè)對(duì)象的繼承體系梨树。有關(guān)虛表的最后一個(gè)問(wèn)題涉及它們?cè)诔绦蛑腥绾伪灰谩R粋€(gè)類(lèi)的虛表被直接引用岖寞,只存在兩種情況: 在該類(lèi)的構(gòu)造函數(shù)中引用和在析構(gòu)函數(shù)中引用抡四。定位一個(gè)虛表后,可以利用IDA 的數(shù)據(jù)交叉引用功能迅速定位相關(guān)類(lèi)的所有構(gòu)造函數(shù)和析構(gòu)函數(shù)
名稱(chēng)改編(名稱(chēng)修飾):是c++編譯器用于區(qū)分重載函數(shù)的機(jī)制仗谆,為了給重載函數(shù)生成唯一的名稱(chēng)編譯器用其他字符來(lái)修飾函數(shù)名稱(chēng)床嫌,用來(lái)編碼關(guān)于函數(shù)的各種信息,編碼后的信息描述函數(shù)的返回類(lèi)型胸私,所屬的類(lèi)和調(diào)用該參數(shù)所需的參數(shù)序列(類(lèi)型和順序)厌处。名稱(chēng)改編是C++程序的一個(gè)編譯器實(shí)現(xiàn)細(xì)節(jié),本身并不屬于C++語(yǔ)言規(guī)范岁疼,但通常IDA理解阔涉,并以注釋的形式顯示原始名稱(chēng)。
Options->Demangled Names 可選擇 demangle選項(xiàng)
改編名稱(chēng)能提供大量與函數(shù)簽名有關(guān)的信息捷绒,方便ida自己去識(shí)別函數(shù)
運(yùn)行時(shí)類(lèi)型識(shí)別(RTTI)也是編譯器實(shí)現(xiàn)細(xì)節(jié)瑰排,不是語(yǔ)言問(wèn)題。下面有例子:
class abstract_class{
public :
? ? ? ? ?virtual int vfunc()= o;
};
class concrete_class: public abstract_class {
public :
? ? ? concrete_class() ;
? ? ? int vfunc( );
};
void print_type(abstract_class*p) {
? ? ? ?cout << typeid(*p).name() < endl;
};
int main() {
? ? ? ? abstract_class *sc = new concrete_class();
? ? ? ? print_type(sc);
};
print_type 函數(shù)必須正確打印指針P 所指向的對(duì)象的類(lèi)型暖侨。在這個(gè)例子中椭住,基于main 函數(shù)創(chuàng)建了一個(gè)concrete_class 對(duì)象這個(gè)事實(shí),concrete_class 必須被打印字逗。那print_type,更具體的說(shuō)是typeid,如何知道p 指向的對(duì)象的類(lèi)型京郑?
因?yàn)槊總€(gè)多態(tài)對(duì)象都包含一個(gè)指向虛表的指針,編譯器將類(lèi)的類(lèi)型信息與類(lèi)虛表存儲(chǔ)在一起葫掉。具休來(lái)說(shuō)些举,編譯器在類(lèi)虛表之前放置一個(gè)指針,這個(gè)指針指向一個(gè)結(jié)構(gòu)體俭厚,其中包含用于確定擁有虛表的類(lèi)的名稱(chēng)所需的信息户魏。在g++代碼中,這個(gè)指針指向一個(gè)type_info結(jié)構(gòu)體挪挤,其中包含一個(gè)指向類(lèi)名稱(chēng)的指針叼丑。在VC+代碼中,指針指向一個(gè)微軟RTTCompleteObjectLocator 結(jié)構(gòu)體扛门,其中又包含一個(gè)指向TypeDescriptor 結(jié)構(gòu)體的指針鸠信。TypeDescriptor結(jié)構(gòu)體中則包含一個(gè)指定多態(tài)類(lèi)名稱(chēng)的字符數(shù)組。只有使用typeid 或dynamiCast 操作符的C++程序才需要RTT信息尖飞。多數(shù)編譯器都提供一些選項(xiàng)症副,禁止不需要RTTI 的二進(jìn)制文件生成RTT 店雅。
繼承關(guān)系:從RTTI出發(fā)