精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

網(wǎng)絡(luò)安全編程:C語(yǔ)言逆向之函數(shù)的識(shí)別

安全
下面借助IDA來(lái)分析由VC6編譯連接C語(yǔ)言的代碼,從而來(lái)學(xué)習(xí)掌握逆向的基礎(chǔ)知識(shí)。

 [[391373]]

在學(xué)習(xí)編程的過(guò)程中,需要閱讀大量的源代碼才能提高自身的編程能力。同樣,在做產(chǎn)品的時(shí)候也需要大量參考同行的軟件才能改善自己產(chǎn)品的不足。如果發(fā)現(xiàn)某個(gè)軟件的功能非常不錯(cuò),是自己急需融入自己軟件產(chǎn)品的功能,而此時(shí)又沒(méi)有源代碼可以參考,那么程序員唯一能做的只有通過(guò)逆向分析來(lái)了解其實(shí)現(xiàn)方式。除此之外,當(dāng)使用的某個(gè)軟件存在 Bug,而該軟件已經(jīng)不再更新時(shí),程序員能做的并不是去尋找同類(lèi)的其他軟件,而是可以通過(guò)逆向分析來(lái)自行修正其軟件的 Bug,從而很好地繼續(xù)使用該軟件。逆向分析程序的原因很多,有些情況不得不進(jìn)行逆向分析,比如病毒分析、漏洞分析等。

可能病毒分析、漏洞分析等高深技術(shù)對(duì)于有些人來(lái)說(shuō)目前還無(wú)法達(dá)到,但是其基礎(chǔ)知識(shí)部分都離不開(kāi)逆向知識(shí)。下面借助IDA來(lái)分析由VC6編譯連接C語(yǔ)言的代碼,從而來(lái)學(xué)習(xí)掌握逆向的基礎(chǔ)知識(shí)。

1. 簡(jiǎn)單的C語(yǔ)言函數(shù)調(diào)用程序

為了方便介紹關(guān)于函數(shù)的識(shí)別,這里寫(xiě)一個(gè)簡(jiǎn)單的C語(yǔ)言程序,用VC6進(jìn)行編譯連接。C語(yǔ)言的代碼如下: 

  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. int test(char *szStr, int nNum)  
  4.  
  5.   printf("%s, %d \r\n", szStr, nNum);  
  6.   MessageBox(NULL, szStr, NULL, MB_OK);  
  7.   return 5;  
  8.  
  9. int main(int argc, char ** argv)  
  10.  
  11.   int nNum = test("hello", 6);  
  12.   printf("%d \r\n", nNum);  
  13.   return 0;  

在程序代碼中,自定義函數(shù)test()由主函數(shù)main()所調(diào)用,test()函數(shù)的返回值為int類(lèi)型。在test()函數(shù)中調(diào)用了printf()函數(shù)和MessageBox()函數(shù)。將代碼在VC6下使用DEBUG方式進(jìn)行編譯連接來(lái)生成一個(gè)可執(zhí)行文件,對(duì)該可執(zhí)行文件通過(guò)IDA進(jìn)行逆向分析。

以上代碼的擴(kuò)展名為“.c”,而不是“.cpp”。這里用來(lái)進(jìn)行逆向分析的例子均使用DEBUG方式在VC6下進(jìn)行編譯連接。

2. 函數(shù)逆向分析

大多數(shù)情況下程序員都是針對(duì)自己比較感興趣的程序部分進(jìn)行逆向分析,分析部分功能或者部分關(guān)鍵函數(shù)。因此,確定函數(shù)的開(kāi)始位置和結(jié)束位置非常重要。不過(guò)通常情況下,函數(shù)的起始位置和結(jié)束位置都可以通過(guò)反匯編工具自動(dòng)識(shí)別,只有在代碼被刻意改變后才需要程序員自己進(jìn)行識(shí)別。IDA可以很好地識(shí)別函數(shù)的起始位置和結(jié)束位置,如果在逆向分析的過(guò)程中發(fā)現(xiàn)有分析不準(zhǔn)確的時(shí)候,可以通過(guò)Alt + P快捷鍵打開(kāi)“Edit function”(編輯函數(shù))對(duì)話框來(lái)調(diào)整函數(shù)的起始位置和結(jié)束位置。“Edit function”對(duì)話框的界面如圖1所示。在圖1中,被選中的部分可以設(shè)定函數(shù)的起始地址和結(jié)束地址。

圖1  “Edit function”對(duì)話框

用IDA打開(kāi)VC6編譯好的程序,在打開(kāi)的時(shí)候,IDA會(huì)有一個(gè)提示,如圖2所示。該圖詢問(wèn)是否使用PDB文件。PDB文件是程序數(shù)據(jù)庫(kù)文件,是編譯器生成的一個(gè)文件,方便程序調(diào)試使用。PDB包含函數(shù)地址、全局變量的名字和地址、參數(shù)和局部變量的名字和在堆棧的偏移量等很多信息。這里選擇“Yes”按鈕。

圖2  提示是否使用PDB文件

在分析其他程序的時(shí)候,通常沒(méi)有PDB文件,那么這里會(huì)選擇“No”按鈕。在有PDB和無(wú)PDB文件時(shí),IDA的分析結(jié)果是截然不同的。請(qǐng)大家在自己分析時(shí),嘗試對(duì)比不加載編譯器生成的PDB文件和加載了PDB文件IDA生成的反匯編代碼的差異。

當(dāng)IDA完成對(duì)程序的分析后,IDA直接找到了main()函數(shù)的跳表項(xiàng),如圖3所示。

圖3  main()函數(shù)的跳表

所謂main()函數(shù)的跳表項(xiàng),意思是這里并不是main()函數(shù)的真正的起始位置,而是該位置是一個(gè)跳表,用來(lái)統(tǒng)一管理各個(gè)函數(shù)的地址。從圖3中看到,有一條jmp _main的匯編代碼,這條代碼用來(lái)跳向真正的main()函數(shù)的地址。在IDA中查看圖3上下位置,可能只能找到這么一條跳轉(zhuǎn)指令。在圖3的靠下部分有一句注釋為“[00000005 BYTES: COLLAPSED FUNCTION j__test. PRESS KEYPAD "+" TO EXPAND]”。這里是可以展開(kāi)的,在該注釋上單擊右鍵,出現(xiàn)右鍵菜單后選擇“Unhide”項(xiàng),則可以看到被隱藏的跳表項(xiàng),如圖4所示。

圖4  展開(kāi)后的跳表

在實(shí)際的反匯編代碼時(shí),jmp _main和jmp _test是緊挨著的兩條指令,而且jmp后面是兩個(gè)地址。這里的顯示函數(shù)形式、_main和_test是由IDA進(jìn)行處理的。在OD下觀察跳表的形式,如圖5所示。

圖5  OD中跳表的指令位置

并不是每個(gè)程序都能被IDA識(shí)別出跳轉(zhuǎn)到main()函數(shù)的跳表項(xiàng),而且程序的入口點(diǎn)也并非main()函數(shù)。首先來(lái)看一下程序的入口函數(shù)位置。在IDA上單擊窗口選項(xiàng)卡,選擇“Exports”窗口(Exports窗口是導(dǎo)出窗口,用于查看導(dǎo)出函數(shù)的地址,但是對(duì)于EXE程序來(lái)說(shuō)通常是沒(méi)有導(dǎo)出函數(shù)的,這里將顯示EXE程序的入口函數(shù)),在“Exports”窗口中可以看到_mainCRTStartup,如圖6所示。

圖6  Exports窗口

雙擊_mainCRTStartup就可以到達(dá)啟動(dòng)函數(shù)的位置了。在C語(yǔ)言中,main()不是程序運(yùn)行的第一個(gè)函數(shù),而是程序員編寫(xiě)程序時(shí)的第一個(gè)函數(shù),main()函數(shù)是由啟動(dòng)函數(shù)來(lái)調(diào)用的。現(xiàn)在看一下

  1. _mainCRTStartup函數(shù)的部分反匯編代碼:  
  2. .text:004011D0 public _mainCRTStartup  
  3. .text:004011D0 _mainCRTStartup proc near  
  4. .text:004011D0  
  5. .text:004011D0 Code = dword ptr -1Ch  
  6. .text:004011D0 var_18 = dword ptr -18h  
  7. .text:004011D0 var_4 = dword ptr -4  
  8. .text:004011D0  
  9. .text:004011D0 push ebp  
  10. .text:004011D1 mov ebp, esp  
  11. .text:004011D3 push 0FFFFFFFFh  
  12. .text:004011D5 push offset stru_422148  
  13. .text:004011DA push offset __except_handler3  
  14. .text:004011DF mov eax, large fs:0  
  15. .text:004011E5 push eax  
  16. .text:004011E6 mov large fs:0, esp  
  17. .text:004011ED add esp, 0FFFFFFF0h  
  18. .text:004011F0 push ebx  
  19. .text:004011F1 push esi  
  20. .text:004011F2 push edi  
  21. .text:004011F3 mov [ebp+var_18], esp  
  22. .text:004011F6 call ds:__imp__GetVersion@0 ; GetVersion()  
  23. .text:004011FC mov __osver, eax  
  24. .text:00401201 mov eax, __osver  
  25. .text:00401206 shr eax, 8  
  26. .text:00401209 and eax, 0FFh  
  27. .text:0040120E mov __winminor, eax  
  28. .text:00401213 mov ecx, __osver  
  29. .text:00401219 and ecx, 0FFh  
  30. .text:0040121F mov __winmajor, ecx  
  31. .text:00401225 mov edx, __winmajor  
  32. .text:0040122B shl edx, 8  
  33. .text:0040122E add edx, __winminor  
  34. .text:00401234 mov __winver, edx  
  35. .text:0040123A mov eax, __osver  
  36. .text:0040123F shr eax, 10h  
  37. .text:00401242 and eax, 0FFFFh  
  38. .text:00401247 mov __osver, eax  
  39. .text:0040124C push 0  
  40. .text:0040124E call __heap_init  
  41. .text:00401253 add esp, 4  
  42. .text:00401256 test eax, eax  
  43. .text:00401258 jnz short loc_401264  
  44. .text:0040125A push 1Ch  
  45. .text:0040125C call fast_error_exit 
  46. .text:00401261; ------------------------------------------------  
  47. .text:00401261 add esp, 4  
  48. .text:00401264  
  49. .text:00401264 loc_401264: ; CODE XREF: _mainCRTStartup+88j  
  50. .text:00401264 mov [ebp+var_4], 0  
  51. .text:0040126B call __ioinit  
  52. .text:00401270 call ds:__imp__GetCommandLineA@0 ; GetCommandLineA()  
  53. .text:00401276 mov __acmdln, eax  
  54. .text:0040127B call ___crtGetEnvironmentStringsA  
  55. .text:00401280 mov __aenvptr, eax  
  56. .text:00401285 call __setargv  
  57. .text:0040128A call __setenvp 
  58. .text:0040128F call __cinit  
  59. .text:00401294 mov ecx, __environ  
  60. .text:0040129A mov ___initenv, ecx  
  61. .text:004012A0 mov edx, __environ  
  62. .text:004012A6 push edx  
  63. .text:004012A7 mov eax, ___argv  
  64. .text:004012AC push eax  
  65. .text:004012AD mov ecx, ___argc 
  66. .text:004012B3 push ecx  
  67. .text:004012B4 call _main_0  
  68. .text:004012B9 add esp, 0Ch  
  69. .text:004012BC mov [ebp+Code], eax  
  70. .text:004012BF mov edx, [ebp+Code]  
  71. .text:004012C2 push edx ; Code  
  72. .text:004012C3 call _exit  
  73. .text:004012C3 _mainCRTStartup endp 

從反匯編代碼中可以看到,main()函數(shù)的調(diào)用在004012B4位置處。啟動(dòng)函數(shù)從004011D0地址處開(kāi)始,期間調(diào)用GetVersion()函數(shù)獲得了系統(tǒng)版本號(hào)、調(diào)用__heap_init函數(shù)初始化了程序所使用的堆空間、調(diào)用GetCommandLineA()函數(shù)獲取了命令行參數(shù)、調(diào)用___crtGetEnviro nmentStringsA函數(shù)獲得了環(huán)境變量字符串……在完成一系列啟動(dòng)所需的工作后,終于在004012B4處調(diào)用了_main_0。由于這里使用的是調(diào)試版且有PDB文件,因此在反匯編代碼中直接顯示出程序中的符號(hào),在分析其他程序時(shí)是沒(méi)有PDB文件的,這樣_main_0就會(huì)顯示為一個(gè)地址,而不是一個(gè)符號(hào)。不過(guò)依然可以通過(guò)規(guī)律來(lái)找到_main_0所在的位置。

沒(méi)有PDB文件,如何找到_main_0所在的位置呢?在VC6中,啟動(dòng)函數(shù)會(huì)依次調(diào)用GetVersion()、GetCommandLineA()、GetEnvironmentStringsA()等函數(shù),而這一系列函數(shù)即是一串明顯的特征。在調(diào)用完GetEnvironmentStringsA()后,不遠(yuǎn)處會(huì)有3個(gè)push操作,分別是main()函數(shù)的3個(gè)參數(shù),代碼如下: 

  1. .text:004012A0 mov edx, __environ  
  2. .text:004012A6 push edx  
  3. .text:004012A7 mov eax, ___argv  
  4. .text:004012AC push eax  
  5. .text:004012AD mov ecx, ___argc  
  6. .text:004012B3 push ecx  
  7. .text:004012B4 call _main_0 

該反匯編代碼對(duì)應(yīng)的C代碼如下: 

  1. #ifdef WPRFLAG  
  2.   __winitenv = _wenviron 
  3.   mainret = wmain(__argc, __wargv, _wenviron);  
  4. #else /* WPRFLAG */  
  5.   __initenv = _environ 
  6.   mainmainret = main(__argc, __argv, _environ);  
  7. #endif /* WPRFLAG */ 

該部分代碼是從CRT0.C中得到的,可以看到啟動(dòng)函數(shù)在調(diào)用main()函數(shù)時(shí)有3個(gè)參數(shù)。

接著上面的內(nèi)容,在3個(gè)push操作后的第1個(gè)call處,即是_main_0函數(shù)的地址。往_main_0下面看,_main_0后地址為004012C3的指令為call _exit。確定了程序是由VC6編寫(xiě)的,那么找到對(duì)_exit的調(diào)用后,往上找一個(gè)call指令就找到_main_0所對(duì)應(yīng)的地址。大家可以依照該方法進(jìn)行測(cè)試。

在順利找到_main_0函數(shù)后,直接雙擊反匯編的_main_0,到達(dá)函數(shù)跳轉(zhuǎn)表處。在跳轉(zhuǎn)表中雙擊_main,即可到真正的_main函數(shù)的反匯編代碼處。_main函數(shù)的返匯編代碼如下: 

  1. .text:004010A0 _main proc near ; CODE XREF: _main_0j  
  2. .text:004010A0  
  3. .text:004010A0 var_44 = byte ptr -44h  
  4. .text:004010A0 var_4 = dword ptr -4  
  5. .text:004010A0  
  6. .text:004010A0 push ebp  
  7. .text:004010A1 mov ebp, esp  
  8. .text:004010A3 sub esp, 44h  
  9. .text:004010A6 push ebx  
  10. .text:004010A7 push esi  
  11. .text:004010A8 push edi  
  12. .text:004010A9 lea edi, [ebp+var_44]  
  13. .text:004010AC mov ecx, 11h  
  14. .text:004010B1 mov eax, 0CCCCCCCCh  
  15. .text:004010B6 rep stosd  
  16. .text:004010B8 push 6  
  17. .text:004010BA push offset aHello ; "hello"  
  18. .text:004010BF call j__test  
  19. .text:004010C4 add esp, 8  
  20. .text:004010C7 mov [ebp+var_4], eax  
  21. .text:004010CA mov eax, [ebp+var_4]  
  22. .text:004010CD push eax  
  23. .text:004010CE push offset aD ; "%d \r\n"  
  24. .text:004010D3 call _printf  
  25. .text:004010D8 add esp, 8  
  26. .text:004010DB xor eax, eax  
  27. .text:004010DD pop edi 
  28. .text:004010DE pop esi  
  29. .text:004010DF pop ebx  
  30. .text:004010E0 add esp, 44h  
  31. .text:004010E3 cmp ebp, esp  
  32. .text:004010E5 call __chkesp  
  33. .text:004010EA mov esp, ebp  
  34. .text:004010EC pop ebp  
  35. .text:004010ED retn  
  36. .text:004010ED _main endp 

短短幾行C語(yǔ)言代碼,在編譯連接生成可執(zhí)行文件后,再進(jìn)行反匯編竟然生成了比C語(yǔ)言代碼多很多的代碼。仔細(xì)觀察上面的反匯編代碼,通過(guò)特征可以確定這是寫(xiě)的主函數(shù),首先代碼中有一個(gè)對(duì)test()函數(shù)的調(diào)用在004010BF地址處,其次有一個(gè)對(duì)printf()函數(shù)的調(diào)用在004010D3地址處。_main函數(shù)的入口部分代碼如下: 

  1. .text:004010A0 push ebp  
  2. .text:004010A1 mov ebp, esp  
  3. .text:004010A3 sub esp, 44h  
  4. .text:004010A6 push ebx  
  5. .text:004010A7 push esi  
  6. .text:004010A8 push edi  
  7. .text:004010A9 lea edi, [ebp+var_44]  
  8. .text:004010AC mov ecx, 11h  
  9. .text:004010B1 mov eax, 0CCCCCCCCh  
  10. .text:004010B6 rep stosd 

大多數(shù)函數(shù)的入口處都是push ebp/mov ebp, esp/sub esp, ×××這樣的形式,這幾句代碼完成了保存棧幀,并開(kāi)辟了當(dāng)前函數(shù)所需的棧空間。push ebx/push esi/push edi是用來(lái)保存幾個(gè)關(guān)鍵寄存器的值,以便函數(shù)返回后這幾個(gè)寄存器中的值還能在調(diào)用函數(shù)處繼續(xù)使用而沒(méi)有被破壞掉。lea edi, [ebp + var_44]/mov ecx, 11h/move ax , 0CCCCCCCCh/rep stosd,這幾句代碼是開(kāi)辟的內(nèi)存空間,全部初始化為0xCC。0xCC被當(dāng)作機(jī)器碼來(lái)解釋時(shí),其對(duì)應(yīng)的匯編指令為int 3,也就是調(diào)用3號(hào)斷點(diǎn)中斷來(lái)產(chǎn)生一個(gè)軟件中斷。將新開(kāi)辟的棧空間初始化為0xCC,這樣做的好處是方便調(diào)試,尤其是給指針變量的調(diào)試帶來(lái)了方便。

以上反匯編代碼是一個(gè)固定的形式,唯一會(huì)發(fā)生變化的是sub esp, ×××部分,在當(dāng)前反匯編代碼處是sub esp, 44h。在VC6下使用Debug方式編譯,如果當(dāng)前函數(shù)沒(méi)有變量,那么該句代碼是sub esp, 40h;如果有一個(gè)變量,其代碼是sub esp, 44h;有兩個(gè)變量時(shí),為sub esp, 48h。也就是說(shuō),通過(guò)Debug方式編譯時(shí),函數(shù)分配棧空間總是開(kāi)辟了局部變量的空間后又預(yù)留了40h字節(jié)的空間。局部變量都在棧空間中,棧空間是在進(jìn)入函數(shù)后臨時(shí)開(kāi)辟的空間,因此局部變量在函數(shù)結(jié)束后就不復(fù)存在了。與函數(shù)入口代碼對(duì)應(yīng)的代碼當(dāng)然是出口代碼,其代碼如下: 

  1. .text:004010DD pop edi  
  2. .text:004010DE pop esi  
  3. .text:004010DF pop ebx  
  4. .text:004010E0 add esp, 44h  
  5. .text:004010E3 cmp ebp, esp  
  6. .text:004010E5 call __chkesp  
  7. .text:004010EA mov esp, ebp  
  8. .text:004010EC pop ebp 
  9. .text:004010ED retn  
  10. .text:004010ED _main endp 

函數(shù)的出口部分(或者是函數(shù)返回時(shí)的部分)也屬于固定格式,這個(gè)格式跟入口的格式基本是對(duì)應(yīng)的。首先是pop edi/pop esi/pop ebx,這里是將入口部分保存的幾個(gè)關(guān)鍵寄存器的值進(jìn)行恢復(fù)。push和pop是對(duì)堆棧進(jìn)行操作的指令。堆棧結(jié)構(gòu)的特點(diǎn)是后進(jìn)先出,或先進(jìn)后出。因此,在函數(shù)的入口部分的入棧順序是push ebx/push esi/push edi,出棧順序則是倒序pop edi/pop esi/pop ebx。恢復(fù)完寄存器的值后,需要恢復(fù)esp指針的位置,這里的指令是add esp, 44h,將臨時(shí)開(kāi)辟的棧空間釋放掉(這里的釋放只是改變寄存器的值,其中的數(shù)據(jù)并未清除掉),其中44h也是與入口處的44h對(duì)應(yīng)的。從入口和出口改變esp寄存器的情況可以看出,棧的方向是由高地址向低地址方向延伸的,開(kāi)辟空間是將esp做減法操作。mov esp, ebp/pop ebp是恢復(fù)棧幀,retn就返回上層函數(shù)了。在該反匯編代碼中還有一步?jīng)]有講到,也就是cmp ebp, esp/call __chkesp,這兩句是對(duì)__chkesp函數(shù)的一個(gè)調(diào)用。在Debug方式下編譯,對(duì)幾乎所有的函數(shù)調(diào)用完成后都會(huì)調(diào)用一次__chkesp。該函數(shù)的功能是用來(lái)檢查棧是否平衡,以保證程序的正確性。如果棧不平,會(huì)給出錯(cuò)誤提示。這里做個(gè)簡(jiǎn)單的測(cè)試,在主函數(shù)的return語(yǔ)句前加一條內(nèi)聯(lián)匯編__asm push ebx(只要是改變esp或ebp寄存器值的操作都可以達(dá)到效果),然后編譯連接運(yùn)行,在輸出后會(huì)看到一個(gè)錯(cuò)誤的提示,如圖7所示。

圖7  調(diào)用__chkesp后對(duì)棧平衡進(jìn)行檢查后的出錯(cuò)提示

圖7就是__chkesp函數(shù)在檢測(cè)到ebp與esp不平時(shí)給出的提示框。該功能只在DEBUG版本中存在。

主函數(shù)的反匯編代碼中還有一部分沒(méi)有介紹,反匯編代碼如下: 

  1. .text:004010B8 push 6  
  2. .text:004010BA push offset aHello ; "hello"  
  3. .text:004010BF call j__test  
  4. .text:004010C4 add esp, 8  
  5. .text:004010C7 mov [ebp+var_4], eax  
  6. .text:004010CA mov eax, [ebp+var_4]  
  7. .text:004010CD push eax  
  8. .text:004010CE push offset aD ; "%d \r\n"  
  9. .text:004010D3 call _printf  
  10. .text:004010D8 add esp, 8 
  11. .text:004010DB xor eax, eax 

首先幾條反匯編代碼是push 6/push offset aHello/call j_test/add esp, 8/mov [ebp+var_ 4], eax,這幾條反匯編代碼是主函數(shù)對(duì)test()函數(shù)的調(diào)用。函數(shù)參數(shù)的傳遞可以選擇寄存器或者內(nèi)存。由于寄存器數(shù)量有限,幾乎大部分函數(shù)調(diào)用都是通過(guò)內(nèi)存進(jìn)行傳遞的。當(dāng)參數(shù)使用完成后,需要把參數(shù)所使用的內(nèi)存進(jìn)行回收。對(duì)于VC開(kāi)發(fā)環(huán)境而言,其默認(rèn)的調(diào)用約定方式是cdecl。這種函數(shù)調(diào)用約定對(duì)參數(shù)的傳遞依靠棧內(nèi)存,在調(diào)用函數(shù)前,會(huì)通過(guò)壓棧操作將參數(shù)從右往左依次送入棧中。在C代碼中,對(duì)test()函數(shù)的調(diào)用形式如下: 

  1. int nNum = test("hello", 6); 

而對(duì)應(yīng)的反匯編代碼為push 6 / push offset aHello / call j_test。從壓棧操作的push指令來(lái)看,參數(shù)是從右往左依次入棧的。當(dāng)函數(shù)返回時(shí),需要將參數(shù)使用的空間回收。這里的回收,指的是恢復(fù)esp寄存器的值到函數(shù)調(diào)用前的值。而對(duì)于cdecl調(diào)用方式而言,平衡堆棧的操作是由函數(shù)調(diào)用方來(lái)做的。從上面的反匯編代碼中可以看到反匯編代碼add esp, 8,它是用于平衡堆棧的。該代碼對(duì)應(yīng)的語(yǔ)言為調(diào)用函數(shù)前的兩個(gè)push操作,即函數(shù)參數(shù)入棧的操作。

函數(shù)的返回值通常保存在eax寄存器中,這里的返回值是以return語(yǔ)句來(lái)完成的返回值,并非以參數(shù)接收的返回值。004010C7地址處的反匯編代碼mov [ebp+var_4], eax是將對(duì)j_test調(diào)用后的返回值保存在[ebp + var_4]中,這里的[ebp + var_4]就相當(dāng)于C語(yǔ)言代碼中的nNum變量。逆向分析時(shí),可以在IDA中通過(guò)快捷鍵N來(lái)完成對(duì)var_4的重命名。

在對(duì)j_test調(diào)用完成并將返回值保存在var_4中后,緊接著push eax/push offset aD/call _printf/add esp, 8的反匯編代碼應(yīng)該就不陌生了。而最后面的xor eax, eax這句代碼是將eax進(jìn)行清0。因?yàn)樵贑語(yǔ)言代碼中,main()函數(shù)的返回值為0,即return 0;,因此這里對(duì)eax進(jìn)行了清0操作。

雙擊004010BF地址處的call j__test,會(huì)移到j(luò)_test的函數(shù)跳表處,反匯編代碼如下: 

  1. .text:0040100A j__test proc near ; CODE XREF: _main+1Fp  
  2. .text:0040100A jmp _test  
  3. .text:0040100A j__test endp 

雙擊跳表中的_test,到如下反匯編處: 

  1. .text:00401020 ; int __cdecl test(LPCSTR lpText, int)  
  2. .text:00401020 _test proc near ; CODE XREF: j__testj  
  3. .text:00401020  
  4. .text:00401020 var_40 = byte ptr -40h  
  5. .text:00401020 lpText = dword ptr 8  
  6. .text:00401020 arg_4 = dword ptr 0Ch  
  7. .text:00401020  
  8. .text:00401020 push ebp  
  9. .text:00401021 mov ebp, esp  
  10. .text:00401023 sub esp, 40h  
  11. .text:00401026 push ebx  
  12. .text:00401027 push esi  
  13. .text:00401028 push edi  
  14. .text:00401029 lea edi, [ebp+var_40]  
  15. .text:0040102C mov ecx, 10h  
  16. .text:00401031 mov eax, 0CCCCCCCCh  
  17. .text:00401036 rep stosd  
  18. .text:00401038 mov eax, [ebp+arg_4]  
  19. .text:0040103B push eax  
  20. .text:0040103C mov ecx, [ebp+lpText]  
  21. .text:0040103F push ecx  
  22. .text:00401040 push offset Format ; "%s, %d \r\n"  
  23. .text:00401045 call _printf  
  24. .text:0040104A add esp, 0Ch  
  25. .text:0040104D mov esi, esp  
  26. .text:0040104F push 0 ; uType  
  27. .text:00401051 push 0 ; lpCaption  
  28. .text:00401053 mov edx, [ebp+lpText]  
  29. .text:00401056 push edx ; lpText  
  30. .text:00401057 push 0 ; hWnd  
  31. .text:00401059 call ds:__imp__MessageBoxA@16 ; MessageBoxA(x,x,x,x)  
  32. .text:0040105F cmp esi, esp  
  33. .text:00401061 call __chkesp  
  34. .text:00401066 mov eax, 5 
  35. .text:0040106B pop edi  
  36. .text:0040106C pop esi 
  37. .text:0040106D pop ebx  
  38. .text:0040106E add esp, 40h  
  39. .text:00401071 cmp ebp, esp  
  40. .text:00401073 call __chkesp  
  41. .text:00401078 mov esp, ebp  
  42. .text:0040107A pop ebp  
  43. .text:0040107B retn  
  44. .text:0040107B _test endp 

該反匯編代碼的開(kāi)頭部分和結(jié)尾部分,這里不再重復(fù),主要看一下中間的反匯編代碼部分。中間的部分主要是printf()函數(shù)和MessageBoxA()函數(shù)的反匯編代碼。

調(diào)用printf()函數(shù)的反匯編代碼如下: 

  1. .text:00401038 mov eax, [ebp+arg_4]  
  2. .text:0040103B push eax  
  3. .text:0040103C mov ecx, [ebp+lpText]  
  4. .text:0040103F push ecx  
  5. .text:00401040 push offset Format ; "%s, %d \r\n"  
  6. .text:00401045 call _printf  
  7. .text:0040104A add esp, 0Ch 

調(diào)用MessageBoxA()函數(shù)的反匯編代碼如下: 

  1. .text:0040104F push 0 ; uType  
  2. .text:00401051 push 0 ; lpCaption  
  3. .text:00401053 mov edx, [ebp+lpText] 
  4. .text:00401056 push edx ; lpText  
  5. .text:00401057 push 0 ; hWnd  
  6. .text:00401059 call ds:__imp__MessageBoxA@16 ; MessageBoxA(x,x,x,x) 

比較以上簡(jiǎn)單的兩段代碼會(huì)發(fā)現(xiàn)很多不同之處,首先在調(diào)用完_printf后會(huì)有add esp, 0Ch的代碼進(jìn)行平衡堆棧,而調(diào)用MessageBoxA后沒(méi)有。為什么對(duì)MessageBoxA函數(shù)的調(diào)用則沒(méi)有呢?原因在于,在Windows系統(tǒng)下,對(duì)API函數(shù)的調(diào)用都遵循的函數(shù)調(diào)用約定是stdcall。對(duì)于stdcall這種調(diào)用約定而言,參數(shù)依然是從右往左依次被送入堆棧,而參數(shù)的平棧是在API函數(shù)內(nèi)完成的,而不是在函數(shù)的調(diào)用方完成的。在OD中看一下MessageBoxA函數(shù)在返回時(shí)的平棧方式,如圖8所示。

圖8  MessageBoxA函數(shù)的平棧操作

從圖8中可以看出,MessageBoxA函數(shù)在調(diào)用retn指令后跟了一個(gè)10。這里的10是一個(gè)16進(jìn)制數(shù),16進(jìn)制的10等于10進(jìn)制的16。而在為MessageBoxA傳遞參數(shù)時(shí),每個(gè)參數(shù)是4字節(jié),4個(gè)參數(shù)等于16字節(jié),因此retn 10除了有返回的作用外,還包含了add esp, 10的作用。

上面兩段反匯編代碼中除了平衡堆棧的不同外,還有另外一個(gè)明顯的區(qū)別。在調(diào)用printf時(shí)的指令為call _printf,而調(diào)用MessageBoxA時(shí)的指令為call ds:__imp__MessageBoxA@16。printf()函數(shù)在stdio.h頭文件中,該函數(shù)屬于C語(yǔ)言的靜態(tài)庫(kù),在連接時(shí)會(huì)將其代碼連接入二進(jìn)制文件中。而MessageBoxA函數(shù)的實(shí)現(xiàn)在user32.dll這個(gè)動(dòng)態(tài)連接庫(kù)中。在代碼中,這里只留了進(jìn)入MessageBoxA函數(shù)的一個(gè)地址,并沒(méi)有具體的代碼。MessageBoxA的具體地址存放在數(shù)據(jù)節(jié)中,因此在反匯編代碼中給出了提示,使用了前綴“ds:”。“__imp__”表示導(dǎo)入函數(shù)。MessageBoxA后面的“@16”表示該API函數(shù)有4個(gè)參數(shù),即16 / 4 = 4。

多參的API函數(shù)仍然在調(diào)用方進(jìn)行平棧,比如wsprintf()函數(shù)。原因在于,被調(diào)用的函數(shù)無(wú)法具體明確調(diào)用方會(huì)傳遞幾個(gè)參數(shù),因此多參函數(shù)無(wú)法在函數(shù)內(nèi)完成參數(shù)的堆棧平衡工作。

stdcall是Windows下的標(biāo)準(zhǔn)函數(shù)調(diào)用約定。Windows提供的應(yīng)用層及內(nèi)核層函數(shù)均使用stdcall的調(diào)用約定方式。cdecl是C語(yǔ)言的調(diào)用函數(shù)約定方式。

3. 結(jié)語(yǔ)

在逆向分析函數(shù)時(shí),首先需要確定函數(shù)的起始位置,這通常會(huì)由IDA自動(dòng)進(jìn)行識(shí)別(識(shí)別不準(zhǔn)確的話,就只能手動(dòng)識(shí)別了);其次需要掌握函數(shù)的調(diào)用約定和確定函數(shù)的參數(shù)個(gè)數(shù),確定函數(shù)的調(diào)用約定和參數(shù)個(gè)數(shù)都是通過(guò)平棧的方式和平棧時(shí)對(duì)esp操作的值來(lái)進(jìn)行判斷的;最后就是觀察函數(shù)的返回值,這部分通常就是觀察eax的值,由于return通常只返回布爾類(lèi)型、數(shù)值類(lèi)型相關(guān)的值,因此通過(guò)觀察eax的值可以確定返回值的類(lèi)型,確定了返回值的類(lèi)型后,可以進(jìn)一步考慮函數(shù)調(diào)用方下一步的動(dòng)作。 

 

責(zé)任編輯:龐桂玉 來(lái)源: 計(jì)算機(jī)與網(wǎng)絡(luò)安全
相關(guān)推薦

2021-04-14 15:53:58

網(wǎng)絡(luò)安全C語(yǔ)言wcslen

2021-04-13 11:15:54

網(wǎng)絡(luò)安全C語(yǔ)言循環(huán)結(jié)構(gòu)

2021-04-08 11:10:22

網(wǎng)絡(luò)安全C語(yǔ)言if…else…

2021-03-31 11:35:00

網(wǎng)絡(luò)安全OllyDbg分析工具

2021-05-08 11:50:59

網(wǎng)絡(luò)安全API函數(shù)代碼

2021-04-01 10:40:22

網(wǎng)絡(luò)安全軟件

2016-10-10 00:18:27

2011-03-17 13:32:45

2021-03-24 09:46:46

網(wǎng)絡(luò)安全軟件反匯編

2021-05-24 11:55:55

網(wǎng)絡(luò)安全Windows鉤子函數(shù)

2021-03-03 12:20:42

網(wǎng)絡(luò)安全DLL編程

2021-12-28 00:11:40

網(wǎng)絡(luò)安全攻擊

2021-02-07 10:55:01

網(wǎng)絡(luò)安全文件API

2022-01-05 00:05:07

安全設(shè)備網(wǎng)絡(luò)

2021-04-30 18:50:44

網(wǎng)絡(luò)安全PE編程添加節(jié)區(qū)

2022-10-08 07:30:17

網(wǎng)絡(luò)安全編程語(yǔ)言C++

2021-01-26 13:45:03

網(wǎng)絡(luò)安全Winsock編程

2021-03-05 13:46:56

網(wǎng)絡(luò)安全遠(yuǎn)程線程

2021-05-21 12:52:47

網(wǎng)絡(luò)安全Android App虛擬機(jī)

2021-04-26 10:32:38

網(wǎng)絡(luò)安全PE編程工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

国产精品久久久久久久久免费看| 欧美日韩在线观看一区二区 | 日韩制服诱惑| 久久精品一区二区三区不卡牛牛| 国产美女精品视频| 国产系列精品av| 激情五月综合| 精品少妇一区二区三区| 欧美视频免费播放| 菠萝蜜视频国产在线播放| 成人短视频下载| 国产精品视频一| 日韩手机在线观看| 亚洲九九视频| 亚洲欧美日韩精品久久亚洲区| gai在线观看免费高清| 国产激情在线播放| 中文字幕佐山爱一区二区免费| 久久99影院| 99国产精品久久久久久久成人 | 日韩在线观看视频网站| 日本欧美一区二区三区乱码| 欧美黑人一区二区三区| 久久久久久久久福利| 国产成人一二| 欧美一区二区三区四区五区| 国产视频一区二区视频| 九色porny丨首页入口在线| 中文字幕一区二区三区不卡| 欧美一二三四五区| 天天操天天干天天爱| 国产一区二区三区观看| 国产精品亚洲一区二区三区| 欧美h在线观看| 伊人久久亚洲美女图片| 久久精品久久久久久| 自拍偷拍视频亚洲| 精品一区欧美| 亚洲乱码国产乱码精品精天堂| 亚洲视频 中文字幕| 国产一区二区三区免费观看在线 | 亚洲综合精品自拍| 青青草原网站在线观看| 一广人看www在线观看免费视频| 91视频国产观看| 狠狠色噜噜狠狠色综合久 | 亚洲男人7777| 亚洲精品视频大全| 精品资源在线| 亚洲精品国产精品国自产在线| 先锋资源在线视频| 激情视频亚洲| 日韩欧美激情在线| 国产成人精品综合久久久久99| 2020国产精品小视频| 欧美精品色一区二区三区| 中文字幕一区二区三区四区在线视频| 欧美三级精品| 欧美日韩一区二区欧美激情 | 日韩久久中文字幕| 日韩电影一区二区三区四区| 国产日韩高清一区二区三区在线| 日韩av中文字幕在线播放| 午夜不卡久久精品无码免费| 久久精品福利| 亚洲男人的天堂在线| 男人的天堂官网| 欧美成人自拍| 美女撒尿一区二区三区| 久久久全国免费视频| 99精品视频免费| 欧美一级高清免费播放| 一级久久久久久| 国产一区二区在线免费观看| 成人女人免费毛片| 亚欧洲精品视频| 国产女人18毛片水真多成人如厕| 亚洲一区二区免费视频软件合集| free性欧美hd另类精品| 亚洲成人av中文| 亚洲人成无码www久久久| 激情中国色综合| 日韩女优av电影在线观看| 国产xxxx视频| 日韩在线视频精品| 欧美成人黄色小视频| 福利一区二区三区四区| 日韩电影一二三区| 不卡的av一区| 国产高清一级毛片在线不卡| 亚洲人一二三区| 欧美网站免费观看| 亚洲成a人片777777久久| 欧美成人免费网站| 免费一级特黄3大片视频| 中文字幕av亚洲精品一部二部| 97婷婷涩涩精品一区| 啪啪小视频网站| 成人免费视频网站在线观看| 日韩精品欧美专区| xxxx另类黑人| 欧美美女视频在线观看| 动漫美女无遮挡免费| 成人亚洲一区二区| 午夜精品一区二区三区视频免费看 | 精品福利久久久| 欧美国产精品日韩| 亚洲精品国产精品乱码视色| 丁香啪啪综合成人亚洲小说| 亚洲精品中文字幕在线| аⅴ资源天堂资源库在线| 欧美日韩专区在线| 短视频在线观看| 国产精品啊v在线| 91精品久久久久久久久久久久久久 | 伊人久久大香| 亚洲欧美在线免费| 国产精品成人国产乱| 日韩精品一级二级| 九九九热999| 黄页网站在线| 欧美一区2区视频在线观看| 人与嘼交av免费| 亚洲影院免费| 久久99精品久久久久久水蜜桃| 在线免费观看的av| 91精品国产综合久久国产大片| 微拍福利一区二区| 性欧美长视频| 久久av二区| 蜜桃视频动漫在线播放| 欧美大片一区二区三区| 四虎精品免费视频| 精品中文字幕一区二区小辣椒| 日韩hmxxxx| 欧美一级大片| 亚洲天堂av在线播放| 久久青青草原亚洲av无码麻豆| 成人少妇影院yyyy| 18禁网站免费无遮挡无码中文| 日本在线成人| 色综合久久中文字幕综合网小说| 国产精品永久久久久久久久久| 中文字幕的久久| 99热手机在线| 日本道不卡免费一区| 国产精品91一区| www.日韩在线观看| 亚洲综合图片区| 欧美图片自拍偷拍| 在线欧美日韩| 久久99精品久久久久久久青青日本 | 中文字幕av免费专区久久| www.超碰com| 日韩精品久久久久久久电影99爱| 国产精品亚洲欧美导航| 欧美激情午夜| 欧美一区二区三区在| 欧美成人国产精品高潮| 国产suv精品一区二区三区| 国产欧美久久久久| 欧美亚视频在线中文字幕免费| 91成人在线播放| 日韩精品视频无播放器在线看| 色乱码一区二区三区88| 国产精品国产三级国产专业不 | 国产小视频福利在线| 岛国视频午夜一区免费在线观看| 欧美狂猛xxxxx乱大交3| 青青草一区二区三区| 一区不卡字幕| 成人精品动漫一区二区三区| 88xx成人精品| 国产福利免费在线观看| 欧美高清视频一二三区| 久久视频免费看| 久久久国际精品| 午夜一级免费视频| 亚洲国产三级| 亚洲国产精品日韩| 一区二区中文字幕在线观看| 91高清免费视频| 一本一道波多野毛片中文在线| 精品女同一区二区| 久久精品视频5| 亚洲日穴在线视频| 亚洲观看黄色网| 久久99蜜桃精品| 国产不卡一区二区视频| 成人av二区| 成人国产1314www色视频| 国产精品高清乱码在线观看| 麻豆乱码国产一区二区三区| 午夜视频福利在线| 91麻豆精品国产| 亚洲天堂一区在线| 亚洲三级在线观看| 成年人免费观看视频网站| 国产精品一二三四区| 国产成人久久婷婷精品流白浆| 欧美aⅴ99久久黑人专区| 免费在线成人av电影| 视频精品一区| 国产精品夜间视频香蕉| 丝袜老师在线| 欧美激情国产高清| 视频三区在线| 亚洲欧美激情一区| 成人无码一区二区三区| 欧美日韩视频第一区| 日韩av在线播| 亚洲美女视频在线观看| 天堂在线中文视频| 91麻豆国产福利精品| 中文字幕1区2区| 久久91精品久久久久久秒播| 99re在线视频免费观看| 亚洲午夜在线| 在线观看污视频| 久久性感美女视频| 日韩亚洲欧美精品| 啪啪激情综合网| 丁香婷婷久久久综合精品国产| 日韩综合久久| 国产精品成人免费视频| 中文字幕乱码在线播放| 久久免费精品视频| 国产探花视频在线观看| 久久中国妇女中文字幕| 免费在线毛片网站| 最近2019年手机中文字幕| 欧美精品a∨在线观看不卡 | av在线电影网| 国产一区二区黄| 国产高清视频免费最新在线| 亚洲一区av在线播放| 日本人妖在线| 亚洲女人被黑人巨大进入al| 婷婷五月综合激情| 亚洲国产91色在线| 天天干天天舔天天射| 亚洲激情自拍图| 污视频网站免费观看| 亚洲福利精品在线| 亚洲日本香蕉视频| 亚洲免费视频观看| 搞黄视频在线观看| 一区二区三区天堂av| 成全电影播放在线观看国语| 在线观看视频99| 在线观看国产原创自拍视频| 色偷偷av一区二区三区| 黄色免费网站在线| 久久91精品国产91久久跳| 日韩精品亚洲人成在线观看| 久久久久国色av免费观看性色| 成人福利影视| 91成人在线观看国产| free欧美| 成人精品视频在线| 在线综合色站| 久久精品国产第一区二区三区最新章节 | 日韩在线视频国产| aaa大片在线观看| 久久久综合免费视频| 国产精品迅雷| 国产日韩在线免费| 视频一区国产| 国模精品娜娜一二三区| 欧美美女在线观看| 中日韩在线视频| 欧美日韩国产精品一区二区亚洲| 精品国产一区二区三区无码| 亚洲综合日韩| 中文字幕国产免费| 成人亚洲精品久久久久软件| 国产一卡二卡三卡四卡| 久久午夜电影网| 久草手机视频在线观看| 亚洲一级二级三级| 超碰在线观看91| 日韩亚洲欧美在线| 你懂的视频在线观看| 播播国产欧美激情| 僵尸再翻生在线观看| 国产精品入口尤物| 国产精品巨作av| 亚洲蜜桃av| 日韩午夜黄色| 夜夜夜夜夜夜操| 91久色porny| 性欧美videos| 日本韩国一区二区三区视频| www.av日韩| 中文字幕久热精品在线视频| 久久亚洲资源| 国产日本欧美视频| 亚洲免费福利一区| 日本a级片在线播放| 青青国产91久久久久久| 国产性生活毛片| 亚洲欧美中日韩| 伊人中文字幕在线观看| 日韩一区二区三区精品视频| 久热av在线| 国外成人在线播放| 国产在线一区不卡| 日韩一二三区不卡在线视频| 亚洲调教视频在线观看| www.99r| 国产日韩欧美精品电影三级在线| 国产一级片免费看| 538prom精品视频线放| 精品国产黄色片| 在线观看中文字幕亚洲| 日本网站在线免费观看视频| 日本电影亚洲天堂| 国产伦理久久久久久妇女| 欧美日韩一级在线| 免费观看一级特黄欧美大片| 国产亚洲色婷婷久久99精品91| 亚洲综合色网站| 国产人妖一区二区三区| 在线精品国产成人综合| 波多视频一区| 久久精品日产第一区二区三区乱码| 国产综合视频| 伊人av在线播放| 亚洲欧美激情在线| 国产精品久久久久久无人区| 尤物tv国产一区| 天堂久久午夜av| 日韩精品另类天天更新| 日韩av在线发布| 国产免费看av| 色婷婷久久久综合中文字幕| 性感美女福利视频| 秋霞午夜一区二区| 杨幂一区二区三区免费看视频| 国产中文字幕在线免费观看| 99综合电影在线视频| 日韩福利片在线观看| 亚洲国产精品女人久久久| 爱情岛亚洲播放路线| 国产一区二区三区无遮挡| 中文欧美日韩| 国产精品无码一区二区三区免费| 欧美性感美女h网站在线观看免费| 香蕉久久一区二区三区| 欧美在线性视频| 欧美日韩精品一区二区视频| 天天干在线影院| 亚洲欧美怡红院| www.色视频| 久久综合九色九九| 粉嫩精品导航导航| 日韩免费一级视频| 国产色产综合色产在线视频 | 亚洲精品国产精品国产| 欧美精品一区在线| 欧美aaa在线| 538任你躁在线精品视频网站| 日韩欧美一区二区视频| аⅴ资源天堂资源库在线| 欧美精品与人动性物交免费看| 日韩精品电影在线观看| 男人的午夜天堂| 精品日韩成人av| 欧美粗大gay| 在线天堂一区av电影| 国产成人在线免费| 91国产丝袜播放在线| 伊人久久久久久久久久久久久| 亚洲综合资源| 国产午夜福利100集发布| 国产亚洲精久久久久久| 国产精品毛片久久久久久久av| 欧美极品美女电影一区| 国产探花一区二区| 日本一二三四区视频| 狠狠做深爱婷婷久久综合一区| 经典三级在线| 99久re热视频这里只有精品6| 奶水喷射视频一区| 极品魔鬼身材女神啪啪精品| 日韩大陆毛片av| 色狠狠一区二区三区| 日本中文字幕网址| 国产精品久久久久久久久晋中| 亚洲男女视频在线观看| 国产精品欧美日韩久久| 国产主播精品| 毛片视频免费播放| 亚洲精品国产精品乱码不99按摩 | 日韩av影院在线观看| 日韩国产一二三区| 国产99久久九九精品无码| 成人免费在线播放视频| 牛牛澡牛牛爽一区二区| 999视频在线免费观看|