[C]通过反汇编看函数栈帧

1.前言

代码使用C语言,编译器为VS2017

汇编、栈、寄存器这些概念严格来说都不是C语言的内容,但了解这些概念对于学习与工作百利而无一害。本文对这些概念只进行最低限度的介绍,不会详细讲解。

*若编译器版本不同,反汇编结果通常也会有些不同,但基本规则是相通的。

1.1基本概念:汇编、栈、寄存器

本小节的概念与定义来自网络。 1

【汇编语言】:汇编语言是二进制指令的文本形式,与指令是一一对应的关系。比如,加法指令00000011写成汇编语言就是 ADD。只要还原成二进制,汇编语言就可以被 CPU 直接执行,所以它是最底层的低级语言。

【栈】:简单说,栈(Stack)是由于函数运行而临时占用的内存区域。内存中栈是从高地址向低地址延伸的。C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。

【寄存器】:寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。32 位 CPU 的寄存器大小是4个字节。早期的 x86CPU 有8个寄存器,其中 esp 寄存器 保存当前 Stack 的地址。

1.2 C代码

int add(fa, fb) {
	return fa+fb;
}

int main() {
	int a = 1;
	int b = 2;
	int c = 0;
	c = add(a, b);
	return 0;
}

1.3:add函数的反汇编代码及注释

int add(fa, fb) {
005E16C0  push        ebp  //栈底指针ebp入栈
005E16C1  mov         ebp,esp  //栈顶指针esp的值赋给栈底指针ebp
005E16C3  sub         esp,0C0h  //将esp的值减0C0h(移动esp),为函数栈帧分配空间
005E16C9  push        ebx  
005E16CA  push        esi  
005E16CB  push        edi  
//↑ebx,esi,edi入栈
005E16CC  lea         edi,[ebp-0C0h]  //取[ebp-0C0h]偏移地址
005E16D2  mov         ecx,30h  //将30h赋值给ecx
005E16D7  mov         eax,0CCCCCCCCh  //将0CCCCCCCCh赋值给eax
005E16DC  rep stos    dword ptr es:[edi]  //eax赋值给edi所指地址,edi增加4个字节,Rep使指令重复执行ecx中保存的次数
//↑栈帧初始化为0CCCCCCCCh
	return fa+fb;
005E16DE  mov         eax,dword ptr [fa]  //把fa的内存地址中的双字型数据赋给eax
005E16E1  add         eax,dword ptr [fb]  //把fb的内存地址中的双字型数据加给eax
}
//↑操作结束
005E16E4  pop         edi  
005E16E5  pop         esi  
005E16E6  pop         ebx  
//↑edi,esi,ebx出栈
005E16E7  mov         esp,ebp  //恢复esp栈顶指针
005E16E9  pop         ebp  //恢复ebp栈底指针
005E16EA  ret  			  //ret是函数返回指令

1.4: main函数的反汇编代码及注释

int main() {
005E1700  push        ebp  //栈底指针ebp入栈
//↑不知为何VS2017下这条会单独拎出来,VS2019看着并没有这个情况
int main() {
005E1701  mov         ebp,esp  //栈顶指针esp的值赋给栈底指针ebp
005E1703  sub         esp,0E4h  //将esp的值减0E4h(移动esp),为函数栈帧分配空间
005E1709  push        ebx  
005E170A  push        esi  
005E170B  push        edi  
//↑ebx,esi,edi入栈
005E170C  lea         edi,[ebp-0E4h]  
005E1712  mov         ecx,39h  
005E1717  mov         eax,0CCCCCCCCh  
005E171C  rep stos    dword ptr es:[edi]  
//↑栈帧初始化为0CCCCCCCCh
	int a = 1;
005E171E  mov         dword ptr [a],1  
	int b = 2;
005E1725  mov         dword ptr [b],2  
	int c = 0;
005E172C  mov         dword ptr [c],0  
//↑a、b、c的创建和初始化
	c = add(a, b);
005E1733  mov         eax,dword ptr [b]  //&b指向的内容赋值给eax
005E1736  push        eax  //eax入栈
005E1737  mov         ecx,dword ptr [a]  //&a指向的内容赋值给ecx
005E173A  push        ecx  //ecx入栈
005E173B  call        _add (05E1118h)  //执行流跳转到add
005E1740  add         esp,8  //在栈顶开辟空间并放入call的下一条指令的地址
005E1743  mov         dword ptr [c],eax  //把eax赋值给c的内存地址中的双字型数据
	return 0;
005E1746  xor         eax,eax  //通过与自身异或将寄存器eax清零
}
005E1748  pop         edi  
005E1749  pop         esi  
005E174A  pop         ebx  
//↑edi,esi,ebx出栈
005E174B  add         esp,0E4h 
005E1751  cmp         ebp,esp  
005E1753  call        __RTC_CheckEsp (05E1122h)  
005E1758  mov         esp,ebp  
005E175A  pop         ebp  
005E175B  ret  
//↑main函数结束

1.5:示意图

图图


  1. 参考链接:汇编语言入门教程-阮一峰栈帧 - 百度百科寄存器 - 百度百科 ↩︎

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值