虽然编了几年程序,但是对于程序到底是什么规则变成汇编代码的,在这里搞了一个小程序。用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调用约定函数,子函数调用时实参入栈顺序也是从左到右,但是堆栈恢复是子函数返回时自己通过Ret n指令来完成的。
下边就是针对这些知识进行的部分实践:
- #include<stdio.h>
- #include<windows.h>
- #include<stdlib.h>
- intfun(char*szIn,intnTest)
- {
- charszBuf[9];
- printf("%d\n",nTest);
- strcpy(szBuf,szIn);
- return0;
- }
- intmain(intargc,char*argv[])
- {
- charsz_In[]="1234567";
- fun(sz_In,888);
- return0;
- }
汇编代码
- 00401003int3
- 00401004int3
- @ILT+0(?fun@@YAHPADH@Z):
- 00401005jmpfun(00401020)//进入fun函数
- @ILT+5(_main):
- 0040100Ajmpmain(00401080)//进入main函数,该位置是整段代码的入口
- 0040100Fint3
- 00401010int3
- 00401011int3
- 00401012int3
- 00401013int3
- 00401014int3
- 00401015int3
- 00401016int3
- 00401017int3
- 00401018int3
- 00401019int3
- 0040101Aint3
- 0040101Bint3
- 0040101Cint3
- 0040101Dint3
- 0040101Eint3
- 0040101Fint3
- ---c:\project\heap1\heap1.cpp--------------------------------------------------------------------------------------------------------------------------------------
- 1:#include<stdio.h>
- 2:#include<windows.h>
- 3:#include<stdlib.h>
- 4:intfun(char*szIn,intnTest)
- 5:{
- 00401020pushebp
- 00401021movebp,esp//保存基址指针,并将现在的栈顶保存为基址指针。
- 00401023subesp,4Ch//腾出一部分堆栈区用于存放局部变量。
- 00401026pushebx
- 00401027pushesi
- 00401028pushedi//保存三个寄存器的值。
- 00401029leaedi,[ebp-4Ch]
- 0040102Cmovecx,13h
- 00401031moveax,0CCCCCCCCh
- 00401036repstosdwordptr[edi]//将腾出的4Ch的空间初始化值为0xCC。
- 6:charszBuf[9];
- 7:printf("%d\n",nTest);
- 00401038moveax,dwordptr[ebp+0Ch]
- 0040103Bpusheax
- 0040103Cpushoffsetstring"%d\n"(0042201c)//先后压入栈中两个地址,nTest,一个是一个字符串指针。
- 00401041callprintf(004011d0)//调用printf函数时,它会自动做到堆栈平衡。
- 00401046addesp,8//由于刚才压入和两个参数,所以在这里手动将两个参数弹出堆栈
- 8:strcpy(szBuf,szIn);
- 00401049movecx,dwordptr[ebp+8]
- 0040104Cpushecx//压入szIn的指针。这个参数在高出基址的8位处,也就是调用该函数前压入栈中的。
- 0040104Dleaedx,[ebp-0Ch]
- 00401050pushedx//压入szBuf的指针,这个函数在低于基址的OCh位处,这是调用函数后分配的。局部变量的分配大
- 00401051callstrcpy(004010e0)//小都是按4的倍数分配的,所以尽管szBuf[9]但是也分配在了0Ch处。
- 00401056addesp,8
- 9:return0;
- 00401059xoreax,eax//返回值在EAX中。
- 10:}
- 0040105Bpopedi
- 0040105Cpopesi
- 0040105Dpopebx//弹出保存的数据。
- 0040105Eaddesp,4Ch//消除为局部变量腾出的空间。
- 00401061cmpebp,esp
- 00401063call__chkesp(00401250)//检验是否在用户自定义汇编代码中修改了ebp和esp的相对关系。一般情况下EBP>ESP
- 00401068movesp,ebp//将原基址恢复给栈顶寄存器。
- 0040106Apopebp//弹出原调用函数的堆栈基址。
- 0040106Bret//函数返回。
- ---Nosourcefile--------------------------------------------------------------------------------------------------------------------------------------------------
- 0040106Cint3
- 0040106Dint3
- 0040106Eint3
- 0040106Fint3
- 00401070int3
- 00401071int3
- 00401072int3
- 00401073int3
- 00401074int3
- 00401075int3
- 00401076int3
- 00401077int3
- 00401078int3
- 00401079int3
- 0040107Aint3
- 0040107Bint3
- 0040107Cint3
- 0040107Dint3
- 0040107Eint3
- 0040107Fint3
- ---c:\project\heap1\heap1.cpp--------------------------------------------------------------------------------------------------------------------------------------
- 11:intmain(intargc,char*argv[])
- 12:{
- 00401080pushebp
- 00401081movebp,esp//保存堆栈基址
- 00401083subesp,48h//腾出局部变量空间
- 00401086pushebx
- 00401087pushesi
- 00401088pushedi//保存3个寄存器
- 00401089leaedi,[ebp-48h]
- 0040108Cmovecx,12h
- 00401091moveax,0CCCCCCCCh//初始化局部变量空间
- 00401096repstosdwordptr[edi]
- 13:charsz_In[]="1234567";
- 00401098moveax,[string"1234567"(00422020)]
- 0040109Dmovdwordptr[ebp-8],eax
- 004010A0movecx,dwordptr[string"1234567"+4(00422024)]
- 004010A6movdwordptr[ebp-4],ecx//将字符串通过寄存器将字符拷贝到分配的空间中。
- 14:fun(sz_In,888);
- 004010A9push378h
- 004010AEleaedx,[ebp-8]
- 004010B1pushedx//从右至左将参数压入堆栈中,数字直接压入数值,字符串则压入字符串指针
- 004010B2call@ILT+0(fun)(00401005)
- 004010B7addesp,8//恢复堆栈
- 15:return0;
- 004010BAxoreax,eax//返回值在EAX中
- 16:}
- 004010BCpopedi
- 004010BDpopesi
- 004010BEpopebx//恢复3个寄存器
- 004010BFaddesp,48h//清除局部变量空间
- 004010C2cmpebp,esp
- 004010C4call__chkesp(00401250)//检测堆栈指针与堆栈基址
- 004010C9movesp,ebp//恢复调用函数的栈顶
- 004010CBpopebp//恢复调用函数的堆栈基址
- 004010CCret//函数返回
- ---Nosourcefile--------------------------------------------------------------------------------------------------------------------------------------------------
- 004010CDint3
- 004010CEint3
关于这个程序的堆栈使用情况也做了一下分析,如图:
分享到:
相关推荐
C语言版的实现HMAC-SHA1和base64编码,已经对C++做了兼容处理,在VS下运行main.c代码,可以得到经过HMAC-SHA1处理后的结果,并且可以运行里面的base64编码函数得到想要的结果,可以用于连接阿里云MQTT
总之,"C/C++完美演绎-源代码"是一个很好的学习资源,通过深入研究和实践,你将能够提升自己的编程技艺,理解和掌握这两门语言的精髓,从而在IT领域取得更大的成就。记得,学习编程不仅仅是学习语法,更重要的是理解...
用C语言实现multipart/form-data文件上传,没有用到curl之类的库。之前做个小的日志上传程序写的。
5. **调试工具**:Dev-C++集成了GDB(GNU Debugger),一个强大的源码级调试器,可以帮助开发者查找并解决程序中的错误。用户可以通过设置断点、单步执行、查看变量值等方式来调试程序。 6. **可扩展性**:Dev-C++...
学生
C语言/C++基础之爱心程序源码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福
官方介绍:原名《Turbo C/C++ for Windows 集成实验与学习环境》,支持最新操作系统WINDOWS 7,它是从事一线教学的大学教师根据C/C++ 初学者的特点,量身定制的一个简单易用的 C/C++程序设计学习与实验软件(支持TC2/...
C语言/C++基础之冰墩墩源码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福
c语言/c++/qt图形界面
C语言/C++基础之爱心源码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福
#二维码(QRcode)生成算法 C语言/C++ 源码 1. 根据输入字符串识别编码模式; 2. 根据输入字符串长度选择合适的QRcode版本; 3. 将编码转换为二进制位流表示为数据码字; 4. 使用多项式生成纠错码; 5. 将数据码和...
C语言/C++基础之跨年烟花代码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福
本资源提供了一个使用C语言和C++编写的计算器的源代码,适用于学习者进行参考和修改。 1. C语言基础: C语言是一种强大的、低级的编程语言,被广泛用于系统编程、嵌入式开发等领域。在C语言中,我们通常使用结构化...
C语言/C++基础之跨年烟花代码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福
原名《Turbo C/C++ for Windows 集成实验与学习环境》,现已更名为《C/C++程序设计学习与实验系统》,现在已全面支持最新操作系统VISTA,它由从事一线教学的大学教师根据C/C++初学者的特点,量身定制的一个简单易用的...
不错的c语言c++语言课程设计项目--学生成绩管理系统 不错的c语言c++语言课程设计项目--学生成绩管理系统 不错的c语言c++语言课程设计项目--学生成绩管理系统 不错的c语言c++语言课程设计项目--学生成绩管理系统 不错...
PC-Lint 能够检查出很多语法错误和语法上正确的逻辑错误,PC-Lint 为大部分错误消息都分配了一个错误号,编号小于1000的错误号是分配给C 语言的,编号大于1000的错误号则用来说明C++的错误消息。
本资源“c/c++中文帮助文档(API)”为开发者提供了一个全面的参考,帮助他们理解和使用C和C++的各种库函数。 C库函数主要包括I/O操作、内存管理、字符串处理、数学运算、时间日期处理等。例如,`stdio.h`提供了...
此外,文件操作也是C语言中一个重要的知识点,能实现数据的读写。 "C++"的自学则需要理解面向对象编程(OOP)的核心概念,如类、对象、封装、继承、多态。在C++中,模板和异常处理也是重要特性。"进阶"阶段则涉及...