`
xpp02
  • 浏览: 1010879 次
社区版块
存档分类
最新评论

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调用约定函数,子函数调用时实参入栈顺序也是从左到右,但是堆栈恢复是子函数返回时自己通过Ret n指令来完成的。

下边就是针对这些知识进行的部分实践:

  1. #include<stdio.h>
  2. #include<windows.h>
  3. #include<stdlib.h>
  4. intfun(char*szIn,intnTest)
  5. {
  6. charszBuf[9];
  7. printf("%d\n",nTest);
  8. strcpy(szBuf,szIn);
  9. return0;
  10. }
  11. intmain(intargc,char*argv[])
  12. {
  13. charsz_In[]="1234567";
  14. fun(sz_In,888);
  15. return0;
  16. }

汇编代码

  1. 00401003int3
  2. 00401004int3
  3. @ILT+0(?fun@@YAHPADH@Z):
  4. 00401005jmpfun(00401020)//进入fun函数
  5. @ILT+5(_main):
  6. 0040100Ajmpmain(00401080)//进入main函数,该位置是整段代码的入口
  7. 0040100Fint3
  8. 00401010int3
  9. 00401011int3
  10. 00401012int3
  11. 00401013int3
  12. 00401014int3
  13. 00401015int3
  14. 00401016int3
  15. 00401017int3
  16. 00401018int3
  17. 00401019int3
  18. 0040101Aint3
  19. 0040101Bint3
  20. 0040101Cint3
  21. 0040101Dint3
  22. 0040101Eint3
  23. 0040101Fint3
  24. ---c:\project\heap1\heap1.cpp--------------------------------------------------------------------------------------------------------------------------------------
  25. 1:#include<stdio.h>
  26. 2:#include<windows.h>
  27. 3:#include<stdlib.h>
  28. 4:intfun(char*szIn,intnTest)
  29. 5:{
  30. 00401020pushebp
  31. 00401021movebp,esp//保存基址指针,并将现在的栈顶保存为基址指针。
  32. 00401023subesp,4Ch//腾出一部分堆栈区用于存放局部变量。
  33. 00401026pushebx
  34. 00401027pushesi
  35. 00401028pushedi//保存三个寄存器的值。
  36. 00401029leaedi,[ebp-4Ch]
  37. 0040102Cmovecx,13h
  38. 00401031moveax,0CCCCCCCCh
  39. 00401036repstosdwordptr[edi]//将腾出的4Ch的空间初始化值为0xCC。
  40. 6:charszBuf[9];
  41. 7:printf("%d\n",nTest);
  42. 00401038moveax,dwordptr[ebp+0Ch]
  43. 0040103Bpusheax
  44. 0040103Cpushoffsetstring"%d\n"(0042201c)//先后压入栈中两个地址,nTest,一个是一个字符串指针。
  45. 00401041callprintf(004011d0)//调用printf函数时,它会自动做到堆栈平衡。
  46. 00401046addesp,8//由于刚才压入和两个参数,所以在这里手动将两个参数弹出堆栈
  47. 8:strcpy(szBuf,szIn);
  48. 00401049movecx,dwordptr[ebp+8]
  49. 0040104Cpushecx//压入szIn的指针。这个参数在高出基址的8位处,也就是调用该函数前压入栈中的。
  50. 0040104Dleaedx,[ebp-0Ch]
  51. 00401050pushedx//压入szBuf的指针,这个函数在低于基址的OCh位处,这是调用函数后分配的。局部变量的分配大
  52. 00401051callstrcpy(004010e0)//小都是按4的倍数分配的,所以尽管szBuf[9]但是也分配在了0Ch处。
  53. 00401056addesp,8
  54. 9:return0;
  55. 00401059xoreax,eax//返回值在EAX中。
  56. 10:}
  57. 0040105Bpopedi
  58. 0040105Cpopesi
  59. 0040105Dpopebx//弹出保存的数据。
  60. 0040105Eaddesp,4Ch//消除为局部变量腾出的空间。
  61. 00401061cmpebp,esp
  62. 00401063call__chkesp(00401250)//检验是否在用户自定义汇编代码中修改了ebp和esp的相对关系。一般情况下EBP>ESP
  63. 00401068movesp,ebp//将原基址恢复给栈顶寄存器。
  64. 0040106Apopebp//弹出原调用函数的堆栈基址。
  65. 0040106Bret//函数返回。
  66. ---Nosourcefile--------------------------------------------------------------------------------------------------------------------------------------------------
  67. 0040106Cint3
  68. 0040106Dint3
  69. 0040106Eint3
  70. 0040106Fint3
  71. 00401070int3
  72. 00401071int3
  73. 00401072int3
  74. 00401073int3
  75. 00401074int3
  76. 00401075int3
  77. 00401076int3
  78. 00401077int3
  79. 00401078int3
  80. 00401079int3
  81. 0040107Aint3
  82. 0040107Bint3
  83. 0040107Cint3
  84. 0040107Dint3
  85. 0040107Eint3
  86. 0040107Fint3
  87. ---c:\project\heap1\heap1.cpp--------------------------------------------------------------------------------------------------------------------------------------
  88. 11:intmain(intargc,char*argv[])
  89. 12:{
  90. 00401080pushebp
  91. 00401081movebp,esp//保存堆栈基址
  92. 00401083subesp,48h//腾出局部变量空间
  93. 00401086pushebx
  94. 00401087pushesi
  95. 00401088pushedi//保存3个寄存器
  96. 00401089leaedi,[ebp-48h]
  97. 0040108Cmovecx,12h
  98. 00401091moveax,0CCCCCCCCh//初始化局部变量空间
  99. 00401096repstosdwordptr[edi]
  100. 13:charsz_In[]="1234567";
  101. 00401098moveax,[string"1234567"(00422020)]
  102. 0040109Dmovdwordptr[ebp-8],eax
  103. 004010A0movecx,dwordptr[string"1234567"+4(00422024)]
  104. 004010A6movdwordptr[ebp-4],ecx//将字符串通过寄存器将字符拷贝到分配的空间中。
  105. 14:fun(sz_In,888);
  106. 004010A9push378h
  107. 004010AEleaedx,[ebp-8]
  108. 004010B1pushedx//从右至左将参数压入堆栈中,数字直接压入数值,字符串则压入字符串指针
  109. 004010B2call@ILT+0(fun)(00401005)
  110. 004010B7addesp,8//恢复堆栈
  111. 15:return0;
  112. 004010BAxoreax,eax//返回值在EAX中
  113. 16:}
  114. 004010BCpopedi
  115. 004010BDpopesi
  116. 004010BEpopebx//恢复3个寄存器
  117. 004010BFaddesp,48h//清除局部变量空间
  118. 004010C2cmpebp,esp
  119. 004010C4call__chkesp(00401250)//检测堆栈指针与堆栈基址
  120. 004010C9movesp,ebp//恢复调用函数的栈顶
  121. 004010CBpopebp//恢复调用函数的堆栈基址
  122. 004010CCret//函数返回
  123. ---Nosourcefile--------------------------------------------------------------------------------------------------------------------------------------------------
  124. 004010CDint3
  125. 004010CEint3

关于这个程序的堆栈使用情况也做了一下分析,如图:

分享到:
评论

相关推荐

    c/c++编译器c-free适合初学者

    c-free是一款集c语言和c++编译功能于一体的适合广大初学者的编译器,希望大家喜欢

    C/C++实现HMAC-SHA1和base64编码

    C语言版的实现HMAC-SHA1和base64编码,已经对C++做了兼容处理,在VS下运行main.c代码,可以得到经过HMAC-SHA1处理后的结果,并且可以运行里面的base64编码函数得到想要的结果,可以用于连接阿里云MQTT

    C语言/C++集成开发环境 Dev-C++

    C语言/C++集成开发环境 Dev-C++。一款优秀的C/C++集成开发软件。

    用C语言编写multipart/form-data实现上传文件

    用C语言实现multipart/form-data文件上传,没有用到curl之类的库。之前做个小的日志上传程序写的。

    编译原理课程设计 词法分析 C语言/c++版

    编译原理课程设计之一用编程语言实现词法分析,用C++实现 注释清楚详细,程序风格良好 /*目前实现的功能有: */ /* 0.课程要求的词法分析基本功能 */ /* 1.识别用户定义的初次定义的变量还是已经定义的变量还是错误...

    C/C++设计模式-2

    C语言面向对象编程,C语言设计模式,C++设计模式

    Dev-cpp5.4.0及API帮助文档 2018年蓝桥杯C语言/c++

    Dev-cpp5.4.0及API帮助文档 2018年蓝桥杯C语言/c++ 需要的同学可以下载使用

    C/C++编程规范--北京软通动力信息技术有限公司

    C/C++编程规范,来自—北京软通动力信息技术有限公司

    C语言/C++ 烟花表白代码

    C语言/C++ 烟花表白代码 C语言/C++ 烟花表白代码 C语言/C++ 烟花表白代码 C语言/C++ 烟花表白代码

    c语言版数据结构餐厅订餐程序设计

    学生

    经典的c语言c++语言课程设计项目-图书管理系统.rar

    经典的c语言c++语言课程设计项目--图书管理系统 经典的c语言c++语言课程设计项目--图书管理系统 经典的c语言c++语言课程设计项目--图书管理系统 经典的c语言c++语言课程设计项目--图书管理系统 经典的c语言c++语言...

    C语言/C++基础之爱心程序源码

    C语言/C++基础之爱心程序源码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福

    C/C++编译器 LCC-WIN32

    一个小巧的WIN32平台下C/C++编译器 非常小 语法加亮 学习C语言的好帮手

    c语言/c++/qt图形界面

    c语言/c++/qt图形界面

    二维码(QRcode)生成算法 C语言/C++源码

    #二维码(QRcode)生成算法 C语言/C++ 源码 1. 根据输入字符串识别编码模式; 2. 根据输入字符串长度选择合适的QRcode版本; 3. 将编码转换为二进制位流表示为数据码字; 4. 使用多项式生成纠错码; 5. 将数据码和...

    C语言/C++基础之爱心源码

    C语言/C++基础之爱心源码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福

    C++,C语言教程

    C++面向对象程序设计视频教程》上海交通大学 - - 2013-04-03 C语言程序设计视频教程(曾怡) - - 2013-03-16 C语言程序设计视频教程.徐红波 - - 2013-03-16 C语言视频教程》黑鹰基地 - - 2013-04-21 C语言速成...

    C/C++编程指南--林浩博士

    本PDF详细的讲解了C、c++中很多被人们忽视的小细节,讲解了如何提高程序的,可靠性、移植性、健壮性、可读性,详细的讲解了 内存管理的知识,本人读了之后,受益良多,希望造福他人

    C语言/C++基础之冰墩墩源码

    C语言/C++基础之冰墩墩源码,适合初学C语言/C++的小伙伴学习研究,博客中有对应的讲解和演示,避免走弯路,费时费力。也真心希望能够帮助正在苦学C语言/C++ 程序设计的小伙伴们,你们的成长是我最大的幸福

Global site tag (gtag.js) - Google Analytics