www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 嵌入式微處理器
[導讀]在學習編程的過程中,需要閱讀大量的源代碼才能提高自身的編程能力。同樣,在做產(chǎn)品的時候也需要大量參考同行的軟件才能改善自己產(chǎn)品的不足。如果發(fā)現(xiàn)某個軟件的功能非常不錯,是自己急需融入自己軟件產(chǎn)品的功能,而此時又沒有源代碼可以參考,那么程序員唯一能做的只有通過逆向分析來了解其實現(xiàn)方式。...

在學習編程的過程中,需要閱讀大量的源代碼才能提高自身的編程能力。同樣,在做產(chǎn)品的時候也需要大量參考同行的軟件才能改善自己產(chǎn)品的不足。如果發(fā)現(xiàn)某個軟件的功能非常不錯,是自己急需融入自己軟件產(chǎn)品的功能,而此時又沒有源代碼可以參考,那么程序員唯一能做的只有通過逆向分析來了解其實現(xiàn)方式。除此之外,當使用的某個軟件存在 Bug,而該軟件已經(jīng)不再更新時,程序員能做的并不是去尋找同類的其他軟件,而是可以通過逆向分析來自行修正其軟件的 Bug,從而很好地繼續(xù)使用該軟件。逆向分析程序的原因很多,有些情況不得不進行逆向分析,比如病毒分析、漏洞分析等。
可能病毒分析、漏洞分析等高深技術(shù)對于有些人來說目前還無法達到,但是其基礎(chǔ)知識部分都離不開逆向知識。下面借助IDA來分析由VC6編譯連接C語言的代碼,從而來學習掌握逆向的基礎(chǔ)知識。
1.?簡單的C語言函數(shù)調(diào)用程序
為了方便介紹關(guān)于函數(shù)的識別,這里寫一個簡單的C語言程序,用VC6進行編譯連接。C語言的代碼如下:
#include #include int test(char *szStr, int nNum){ printf("%s,?%d?\r\n",?szStr,?nNum);??MessageBox(NULL,?szStr,?NULL,?MB_OK); return?5;}int main(int argc, char ** argv){??int?nNum?=?test("hello",?6);??printf("%d?\r\n",?nNum); return?0;}在程序代碼中,自定義函數(shù)test()由主函數(shù)main()所調(diào)用,test()函數(shù)的返回值為int類型。在test()函數(shù)中調(diào)用了printf()函數(shù)和MessageBox()函數(shù)。將代碼在VC6下使用DEBUG方式進行編譯連接來生成一個可執(zhí)行文件,對該可執(zhí)行文件通過IDA進行逆向分析。
以上代碼的擴展名為“.c”,而不是“.cpp”。這里用來進行逆向分析的例子均使用DEBUG方式在VC6下進行編譯連接。
2.?函數(shù)逆向分析
大多數(shù)情況下程序員都是針對自己比較感興趣的程序部分進行逆向分析,分析部分功能或者部分關(guān)鍵函數(shù)。因此,確定函數(shù)的開始位置和結(jié)束位置非常重要。不過通常情況下,函數(shù)的起始位置和結(jié)束位置都可以通過反匯編工具自動識別,只有在代碼被刻意改變后才需要程序員自己進行識別。IDA可以很好地識別函數(shù)的起始位置和結(jié)束位置,如果在逆向分析的過程中發(fā)現(xiàn)有分析不準確的時候,可以通過Alt P快捷鍵打開“Edit function”(編輯函數(shù))對話框來調(diào)整函數(shù)的起始位置和結(jié)束位置?!癊dit function”對話框的界面如圖1所示。在圖1中,被選中的部分可以設(shè)定函數(shù)的起始地址和結(jié)束地址。圖1? “Edit function”對話框
IDA打開VC6編譯好的程序,在打開的時候,IDA會有一個提示,如圖2所示。該圖詢問是否使用PDB文件。PDB文件是程序數(shù)據(jù)庫文件,是編譯器生成的一個文件,方便程序調(diào)試使用。PDB包含函數(shù)地址、全局變量的名字和地址、參數(shù)和局部變量的名字和在堆棧的偏移量等很多信息。這里選擇“Yes”按鈕。圖2? 提示是否使用PDB文件
在分析其他程序的時候,通常沒有PDB文件,那么這里會選擇“No”按鈕。在有PDB和無PDB文件時,IDA的分析結(jié)果是截然不同的。請大家在自己分析時,嘗試對比不加載編譯器生成的PDB文件和加載了PDB文件IDA生成的反匯編代碼的差異。
IDA完成對程序的分析后,IDA直接找到了main()函數(shù)的跳表項,如圖3所示。圖3? main()函數(shù)的跳表
所謂main()函數(shù)的跳表項,意思是這里并不是main()函數(shù)的真正的起始位置,而是該位置是一個跳表,用來統(tǒng)一管理各個函數(shù)的地址。從圖3中看到,有一條jmp _main的匯編代碼,這條代碼用來跳向真正的main()函數(shù)的地址。在IDA中查看圖3上下位置,可能只能找到這么一條跳轉(zhuǎn)指令。在圖3的靠下部分有一句注釋為“[00000005 BYTES: COLLAPSED FUNCTION j__test. PRESS KEYPAD " " TO EXPAND]”。這里是可以展開的,在該注釋上單擊右鍵,出現(xiàn)右鍵菜單后選擇“Unhide”項,則可以看到被隱藏的跳表項,如圖4所示。圖4? 展開后的跳表
在實際的反匯編代碼時,jmp _main和jmp _test是緊挨著的兩條指令,而且jmp后面是兩個地址。這里的顯示函數(shù)形式、_main和_test是由IDA進行處理的。在OD下觀察跳表的形式,如圖5所示。圖5? OD中跳表的指令位置
并不是每個程序都能被IDA識別出跳轉(zhuǎn)到main()函數(shù)的跳表項,而且程序的入口點也并非main()函數(shù)。首先來看一下程序的入口函數(shù)位置。在IDA上單擊窗口選項卡,選擇“Exports”窗口(Exports窗口是導出窗口,用于查看導出函數(shù)的地址,但是對于EXE程序來說通常是沒有導出函數(shù)的,這里將顯示EXE程序的入口函數(shù)),在“Exports”窗口中可以看到_mainCRTStartup,如圖6所示。圖6? Exports窗口
雙擊_mainCRTStartup就可以到達啟動函數(shù)的位置了。在C語言中,main()不是程序運行的第一個函數(shù),而是程序員編寫程序時的第一個函數(shù),main()函數(shù)是由啟動函數(shù)來調(diào)用的?,F(xiàn)在看一下_mainCRTStartup函數(shù)的部分反匯編代碼:
.text:004011D0 public _mainCRTStartup.text:004011D0 _mainCRTStartup proc near.text:004011D0.text:004011D0 Code = dword ptr -1Ch.text:004011D0 var_18 = dword ptr -18h.text:004011D0 var_4 = dword ptr -4.text:004011D0.text:004011D0 push ebp.text:004011D1 mov ebp, esp.text:004011D3 push 0FFFFFFFFh.text:004011D5 push offset stru_422148.text:004011DA push offset __except_handler3.text:004011DF mov eax, large fs:0.text:004011E5 push eax.text:004011E6 mov large fs:0, esp.text:004011ED add esp, 0FFFFFFF0h.text:004011F0 push ebx.text:004011F1 push esi.text:004011F2 push edi.text:004011F3 mov [ebp var_18], esp.text:004011F6 call ds:__imp__GetVersion@0 ; GetVersion().text:004011FC mov __osver, eax.text:00401201 mov eax, __osver.text:00401206 shr eax, 8.text:00401209 and eax, 0FFh.text:0040120E mov __winminor, eax.text:00401213 mov ecx, __osver.text:00401219 and ecx, 0FFh.text:0040121F mov __winmajor, ecx.text:00401225 mov edx, __winmajor.text:0040122B shl edx, 8.text:0040122E add edx, __winminor.text:00401234 mov __winver, edx.text:0040123A mov eax, __osver.text:0040123F shr eax, 10h.text:00401242 and eax, 0FFFFh.text:00401247 mov __osver, eax.text:0040124C push 0.text:0040124E call __heap_init.text:00401253 add esp, 4.text:00401256 test eax, eax.text:00401258 jnz short loc_401264.text:0040125A push 1Ch.text:0040125C call fast_error_exit.text:00401261; ------------------------------------------------.text:00401261 add esp, 4.text:00401264.text:00401264 loc_401264: ; CODE XREF: _mainCRTStartup 88j.text:00401264 mov [ebp var_4], 0.text:0040126B call __ioinit.text:00401270 call ds:__imp__GetCommandLineA@0 ; GetCommandLineA().text:00401276 mov __acmdln, eax.text:0040127B call ___crtGetEnvironmentStringsA.text:00401280 mov __aenvptr, eax.text:00401285 call __setargv.text:0040128A call __setenvp.text:0040128F call __cinit.text:00401294 mov ecx, __environ.text:0040129A mov ___initenv, ecx.text:004012A0 mov edx, __environ.text:004012A6 push edx.text:004012A7 mov eax, ___argv.text:004012AC push eax.text:004012AD mov ecx, ___argc.text:004012B3 push ecx.text:004012B4 call _main_0.text:004012B9 add esp, 0Ch.text:004012BC mov [ebp Code], eax.text:004012BF mov edx, [ebp Code].text:004012C2 push edx ; Code.text:004012C3 call _exit.text:004012C3 _mainCRTStartup endp從反匯編代碼中可以看到,main()函數(shù)的調(diào)用在004012B4位置處。啟動函數(shù)從004011D0地址處開始,期間調(diào)用GetVersion()函數(shù)獲得了系統(tǒng)版本號、調(diào)用__heap_init函數(shù)初始化了程序所使用的堆空間、調(diào)用GetCommandLineA()函數(shù)獲取了命令行參數(shù)、調(diào)用___crtGetEnviro nmentStringsA函數(shù)獲得了環(huán)境變量字符串……在完成一系列啟動所需的工作后,終于在004012B4處調(diào)用了_main_0。由于這里使用的是調(diào)試版且有PDB文件,因此在反匯編代碼中直接顯示出程序中的符號,在分析其他程序時是沒有PDB文件的,這樣_main_0就會顯示為一個地址,而不是一個符號。不過依然可以通過規(guī)律來找到_main_0所在的位置。
沒有PDB文件,如何找到_main_0所在的位置呢?在VC6中,啟動函數(shù)會依次調(diào)用GetVersion()、GetCommandLineA()、GetEnvironmentStringsA()等函數(shù),而這一系列函數(shù)即是一串明顯的特征。在調(diào)用完GetEnvironmentStringsA()后,不遠處會有3個push操作,分別是main()函數(shù)的3個參數(shù),代碼如下:
.text:004012A0 mov edx, __environ.text:004012A6 push edx.text:004012A7 mov eax, ___argv.text:004012AC push eax.text:004012AD mov ecx, ___argc.text:004012B3 push ecx.text:004012B4 call _main_0該反匯編代碼對應(yīng)的C代碼如下:
#ifdef WPRFLAG __winitenv?=?_wenviron; mainret?=?wmain(__argc,?__wargv,?_wenviron);#else /* WPRFLAG */ __initenv?=?_environ; mainret?=?main(__argc,?__argv,?_environ);#endif /* WPRFLAG */該部分代碼是從CRT0.C中得到的,可以看到啟動函數(shù)在調(diào)用main()函數(shù)時有3個參數(shù)。
接著上面的內(nèi)容,在3個push操作后的第1個call處,即是_main_0函數(shù)的地址。往_main_0下面看,_main_0后地址為004012C3的指令為call _exit。確定了程序是由VC6編寫的,那么找到對_exit的調(diào)用后,往上找一個call指令就找到_main_0所對應(yīng)的地址。大家可以依照該方法進行測試。
在順利找到_main_0函數(shù)后,直接雙擊反匯編的_main_0,到達函數(shù)跳轉(zhuǎn)表處。在跳轉(zhuǎn)表中雙擊_main,即可到真正的_main函數(shù)的反匯編代碼處。_main函數(shù)的返匯編代碼如下:
.text:004010A0 _main proc near ; CODE XREF: _main_0j.text:004010A0.text:004010A0 var_44 = byte ptr -44h.text:004010A0 var_4 = dword ptr -4.text:004010A0.text:004010A0 push ebp.text:004010A1 mov ebp, esp.text:004010A3 sub esp, 44h.text:004010A6 push ebx.text:004010A7 push esi.text:004010A8 push edi.text:004010A9 lea edi, [ebp var_44].text:004010AC mov ecx, 11h.text:004010B1 mov eax, 0CCCCCCCCh.text:004010B6 rep stosd.text:004010B8 push 6.text:004010BA push offset aHello ; "hello".text:004010BF call j__test.text:004010C4 add esp, 8.text:004010C7 mov [ebp var_4], eax.text:004010CA mov eax, [ebp var_4].text:004010CD push eax.text:004010CE push offset aD ; "%d \r\n".text:004010D3 call _printf.text:004010D8 add esp, 8.text:004010DB xor eax, eax.text:004010DD pop edi.text:004010DE pop esi.text:004010DF pop ebx.text:004010E0 add esp, 44h.text:004010E3 cmp ebp, esp.text:004010E5 call __chkesp.text:004010EA mov esp, ebp.text:004010EC pop ebp.text:004010ED retn.text:004010ED _main endp短短幾行C語言代碼,在編譯連接生成可執(zhí)行文件后,再進行反匯編竟然生成了比C語言代碼多很多的代碼。仔細觀察上面的反匯編代碼,通過特征可以確定這是寫的主函數(shù),首先代碼中有一個對test()函數(shù)的調(diào)用在004010BF地址處,其次有一個對printf()函數(shù)的調(diào)用在004010D3地址處。_main函數(shù)的入口部分代碼如下:
.text:004010A0 push ebp.text:004010A1 mov ebp, esp.text:004010A3 sub esp, 44h.text:004010A6 push ebx.text:004010A7 push esi.text:004010A8 push edi.text:004010A9 lea edi, [ebp var_44].text:004010AC mov ecx, 11h.text:004010B1 mov eax, 0CCCCCCCCh.text:004010B6 rep stosd大多數(shù)函數(shù)的入口處都是push ebp/mov ebp, esp/sub esp, ×××這樣的形式,這幾句代碼完成了保存棧幀,并開辟了當前函數(shù)所需的??臻g。push ebx/push esi/push edi是用來保存幾個關(guān)鍵寄存器的值,以便函數(shù)返回后這幾個寄存器中的值還能在調(diào)用函數(shù)處繼續(xù)使用而沒有被破壞掉。lea edi, [ebp var_44]/mov ecx, 11h/move ax , 0CCCCCCCCh/rep stosd,這幾句代碼是開辟的內(nèi)存空間,全部初始化為0xCC。0xCC被當作機器碼來解釋時,其對應(yīng)的匯編指令為int 3,也就是調(diào)用3號斷點中斷來產(chǎn)生一個軟件中斷。將新開辟的??臻g初始化為0xCC,這樣做的好處是方便調(diào)試,尤其是給指針變量的調(diào)試帶來了方便。
以上反匯編代碼是一個固定的形式,唯一會發(fā)生變化的是sub esp, ×××部分,在當前反匯編代碼處是sub esp, 44h。在VC6下使用Debug方式編譯,如果當前函數(shù)沒有變量,那么該句代碼是sub esp, 40h;如果有一個變量,其代碼是sub esp, 44h;有兩個變量時,為sub esp, 48h。也就是說,通過Debug方式編譯時,函數(shù)分配棧空間總是開辟了局部變量的空間后又預留了40h字節(jié)的空間。局部變量都在??臻g中,??臻g是在進入函數(shù)后臨時開辟的空間,因此局部變量在函數(shù)結(jié)束后就不復存在了。與函數(shù)入口代碼對應(yīng)的代碼當然是出口代碼,其代碼如下:
.text:004010DD pop edi.text:004010DE pop esi.text:004010DF pop ebx.text:004010E0 add esp, 44h.text:004010E3 cmp ebp, esp.text:004010E5 call __chkesp.text:004010EA mov esp, ebp.text:004010EC pop ebp.text:004010ED retn.text:004010ED _main endp函數(shù)的出口部分(或者是函數(shù)返回時的部分)也屬于固定格式,這個格式跟入口的格式基本是對應(yīng)的。首先是pop edi/pop esi/pop ebx,這里是將入口部分保存的幾個關(guān)鍵寄存器的值進行恢復。push和pop是對堆棧進行操作的指令。堆棧結(jié)構(gòu)的特點是后進先出,或先進后出。因此,在函數(shù)的入口部分的入棧順序是push ebx/push esi/push edi,出棧順序則是倒序pop edi/pop esi/pop ebx?;謴屯昙拇嫫鞯闹岛?,需要恢復esp指針的位置,這里的指令是add esp, 44h,將臨時開辟的棧空間釋放掉(這里的釋放只是改變寄存器的值,其中的數(shù)據(jù)并未清除掉),其中44h也是與入口處的44h對應(yīng)的。從入口和出口改變esp寄存器的情況可以看出,棧的方向是由高地址向低地址方向延伸的,開辟空間是將esp做減法操作。mov esp, ebp/pop ebp是恢復棧幀,retn就返回上層函數(shù)了。在該反匯編代碼中還有一步?jīng)]有講到,也就是cmp ebp, esp/call __chkesp,這兩句是對__chkesp函數(shù)的一個調(diào)用。在Debug方式下編譯,對幾乎所有的函數(shù)調(diào)用完成后都會調(diào)用一次__chkesp。該函數(shù)的功能是用來檢查棧是否平衡,以保證程序的正確性。如果棧不平,會給出錯誤提示。這里做個簡單的測試,在主函數(shù)的return語句前加一條內(nèi)聯(lián)匯編__asm push ebx(只要是改變esp或ebp寄存器值的操作都可以達到效果),然后編譯連接運行,在輸出后會看到一個錯誤的提示,如圖7所示。圖7? 調(diào)用__chkesp后對棧平衡進行檢查后的出錯提示
圖7就是__chkesp函數(shù)在檢測到ebp與esp不平時給出的提示框。該功能只在DEBUG版本中存在。
主函數(shù)的反匯編代碼中還有一部分沒有介紹,反匯編代碼如下:
.text:004010B8 push 6.text:004010BA push offset aHello ; "hello".text:004010BF call j__test.text:004010C4 add esp, 8.text:004010C7 mov [ebp var_4], eax.text:004010CA mov eax, [ebp var_4].text:004010CD push eax.text:004010CE push offset aD ; "%d \r\n".text:004010D3 call _printf.text:004010D8 add esp, 8.text:004010DB xor eax, eax首先幾條反匯編代碼是push 6/push offset aHello/call j_test/add esp, 8/mov [ebp var_ 4], eax,這幾條反匯編代碼是主函數(shù)對test()函數(shù)的調(diào)用。函數(shù)參數(shù)的傳遞可以選擇寄存器或者內(nèi)存。由于寄存器數(shù)量有限,幾乎大部分函數(shù)調(diào)用都是通過內(nèi)存進行傳遞的。當參數(shù)使用完成后,需要把參數(shù)所使用的內(nèi)存進行回收。對于VC開發(fā)環(huán)境而言,其默認的調(diào)用約定方式是cdecl。這種函數(shù)調(diào)用約定對參數(shù)的傳遞依靠棧內(nèi)存,在調(diào)用函數(shù)前,會通過壓棧操作將參數(shù)從右往左依次送入棧中。在C代碼中,對test()函數(shù)的調(diào)用形式如下:
int nNum = test("hello", 6);而對應(yīng)的反匯編代碼為push 6 / push offset aHello / call j_test。從壓棧操作的push指令來看,參數(shù)是從右往左依次入棧的。當函數(shù)返回時,需要將參數(shù)使用的空間回收。這里的回收,指的是恢復esp寄存器的值到函數(shù)調(diào)用前的值。而對于cdecl調(diào)用方式而言,平衡堆棧的操作是由函數(shù)調(diào)用方來做的。從上面的反匯編代碼中可以看到反匯編代碼add esp, 8,它是用于平衡堆棧的。該代碼對應(yīng)的語言為調(diào)用函數(shù)前的兩個push操作,即函數(shù)參數(shù)入棧的操作。
函數(shù)的返回值通常保存在eax寄存器中,這里的返回值是以return語句來完成的返回值,并非以參數(shù)接收的返回值。004010C7地址處的反匯編代碼mov [ebp var_4], eax是將對j_test調(diào)用后的返回值保存在[ebp var_4]中,這里的[ebp var_4]就相當于C語言代碼中的nNum變量。逆向分析時,可以在IDA中通過快捷鍵N來完成對var_4的重命名。
在對j_test調(diào)用完成并將返回值保存在var_4中后,緊接著push eax/push offset aD/call _printf/add esp, 8的反匯編代碼應(yīng)該就不陌生了。而最后面的xor eax, eax這句代碼是將eax進行清0。因為在C語言代碼中,main()函數(shù)的返回值為0,即return 0;,因此這里對eax進行了清0操作。
雙擊004010BF地址處的call j__test,會移到j(luò)_test的函數(shù)跳表處,反匯編代碼如下:
.text:0040100A j__test proc near ; CODE XREF: _main 1Fp.text:0040100A jmp _test.text:0040100A j__test endp雙擊跳表中的_test,到如下反匯編處:
.text:00401020 ; int __cdecl test(LPCSTR lpText, int).text:00401020 _test proc near ; CODE XREF: j__testj.text:00401020.text:00401020 var_40 = byte ptr -40h.text:00401020 lpText = dword ptr 8.text:00401020 arg_4 = dword ptr 0Ch.text:00401020.text:00401020 push ebp.text:00401021 mov ebp, esp.text:00401023 sub esp, 40h.text:00401026 push ebx.text:00401027 push esi.text:00401028 push edi.text:00401029 lea edi, [ebp var_40].text:0040102C mov ecx, 10h.text:00401031 mov eax, 0CCCCCCCCh.text:00401036 rep stosd.text:00401038 mov eax, [ebp arg_4].text:0040103B push eax.text:0040103C mov ecx, [ebp lpText].text:0040103F push ecx.text:00401040 push offset Format ; "%s, %d \r\n".text:00401045 call _printf.text:0040104A add esp, 0Ch.text:0040104D mov esi, esp.text:0040104F push 0 ; uType.text:00401051 push 0 ; lpCaption.text:00401053 mov edx, [ebp lpText].text:00401056 push edx ; lpText.text:00401057 push 0 ; hWnd.text:00401059 call ds:__imp__MessageBoxA@16 ; MessageBoxA(x,x,x,x).text:0040105F cmp esi, esp.text:00401061 call __chkesp.text:00401066 mov eax, 5.text:0040106B pop edi.text:0040106C pop esi.text:0040106D pop ebx.text:0040106E add esp, 40h.text:00401071 cmp ebp, esp.text:00401073 call __chkesp.text:00401078 mov esp, ebp.text:0040107A pop ebp.text:0040107B retn.text:0040107B _test endp該反匯編代碼的開頭部分和結(jié)尾部分,這里不再重復,主要看一下中間的反匯編代碼部分。中間的部分主要是printf()函數(shù)和MessageBoxA()函數(shù)的反匯編代碼。
調(diào)用printf()函數(shù)的反匯編代碼如下:
.text:00401038 mov eax, [ebp arg_4].text:0040103B push eax.text:0040103C mov ecx, [ebp lpText].text:0040103F push ecx.text:00401040 push offset Format ; "%s, %d \r\n".text:00401045 call _printf.text:0040104A add esp, 0Ch調(diào)用MessageBoxA()函數(shù)的反匯編代碼如下:
.text:0040104F push 0 ; uType.text:00401051 push 0 ; lpCaption.text:00401053 mov edx, [ebp lpText].text:00401056 push edx ; lpText.text:00401057 push 0 ; hWnd.text:00401059 call ds:__imp__MessageBoxA@16 ; MessageBoxA(x,x,x,x)比較以上簡單的兩段代碼會發(fā)現(xiàn)很多不同之處,首先在調(diào)用完_printf后會有add esp, 0Ch的代碼進行平衡堆棧,而調(diào)用MessageBoxA后沒有。為什么對MessageBoxA函數(shù)的調(diào)用則沒有呢?原因在于,在Windows系統(tǒng)下,對API函數(shù)的調(diào)用都遵循的函數(shù)調(diào)用約定是stdcall。對于stdcall這種調(diào)用約定而言,參數(shù)依然是從右往左依次被送入堆棧,而參數(shù)的平棧是在API函數(shù)內(nèi)完成的,而不是在函數(shù)的調(diào)用方完成的。在OD中看一下MessageBoxA函數(shù)在返回時的平棧方式,如圖8所示。圖8? MessageBoxA函數(shù)的平棧操作
從圖8中可以看出,MessageBoxA函數(shù)在調(diào)用retn指令后跟了一個10。這里的10是一個16進制數(shù),16進制的10等于10進制的16。而在為MessageBoxA傳遞參數(shù)時,每個參數(shù)是4字節(jié),4個參數(shù)等于16字節(jié),因此retn 10除了有返回的作用外,還包含了add esp, 10的作用。
上面兩段反匯編代碼中除了平衡堆棧的不同外,還有另外一個明顯的區(qū)別。在調(diào)用printf時的指令為call _printf,而調(diào)用MessageBoxA時的指令為call ds:__imp__MessageBoxA@16。printf()函數(shù)在stdio.h頭文件中,該函數(shù)屬于C語言的靜態(tài)庫,在連接時會將其代碼連接入二進制文件中。而MessageBoxA函數(shù)的實現(xiàn)在user32.dll這個動態(tài)連接庫中。在代碼中,這里只留了進入MessageBoxA函數(shù)的一個地址,并沒有具體的代碼。MessageBoxA的具體地址存放在數(shù)據(jù)節(jié)中,因此在反匯編代碼中給出了提示,使用了前綴“ds:”?!癬_imp__”表示導入函數(shù)。MessageBoxA后面的“@16”表示該API函數(shù)有4個參數(shù),即16 / 4 = 4。
多參的API函數(shù)仍然在調(diào)用方進行平棧,比如wsprintf()函數(shù)。原因在于,被調(diào)用的函數(shù)無法具體明確調(diào)用方會傳遞幾個參數(shù),因此多參函數(shù)無法在函數(shù)內(nèi)完成參數(shù)的堆棧平衡工作。
stdcall是Windows下的標準函數(shù)調(diào)用約定。Windows提供的應(yīng)用層及內(nèi)核層函數(shù)均使用stdcall的調(diào)用約定方式。cdecl是C語言的調(diào)用函數(shù)約定方式。
3. 結(jié)語

在逆向分析函數(shù)時,首先需要確定函數(shù)的起始位置,這通常會由IDA自動進行識別(識別不準確的話,就只能手動識別了);其次需要掌握函數(shù)的調(diào)用約定和確定函數(shù)的參數(shù)個數(shù),確定函數(shù)的調(diào)用約定和參數(shù)個數(shù)都是通過平棧的方式和平棧時對esp操作的值來進行判斷的;最后就是觀察函數(shù)的返回值,這部分通常就是觀察eax的值,由于return通常只返回布爾類型、數(shù)值類型相關(guān)的值,因此通過觀察eax的值可以確定返回值的類型,確定了返回值的類型后,可以進一步考慮函數(shù)調(diào)用方下一步的動作。
參考文獻:C 黑客編程揭秘與防范(第3版)

END
來源:計算機與網(wǎng)絡(luò)安全版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除。
嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉