• 函数调用的汇编解释


    最近看了下汇编,主要是想了解下cdecl和stdcall的区别。

    之前没有汇编基础,只知道少许简单的汇编指令,如mov等等。这两天看了若干,总结一下吧,当然只是部分知识点,对我而言已经很受用了。

    1.  cdecl 和 stdcall 的区别(从汇编层面解释)

    其实在选择这两者时,最主要的考虑是可变参数的问题,也就是谁负责清栈。那么从汇编层面是如何反应出来的呢?

    1.1 stdcall 范式

    int __stdcall add(int a, int b)
    {
    002613A0 push ebp      //保存调用者栈底地址
    002613A1 mov ebp,esp   //设置新的被调用者的栈底指针
    002613A3 sub esp,0C0h
    002613A9 push ebx
    002613AA push esi
    002613AB push edi
    002613AC lea edi,[ebp-0C0h]
    002613B2 mov ecx,30h
    002613B7 mov eax,0CCCCCCCCh
    002613BC rep stos dword ptr es:[edi]
    return a+b;
    002613BE mov eax,dword ptr [a]  
    002613C1 add eax,dword ptr [b]
    }
    002613C4 pop edi
    002613C5 pop esi
    002613C6 pop ebx
    002613C7 mov esp,ebp   //将栈顶指针设置为被调用函数帧栈栈底地址
    002613C9 pop ebp       //还原调用者帧栈栈底地址
    002613CA ret 8         //返回,ret 8 等价于 pop EIP; add sp 8。首先,恢复返回后执行指令的地址,然后,参数出栈,这里是两个int,故8个字节。
    //这里就解释了stdcall是被调用者清栈
    int main() { 002613E0 push ebp 002613E1 mov ebp,esp 002613E3 sub esp,0CCh 002613E9 push ebx 002613EA push esi 002613EB push edi 002613EC lea edi,[ebp-0CCh] 002613F2 mov ecx,33h 002613F7 mov eax,0CCCCCCCCh 002613FC rep stos dword ptr es:[edi] int sum; sum = add(1,2); 002613FE push 2 //参数2压栈 00261400 push 1 //参数1压栈 00261402 call add (261109h) //调用函数add 00261407 mov dword ptr [sum],eax return 0; 0026140A xor eax,eax } 0026140C pop edi 0026140D pop esi 0026140E pop ebx 0026140F add esp,0CCh 00261415 cmp ebp,esp 00261417 call @ILT+315(__RTC_CheckEsp) (261140h) 0026141C mov esp,ebp 0026141E pop ebp 0026141F ret

    1.2 cdecl

    int add(int a, int b)
    {
    00E713A0 push ebp
    00E713A1 mov ebp,esp
    00E713A3 sub esp,0C0h
    00E713A9 push ebx
    00E713AA push esi
    00E713AB push edi
    00E713AC lea edi,[ebp-0C0h]
    00E713B2 mov ecx,30h
    00E713B7 mov eax,0CCCCCCCCh
    00E713BC rep stos dword ptr es:[edi]
    return a+b;
    00E713BE mov eax,dword ptr [a]
    00E713C1 add eax,dword ptr [b]
    }
    00E713C4 pop edi
    00E713C5 pop esi
    00E713C6 pop ebx
    00E713C7 mov esp,ebp
    00E713C9 pop ebp
    00E713CA ret       //对比上面那个,不同之处在于ret等价于pop IP。没有后续参数出栈的操作
    
    
    int main()
    {
    00E713E0 push ebp
    00E713E1 mov ebp,esp
    00E713E3 sub esp,0CCh
    00E713E9 push ebx
    00E713EA push esi
    00E713EB push edi
    00E713EC lea edi,[ebp-0CCh]
    00E713F2 mov ecx,33h
    00E713F7 mov eax,0CCCCCCCCh
    00E713FC rep stos dword ptr es:[edi]
    int sum;
    sum = add(1,2);
    00E713FE push 2
    00E71400 push 1
    00E71402 call add (0E71096h)
    00E71407 add esp,8              //看见没有,参数出栈的地方放到了调用者这里。
    00E7140A mov dword ptr [sum],eax
    return 0;
    00E7140D xor eax,eax
    }
    00E7140F pop edi
    00E71410 pop esi
    00E71411 pop ebx
    00E71412 add esp,0CCh
    00E71418 cmp ebp,esp
    00E7141A call @ILT+315(__RTC_CheckEsp) (0E71140h)
    00E7141F mov esp,ebp
    00E71421 pop ebp
    00E71422 ret

    通过上面的注释,应该很清楚了吧~

    2.函数调用时汇编做的事儿

    参见上面代码示例,可以总结下,函数调用时汇编是怎么做的

    1)参数压栈,push &1, push&2...

    2)调用call指令,这里call等价于push EIP,jmp XXX; 如果是lcall,那么等价于push CS, push EIP, jmp XXX,即保存函数返回后下一条指令地址,并转移到被调用函数指令

    3)保存“调用者”栈基地址,push ebp

    4)设置新的“被调用者”的栈基地址,mov ebp, esp

    5)执行函数。。。

    6)栈顶指针指向“被调用者”栈底 mov esp, ebp

    7)还原“调用者”帧栈基址,pop ebp

    8)还原函数返回后下一条指令地址,并根据调用方式决定是否清理参数帧栈,ret或ret n,也等价于pop EIP或 pop EIP, add esp, n。

    很明确了吧?

    附录:

    intel 各个寄存器的用途

    通用:

    EAX(累加器)

    EBX(基址)

    ECX(计数)

    EDX(数据)

    EBP(基指针):为了传送存储器数据,EBP指向存储单元

    EDI(目的地址):寻址指令的目的数据串

    ESI(源变址):寻址指令的源数据串

    专用:

    EIP(指令指针):代码存储区的下一条指令

    ESP(堆栈指针):堆栈

    EFLAGS:指示处理器的状态并控制它的操作(详细了解)奇偶标准以1为标准

    CS(代码段)实模式下:64KB;保护模式下:4GB;

    DS(数据段)实模式下:64KB;保护模式下:4GB;

    ES(附加段)附加的数据段,为某些串指令存放目的数据

    SS(堆栈段)为堆栈定义了一个存储区域,由堆栈段和堆栈指针寄存器确定堆栈段内当前的入口地址,BP也可寻址堆栈段内的数据。

    CS:EIP(CS:IP):代码起点:下一条指令的偏移地址

    SS:ESP(SS:SP):寻址堆栈区:SS+ESP的存储单元,栈顶地址

    SS:EBP(SS:BP):可以理解为子栈区的栈基址

  • 相关阅读:
    Python Django 之 Template 模板语言简介
    Prometheus
    MySQL 存储过程中执行DDL
    Mysql 存储过程-转载
    Mysql 时间类型整理
    MySQL 5.7 分区表性能下降的案例分析
    Mysql 分区表-分区操作
    Mysql 中Left/Right join on后面and和where条件查询的差异-Mysql SQL运算符是有优先级
    MySQL出现Waiting for table metadata lock的原因以及解决方法
    Python 字符串转换为字典(String to Dict)
  • 原文地址:https://www.cnblogs.com/ShaneZhang/p/3711152.html
Copyright © 2020-2023  润新知