生活随笔
收集整理的這篇文章主要介紹了
C/C++拾遗录--关于一个C语言小程序的分析
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
雖然編了幾年程序,但是對(duì)于程序到底是什么規(guī)則變成匯編代碼的,在這里搞了一個(gè)小程序。用VC查看了一下匯編代碼。在此之前先介紹一下關(guān)于函數(shù)運(yùn)行是堆棧變化的細(xì)節(jié)。
在高級(jí)語(yǔ)言編寫(xiě)程序時(shí),函數(shù)的調(diào)用是很常見(jiàn)的事情,但是在函數(shù)調(diào)用過(guò)程中堆棧的變化通常有幾個(gè)細(xì)節(jié):
1.父函數(shù)將函數(shù)的實(shí)參按照從右至左的順序壓入堆棧;
2.CPU將父函數(shù)中函數(shù)調(diào)用指令Call的下一條指令地址EIP壓入堆棧;
3.父函數(shù)通過(guò)Push Ebp指令將基址指針EBP的值壓入堆棧,并通過(guò)Mov Ebp,Esp指令將當(dāng)前堆棧指針Esp值傳給Ebp;
4.通過(guò)Sub Esp,m(m是字節(jié)數(shù))指令可以為存放函數(shù)中的局部變量開(kāi)辟內(nèi)存。函數(shù)在執(zhí)行的時(shí)候如果需要訪問(wèn)實(shí)參或局部變量,都可以通過(guò)EBP指針來(lái)指引完成。
windows系統(tǒng)下常用的函數(shù)調(diào)用通常有種,__cdecl和__stdCall。
1.在VC、.net等開(kāi)發(fā)環(huán)境中,編寫(xiě)命令行程序時(shí)的Main或者_(dá)tmain函數(shù),以及大家自己定義的很多函數(shù)都是默認(rèn)采用__cdecl調(diào)用方式;
2.通過(guò)MFC編寫(xiě)圖形界面程序的時(shí)候,其主函數(shù)聲明為extern "C" int WINAPI tWinMain(參數(shù)),該函數(shù)的調(diào)用約定是__stdCall。WINAPI和PASCAL等都是__stdCall的宏定義,是一個(gè)意思,此外,大家平時(shí)調(diào)用的API函數(shù),絕大多數(shù)都是采用__staCall的調(diào)用方式;
3.__cdecl調(diào)用方式的函數(shù),父函數(shù)在調(diào)用子函數(shù)的時(shí)候,先將子函數(shù)的實(shí)參按照從右至左的順序壓入堆棧中,子函數(shù)返回后,父函數(shù)通過(guò)Sub Esp,n(n=函數(shù)實(shí)參個(gè)數(shù)*4)指令來(lái)恢復(fù)堆棧;
4.__stdCall調(diào)用約定函數(shù),子函數(shù)調(diào)用時(shí)實(shí)參入棧順序也是從左到右,但是堆棧恢復(fù)是子函數(shù)返回時(shí)自己通過(guò)Ret n指令來(lái)完成的。
下邊就是針對(duì)這些知識(shí)進(jìn)行的部分實(shí)踐:
[cpp] view plaincopy #include<stdio.h> ??#include<windows.h> ??#include<stdlib.h> ??int ?fun(char ?*szIn,?int ?nTest)??{?? ????char ?szBuf[9];?? ????printf("%d\n" ,nTest);?? ????strcpy(szBuf,szIn);?? ????return ?0;?? }?? int ?main(int ?argc,?char ?*argv[])??{?? ????char ?sz_In[]?=?"1234567" ;?? ????fun(sz_In,888);?? ????return ?0;?? }??
匯編代碼
[plain] view plaincopy 00401003???int?????????3?? 00401004???int?????????3?? @ILT+0(?fun@@YAHPADH@Z):?? 00401005???jmp?????????fun?(00401020)????//進(jìn)入fun函數(shù)?? @ILT+5(_main):?? 0040100A???jmp?????????main?(00401080)????//進(jìn)入main函數(shù),該位置是整段代碼的入口?? 0040100F???int?????????3?? 00401010???int?????????3?? 00401011???int?????????3?? 00401012???int?????????3?? 00401013???int?????????3?? 00401014???int?????????3?? 00401015???int?????????3?? 00401016???int?????????3?? 00401017???int?????????3?? 00401018???int?????????3?? 00401019???int?????????3?? 0040101A???int?????????3?? 0040101B???int?????????3?? 0040101C???int?????????3?? 0040101D???int?????????3?? 0040101E???int?????????3?? 0040101F???int?????????3?? ---?c:\project\heap1\heap1.cpp??--------------------------------------------------------------------------------------------------------------------------------------?? 1:????#include<stdio.h>?? 2:????#include<windows.h>?? 3:????#include<stdlib.h>?? 4:????int?fun(char?*szIn,?int?nTest)?? 5:????{?? 00401020???push????????ebp?? 00401021???mov?????????ebp,esp???????????????//保存基址指針,并將現(xiàn)在的棧頂保存為基址指針。?? 00401023???sub?????????esp,4Ch???????????????//騰出一部分堆棧區(qū)用于存放局部變量。?? 00401026???push????????ebx?? 00401027???push????????esi?? 00401028???push????????edi???????????????????//保存三個(gè)寄存器的值。?? 00401029???lea?????????edi,[ebp-4Ch]?? 0040102C???mov?????????ecx,13h?? 00401031???mov?????????eax,0CCCCCCCCh?? 00401036???rep?stos????dword?ptr?[edi]???????//將騰出的4Ch的空間初始化值為0xCC。??? 6:????????char?szBuf[9];?? 7:????????printf("%d\n",nTest);?? 00401038???mov?????????eax,dword?ptr?[ebp+0Ch]?? 0040103B???push????????eax?? 0040103C???push????????offset?string?"%d\n"?(0042201c)????//先后壓入棧中兩個(gè)地址,nTest,一個(gè)是一個(gè)字符串指針。?? 00401041???call????????printf?(004011d0)????????????????????????????//調(diào)用printf函數(shù)時(shí),它會(huì)自動(dòng)做到堆棧平衡。?? 00401046???add?????????esp,8????????????????????????????????????????//由于剛才壓入和兩個(gè)參數(shù),所以在這里手動(dòng)將兩個(gè)參數(shù)彈出堆棧?? 8:????????strcpy(szBuf,szIn);?? 00401049???mov?????????ecx,dword?ptr?[ebp+8]????????????????????????? 0040104C???push????????ecx???????????????????????????//壓入szIn的指針。這個(gè)參數(shù)在高出基址的8位處,也就是調(diào)用該函數(shù)前壓入棧中的。?? 0040104D???lea?????????edx,[ebp-0Ch]?? 00401050???push????????edx?????????????????//壓入szBuf的指針,這個(gè)函數(shù)在低于基址的OCh位處,這是調(diào)用函數(shù)后分配的。局部變量的分配大?? 00401051???call????????strcpy?(004010e0)???//小都是按4的倍數(shù)分配的,所以盡管szBuf[9]但是也分配在了0Ch處。?? 00401056???add?????????esp,8?? 9:????????return?0;?? 00401059???xor?????????eax,eax?????????????//返回值在EAX中。?? 10:???}?? 0040105B???pop?????????edi?? 0040105C???pop?????????esi?? 0040105D???pop?????????ebx?????????????????//彈出保存的數(shù)據(jù)。?? 0040105E???add?????????esp,4Ch?????????????//消除為局部變量騰出的空間。?? 00401061???cmp?????????ebp,esp?? 00401063???call????????__chkesp?(00401250)?//檢驗(yàn)是否在用戶自定義匯編代碼中修改了ebp和esp的相對(duì)關(guān)系。一般情況下EBP>ESP?? 00401068???mov?????????esp,ebp?????????????//將原基址恢復(fù)給棧頂寄存器。?? 0040106A???pop?????????ebp?????????????????//彈出原調(diào)用函數(shù)的堆棧基址。?????????????? 0040106B???ret?????????????????????????????//函數(shù)返回。??? ---?No?source?file??--------------------------------------------------------------------------------------------------------------------------------------------------?? 0040106C???int?????????3?? 0040106D???int?????????3?? 0040106E???int?????????3?? 0040106F???int?????????3?? 00401070???int?????????3?? 00401071???int?????????3?? 00401072???int?????????3?? 00401073???int?????????3?? 00401074???int?????????3?? 00401075???int?????????3?? 00401076???int?????????3?? 00401077???int?????????3?? 00401078???int?????????3?? 00401079???int?????????3?? 0040107A???int?????????3?? 0040107B???int?????????3?? 0040107C???int?????????3?? 0040107D???int?????????3?? 0040107E???int?????????3?? 0040107F???int?????????3?? ---?c:\project\heap1\heap1.cpp??--------------------------------------------------------------------------------------------------------------------------------------?? 11:???int?main(int?argc,?char?*argv[])?? 12:???{?? 00401080???push????????ebp?? 00401081???mov?????????ebp,esp???????????????????????//保存堆棧基址?? 00401083???sub?????????esp,48h???????????????????????//騰出局部變量空間???? 00401086???push????????ebx?? 00401087???push????????esi?? 00401088???push????????edi???????????????????????????//保存3個(gè)寄存器???? 00401089???lea?????????edi,[ebp-48h]?? 0040108C???mov?????????ecx,12h?? 00401091???mov?????????eax,0CCCCCCCCh????????????????//初始化局部變量空間?? 00401096???rep?stos????dword?ptr?[edi]?? 13:???????char?sz_In[]?=?"1234567";?? 00401098???mov?????????eax,[string?"1234567"?(00422020)]?? 0040109D???mov?????????dword?ptr?[ebp-8],eax?? 004010A0???mov?????????ecx,dword?ptr?[string?"1234567"+4?(00422024)]?? 004010A6???mov?????????dword?ptr?[ebp-4],ecx?????????//將字符串通過(guò)寄存器將字符拷貝到分配的空間中。?? 14:???????fun(sz_In,888);?? 004010A9???push????????378h??????????????????????????? 004010AE???lea?????????edx,[ebp-8]?? 004010B1???push????????edx??????//從右至左將參數(shù)壓入堆棧中,數(shù)字直接壓入數(shù)值,字符串則壓入字符串指針?? 004010B2???call????????@ILT+0(fun)?(00401005)?? 004010B7???add?????????esp,8????????????????????????//恢復(fù)堆棧?? 15:???????return?0;?? 004010BA???xor?????????eax,eax??????????????????????//返回值在EAX中?? 16:???}?? 004010BC???pop?????????edi?? 004010BD???pop?????????esi?? 004010BE???pop?????????ebx??????????????????????????//恢復(fù)3個(gè)寄存器?? 004010BF???add?????????esp,48h??????????????????????//清除局部變量空間?? 004010C2???cmp?????????ebp,esp?? 004010C4???call????????__chkesp?(00401250)??????????//檢測(cè)堆棧指針與堆棧基址????? 004010C9???mov?????????esp,ebp??????????????????????//恢復(fù)調(diào)用函數(shù)的棧頂?? 004010CB???pop?????????ebp??????????????????????????//恢復(fù)調(diào)用函數(shù)的堆棧基址?? 004010CC???ret??????????????????????????????????????//函數(shù)返回?? ---?No?source?file??--------------------------------------------------------------------------------------------------------------------------------------------------?? 004010CD???int?????????3?? 004010CE???int?????????3??
關(guān)于這個(gè)程序的堆棧使用情況也做了一下分析,如圖:
?
總結(jié)
以上是生活随笔 為你收集整理的C/C++拾遗录--关于一个C语言小程序的分析 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。