zoukankan      html  css  js  c++  java
  • 结合VC6讲下对调用约定的理解

      调用约定(Calling Convertions)定义了程序中调用函数的方式,决定了在函数调用时数据在堆栈中的组织方式。如参数的压栈顺序和堆栈清理工作。这里结合VC 6.0,根据具体的小程序讲解三种调用约定:cdecl,stdcall,fastcall。(还有一些其他的调用约定,俺就浅尝辄止了)

      首先在VC6.0中创建一个项目,程序代码如下:

    View Code
    1#include <stdio.h>
    2#include <string.h>
    3
    4void
    5age_print(
    6 char* name,
    7 int age
    8)
    9{
    10 printf("%s is %d years old.\n", name, age);
    11}
    12
    13int main(void)
    14{
    15 char name[128];
    16 int age;
    17
    18 strcpy(name, "joe");
    19 age = 100;
    20
    21 age_print(name, age);
    22
    23 return 0;
    24}

      如何在VC6中设置调用约定呢?Project->Setting->C/C++,在category中选择Code Generation。可以看到在下面出现一个下拉框,名称是Calling convention,即可选择调用约定的方式,支持上面讲的三种方式:cdecl,stdcall,fastcall。

          我们通过VC的调试方式看每种调用约定时堆栈的情况。

    1、cdecl

    此种方式下的汇编代码如下所示:

    View Code
    118: strcpy(name, "joe");
    20040108E push offset string "joe" (00422038)
    300401093 lea eax,[ebp-80h]
    400401096 push eax
    500401097 call strcpy (004011b0)
    60040109C add esp,8
    719: age = 100;
    80040109F mov dword ptr [ebp-84h],64h
    920:
    1021: age_print(name, age);
    11004010A9 mov ecx,dword ptr [ebp-84h]
    12004010AF push ecx
    13004010B0 lea edx,[ebp-80h]
    14004010B3 push edx
    15004010B4 call @ILT+0(_age_print) (00401005)
    16004010B9 add esp,8

      由strcpy(name, "joe");对应的汇编代码,可见name的值为ebp-80h;

      由age=100;对应的汇编代码可见age的地址为ebp-84h。

      执行到age_print(name,age)时,对应的汇编代码是首先push age,然后push name,由此可见cdecl方式下,参数是从右到左进行进栈的。最后通过call调用age_print,age_print对应的汇编语言具体如下:

    View Code
    14: void
    25: age_print(
    36: char* name,
    47: int age
    58: )
    69: {
    700401020 push ebp
    800401021 mov ebp,esp
    900401023 sub esp,40h
    1000401026 push ebx
    1100401027 push esi
    1200401028 push edi
    1300401029 lea edi,[ebp-40h]
    140040102C mov ecx,10h
    1500401031 mov eax,0CCCCCCCCh
    1600401036 rep stos dword ptr [edi]
    1710: printf("%s is %d years old.\n", name, age);
    1800401038 mov eax,dword ptr [ebp+0Ch]
    190040103B push eax
    200040103C mov ecx,dword ptr [ebp+8]
    210040103F push ecx
    2200401040 push offset string "%s is %d years old.\n" (0042201c)
    2300401045 call printf (004010f0)
    240040104A add esp,0Ch
    2511: }
    260040104D pop edi
    270040104E pop esi
    280040104F pop ebx
    2900401050 add esp,40h
    3000401053 cmp ebp,esp
    3100401055 call __chkesp (00401170)
    320040105A mov esp,ebp
    330040105C pop ebp
    340040105D ret

      由最后可以看到直接执行了ret,这说明age_print函数本身没有进行堆栈的清理工作,同时,我们可以在main的汇编代码中看到,在执行了call之后,执行了add  esp,8指令,这说明清理了两个参数的空间,由此可见cdecl的堆栈清理工作是由调用函数进行的。

      由此我们总结出cdecl调用约定的规则:参数进栈顺序是从右到左的,堆栈的清理工作是由调用者进行清理的。

      对于参数的进栈顺序,在《Reversing Secrets of Reverse Engineering》中作者是在APPEND C中这么描述的,

          The first parameter is pushed onto the stack first, and the last parameter is pushed last.

      但是结果和作者所说相反,查了下wiki:

      The cdecl calling convention is used by many C systems for the x86 architecture.[1] In cdecl, function parameters are pushed on the stack in a right-to-left order.

    http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl

      好吧,这个问题到此结束。

    2、fastcall

      fastcall顾名思义是快速调用的意思。为什么叫这个名字?让我们来看相应的汇编代码就了然了。直接给出main函数调用age_print对应的汇编代码:

    View Code
    1 21: age_print(name, age);
    2  004010A9 mov edx,dword ptr [ebp-84h]
    3  004010AF lea ecx,[ebp-80h]
    4  004010B2 call @ILT+10(_age_print) (0040100f)

      由此可见,参数并没有进行压栈操作,而只是传递给了edx和ecx寄存器,直接使用寄存器进行存储参数,大家应该明白为什么fast了吧,fastcall的约定通常是使用ecx和edx分别接受第一个和第二个参数。

       对于stdcall,有兴趣的可以自己验证下,呵呵,就写这么多吧。

  • 相关阅读:
    帝国 标签模板 使用程序代码 去除html标记 并 截取字符串
    iis6 伪静态 iis配置方法 【图解】
    您来自的链接不存在 帝国CMS
    帝国cms Warning: Cannot modify header information headers already sent by...错误【解决方法】
    .fr域名注册 51元注册.fr域名
    帝国网站管理系统 恢复栏目目录 建立目录不成功!请检查目录权限 Godaddy Windows 主机
    星外虚拟主机管理平台 开通数据库 出现Microsoft OLE DB Provider for SQL Server 错误 '8004' 从字符串向 datetime 转换失败
    ASP.NET 自定义控件学习研究
    CSS层叠样式表之CSS解析机制的优先级
    ASP.NET程序员工作面试网络收藏夹
  • 原文地址:https://www.cnblogs.com/nocode/p/2005799.html
Copyright © 2011-2022 走看看