生活随笔
收集整理的這篇文章主要介紹了
C/C++拾遗录--关于一个C语言小程序的分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
雖然編了幾年程序,但是對于程序到底是什么規則變成匯編代碼的,在這里搞了一個小程序。用VC查看了一下匯編代碼。在此之前先介紹一下關于函數運行是堆棧變化的細節。
在高級語言編寫程序時,函數的調用是很常見的事情,但是在函數調用過程中堆棧的變化通常有幾個細節:
1.父函數將函數的實參按照從右至左的順序壓入堆棧;
2.CPU將父函數中函數調用指令Call的下一條指令地址EIP壓入堆棧;
3.父函數通過Push Ebp指令將基址指針EBP的值壓入堆棧,并通過Mov Ebp,Esp指令將當前堆棧指針Esp值傳給Ebp;
4.通過Sub Esp,m(m是字節數)指令可以為存放函數中的局部變量開辟內存。函數在執行的時候如果需要訪問實參或局部變量,都可以通過EBP指針來指引完成。
windows系統下常用的函數調用通常有種,__cdecl和__stdCall。
1.在VC、.net等開發環境中,編寫命令行程序時的Main或者_tmain函數,以及大家自己定義的很多函數都是默認采用__cdecl調用方式;
2.通過MFC編寫圖形界面程序的時候,其主函數聲明為extern "C" int WINAPI tWinMain(參數),該函數的調用約定是__stdCall。WINAPI和PASCAL等都是__stdCall的宏定義,是一個意思,此外,大家平時調用的API函數,絕大多數都是采用__staCall的調用方式;
3.__cdecl調用方式的函數,父函數在調用子函數的時候,先將子函數的實參按照從右至左的順序壓入堆棧中,子函數返回后,父函數通過Sub Esp,n(n=函數實參個數*4)指令來恢復堆棧;
4.__stdCall調用約定函數,子函數調用時實參入棧順序也是從左到右,但是堆?;謴褪亲雍瘮捣祷貢r自己通過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函數?? @ILT+5(_main):?? 0040100A???jmp?????????main?(00401080)????//進入main函數,該位置是整段代碼的入口?? 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???????????????//保存基址指針,并將現在的棧頂保存為基址指針。?? 00401023???sub?????????esp,4Ch???????????????//騰出一部分堆棧區用于存放局部變量。?? 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)????????????????????????????//調用printf函數時,它會自動做到堆棧平衡。?? 00401046???add?????????esp,8????????????????????????????????????????//由于剛才壓入和兩個參數,所以在這里手動將兩個參數彈出堆棧?? 8:????????strcpy(szBuf,szIn);?? 00401049???mov?????????ecx,dword?ptr?[ebp+8]????????????????????????? 0040104C???push????????ecx???????????????????????????//壓入szIn的指針。這個參數在高出基址的8位處,也就是調用該函數前壓入棧中的。?? 0040104D???lea?????????edx,[ebp-0Ch]?? 00401050???push????????edx?????????????????//壓入szBuf的指針,這個函數在低于基址的OCh位處,這是調用函數后分配的。局部變量的分配大?? 00401051???call????????strcpy?(004010e0)???//小都是按4的倍數分配的,所以盡管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?????????????????//彈出保存的數據。?? 0040105E???add?????????esp,4Ch?????????????//消除為局部變量騰出的空間。?? 00401061???cmp?????????ebp,esp?? 00401063???call????????__chkesp?(00401250)?//檢驗是否在用戶自定義匯編代碼中修改了ebp和esp的相對關系。一般情況下EBP>ESP?? 00401068???mov?????????esp,ebp?????????????//將原基址恢復給棧頂寄存器。?? 0040106A???pop?????????ebp?????????????????//彈出原調用函數的堆?;贰?????????????? 0040106B???ret?????????????????????????????//函數返回。??? ---?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??????//從右至左將參數壓入堆棧中,數字直接壓入數值,字符串則壓入字符串指針?? 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??????????????????????//恢復調用函數的棧頂?? 004010CB???pop?????????ebp??????????????????????????//恢復調用函數的堆?;?? 004010CC???ret??????????????????????????????????????//函數返回?? ---?No?source?file??--------------------------------------------------------------------------------------------------------------------------------------------------?? 004010CD???int?????????3?? 004010CE???int?????????3??
關于這個程序的堆棧使用情況也做了一下分析,如圖:
?
總結
以上是生活随笔 為你收集整理的C/C++拾遗录--关于一个C语言小程序的分析 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。