生活随笔
收集整理的這篇文章主要介紹了
C/C++拾遗录--关于一个C语言小程序的分析
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
雖然編了幾年程序,但是對于程序到底是什么規(guī)則變成匯編代碼的,在這里搞了一個小程序。用VC查看了一下匯編代碼。在此之前先介紹一下關于函數(shù)運行是堆棧變化的細節(jié)。
在高級語言編寫程序時,函數(shù)的調(diào)用是很常見的事情,但是在函數(shù)調(diào)用過程中堆棧的變化通常有幾個細節(jié):
1.父函數(shù)將函數(shù)的實參按照從右至左的順序壓入堆棧;
2.CPU將父函數(shù)中函數(shù)調(diào)用指令Call的下一條指令地址EIP壓入堆棧;
3.父函數(shù)通過Push Ebp指令將基址指針EBP的值壓入堆棧,并通過Mov Ebp,Esp指令將當前堆棧指針Esp值傳給Ebp;
4.通過Sub Esp,m(m是字節(jié)數(shù))指令可以為存放函數(shù)中的局部變量開辟內(nèi)存。函數(shù)在執(zhí)行的時候如果需要訪問實參或局部變量,都可以通過EBP指針來指引完成。
windows系統(tǒng)下常用的函數(shù)調(diào)用通常有種,__cdecl和__stdCall。
1.在VC、.net等開發(fā)環(huán)境中,編寫命令行程序時的Main或者_tmain函數(shù),以及大家自己定義的很多函數(shù)都是默認采用__cdecl調(diào)用方式;
2.通過MFC編寫圖形界面程序的時候,其主函數(shù)聲明為extern "C" int WINAPI tWinMain(參數(shù)),該函數(shù)的調(diào)用約定是__stdCall。WINAPI和PASCAL等都是__stdCall的宏定義,是一個意思,此外,大家平時調(diào)用的API函數(shù),絕大多數(shù)都是采用__staCall的調(diào)用方式;
3.__cdecl調(diào)用方式的函數(shù),父函數(shù)在調(diào)用子函數(shù)的時候,先將子函數(shù)的實參按照從右至左的順序壓入堆棧中,子函數(shù)返回后,父函數(shù)通過Sub Esp,n(n=函數(shù)實參個數(shù)*4)指令來恢復堆棧;
4.__stdCall調(diào)用約定函數(shù),子函數(shù)調(diào)用時實參入棧順序也是從左到右,但是堆棧恢復是子函數(shù)返回時自己通過Ret n指令來完成的。
下邊就是針對這些知識進行的部分實踐:
[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)????//進入fun函數(shù)?? @ILT+5(_main):?? 0040100A???jmp?????????main?(00401080)????//進入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???????????????????//保存三個寄存器的值。?? 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)????//先后壓入棧中兩個地址,nTest,一個是一個字符串指針。?? 00401041???call????????printf?(004011d0)????????????????????????????//調(diào)用printf函數(shù)時,它會自動做到堆棧平衡。?? 00401046???add?????????esp,8????????????????????????????????????????//由于剛才壓入和兩個參數(shù),所以在這里手動將兩個參數(shù)彈出堆棧?? 8:????????strcpy(szBuf,szIn);?? 00401049???mov?????????ecx,dword?ptr?[ebp+8]????????????????????????? 0040104C???push????????ecx???????????????????????????//壓入szIn的指針。這個參數(shù)在高出基址的8位處,也就是調(diào)用該函數(shù)前壓入棧中的。?? 0040104D???lea?????????edx,[ebp-0Ch]?? 00401050???push????????edx?????????????????//壓入szBuf的指針,這個函數(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)?//檢驗是否在用戶自定義匯編代碼中修改了ebp和esp的相對關系。一般情況下EBP>ESP?? 00401068???mov?????????esp,ebp?????????????//將原基址恢復給棧頂寄存器。?? 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個寄存器???? 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?????????//將字符串通過寄存器將字符拷貝到分配的空間中。?? 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????????????????????????//恢復堆棧?? 15:???????return?0;?? 004010BA???xor?????????eax,eax??????????????????????//返回值在EAX中?? 16:???}?? 004010BC???pop?????????edi?? 004010BD???pop?????????esi?? 004010BE???pop?????????ebx??????????????????????????//恢復3個寄存器?? 004010BF???add?????????esp,48h??????????????????????//清除局部變量空間?? 004010C2???cmp?????????ebp,esp?? 004010C4???call????????__chkesp?(00401250)??????????//檢測堆棧指針與堆棧基址????? 004010C9???mov?????????esp,ebp??????????????????????//恢復調(diào)用函數(shù)的棧頂?? 004010CB???pop?????????ebp??????????????????????????//恢復調(diào)用函數(shù)的堆棧基址?? 004010CC???ret??????????????????????????????????????//函數(shù)返回?? ---?No?source?file??--------------------------------------------------------------------------------------------------------------------------------------------------?? 004010CD???int?????????3?? 004010CE???int?????????3??
關于這個程序的堆棧使用情況也做了一下分析,如圖:
?
總結
以上是生活随笔 為你收集整理的C/C++拾遗录--关于一个C语言小程序的分析 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。