C代码+汇编 C的 函数汇编学习分析 rep stos dword ptr [edi]

如分析有误,请在评论区中,指出 谢谢合作
主要是分析C的函数调用在汇编中的执行流程

本章主要是说一下函数调用时堆栈的变化,重点理解部分

C代码

#include"stdafx.h"
int plus(int x,int y){
	return x+y;
}

int main()
{
plus(0,1);
	return 0;
}

汇编代码 VC6.0++ 反编译所得 (如分析有误,请在评论区提出,谢谢)

//函数部分
0040100A   jmp         plus (00401010)	 //跳转到 00401010内存位置
0040100F   int         3				//无视
00401010   push        ebp     	//ebp压栈
00401011   mov         ebp,esp  // esp的值 赋给 ebp
00401013   sub         esp,40h  //esp = esp-40H    //esp寄存器是栈首位置,相当于提升堆栈,开辟
00401016   push        ebx       //压栈
00401017   push        esi			 //压栈
00401018   push        edi      //压栈
00401019   lea         edi,[ebp-40h]   //将ebp的内存地址 - 40H 后所得的内存地址 赋值给 edi寄存器
0040101C   mov         ecx,10h    将 10H (16)   赋给 ecx寄存器
00401021   mov         eax,0CCCCCCCCh   将 ,0CCCCCCCCh 赋值给eax
00401026   rep stos    dword ptr [edi]     //循环 ecx的次数
00401028   mov         eax,dword ptr [ebp+8]  //将ebp+8的内存地址的值 赋给 eax
0040102B   add         eax,dword ptr [ebp+0Ch]  // eax = eax + (ebp+0Ch)的值
0040102E   pop         edi    //弹出栈
0040102F   pop         esi		 //弹出栈
00401030   pop         ebx   //弹出栈
00401031   mov         esp,ebp  //恢复堆栈
00401033   pop         ebp  //弹出栈底
00401034   ret     //相当于 pop eip

//main函数部分    
0040B76E   push        1    //压栈     因为C函数的调用约定 '__stdcall',  plus(0,1);    参数从右向左压入堆栈
0040B770   push        0   //压栈   
0040B772   call        @ILT+5(plus) (0040100a)
0040B777   add         esp,8

在这里插入图片描述

一步一步来,这里的话 需要画图哈.别急

前置条件:

堆栈的特点是 前大后小(内存地址) 本章是 32位寄存器 所以一次性 4
main函数中, (栈首)esp的寄存器的值为 0019FEF4 , (栈尾)ebp的寄存器的值为 0019FF40
自绘图
在这里插入图片描述
汇编图
在这里插入图片描述

代码逻辑执行开始,主要是堆栈的变

------1

0040B76E   push        1    //压栈     因为C函数的调用约定 '__stdcall',  plus(0,1);    参数从右向左压入堆栈
0040B770   push        0   //压栈   
0040B772   call        @ILT+5(plus) (0040100a)0040B777
0040B777   add         esp,8

push压栈时: 修改的 esp(栈顶的值) -4
执行前三句:结果如图
前两句应该很好理解
call指令,在指令执行的时候,会把call的内存地址 下一行的内存地址 压入堆栈中
在这里插入图片描述
--------2

0040100A   jmp         plus (00401010)	 //跳转到 00401010内存位置
0040100F   int         3				//无视
00401010   push        ebp     	//ebp压栈

第一行跳转,不用多说
第二行无视
第三行: push ebp//将堆栈的底,压入到内存地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190811004105562.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1MzQ5OTgy,size_16,color_FFFFFF,t_70

00401011   mov         ebp,esp  
00401013   sub         esp,40h  // 40h = 64(十进制)

第一句 ebp = esp,含义为 执行这个函数开辟内存
在这里插入图片描述
第二局 esp的内存地址 减去40H = 19FEA4‬ 个人见解:提升堆栈 40H=64(十进制) 因为32寄存器 堆栈为4 所以就是提升 64/4= 16
在这里插入图片描述

00401016   push        ebx       //压栈
00401017   push        esi			 //压栈
00401018   push        edi      //压栈
00401019   lea         edi,[ebp-40h]   //将ebp的内存地址 - 40H 后所得的内存地址 赋值给 edi寄存器

前三句没什么好说的
就是压栈
在这里插入图片描述
lea edi,[ebp-40h] //这句话就有点意思了。 ebp-40H就是刚才 esp-40H的地址一样的,将值赋值给EDI

高能从这里开始 就比较有意思了

0040101C   mov         ecx,10h      //重复次数
00401021   mov         eax,0CCCCCCCCh   //eax = 0XCCCCCCCC
00401026   rep stos    dword ptr [edi]    //

第一句 ecx寄存器一般用来计数, 10H = 16(十进制) 跟我们刚才 ESP-40H 的内存地址也是16个
第二句 将0CCCCCCCCh 赋值给EAX寄存器
第三句 rep stos 【repeat(重复) store(保存) string】
指令解析

rep 重复前缀指令,英文缩写 repeat,每执行一次, ecx 减 1,直到 ecx 减至0,重复执行结束

stos 串存储指令,英文缩写 store string ,将 eax 中的数据传送到目的地址(目的地址默认edi寄存器),
以下两条指令相当于一条 stos 指令

  mov [edi], eax
  add edi, 4       ;或者 sub edi, 4 
  // 至于到底是加 4 还是减 4 ,是由方向标志 DF 来决定,可以由指令 cld 和 std 指令设置 
  cld: 从低地址往高地址传送 
  std: 从高地址往低地址传送

首次执行的汇编图
为 es:[
10H = 16(十进制) 所以rep就是执行16次,所得结果如图
汇编图:
在这里插入图片描述自绘图
在这里插入图片描述

00401028   mov         eax,dword ptr [ebp+8]
0040102B   add         eax,dword ptr [ebp+0Ch]

自绘图可以明显看到 ebp+8 与 ebp+0ch 也就是函数传参的两个值
也就是C代码 x+y;

0040102E   pop         edi    //弹出栈
0040102F   pop         esi		 //弹出栈
00401030   pop         ebx   //弹出栈

这三句都是出栈, 注意出栈后esp的栈顶指针会+4
这三句的自绘图,这时esp的栈顶指针的内存地址为 19FEA4
在这里插入图片描述

这三句是本章重点

00401031   mov         esp,ebp
00401033   pop         ebp
00401034   ret

自绘图,这三句执行前的堆栈图
在这里插入图片描述

mov   esp,ebp

当执行 的时候,堆栈的 栈顶与栈尾在同一个位置
在这里插入图片描述

00401033   pop         ebp

因为执行pop 所以 esp的栈顶指针(0019FEE4)+4 = (0019FEE8)
因为 pop弹出的是堆栈 所以 ebp的值会回到 上一次的位置 也就是 0019FF40(有疑问请看第一张自绘图,这里比较难理解,多看几遍,建议手画一画图)

 ret  //pop eip

ret指令用栈中的数据,修改偏移地址,从而实现近转移 就相当于 pop eip
所以ESP栈顶指针(0019FEE8) +4 = (0019FEEC),这时的堆栈图
在这里插入图片描述

0040B777   add         esp,8

//esp (0019FEEC)+8 = 0019 FEF4‬
这句就是函数的堆栈平衡,也就是操作数据时,开辟堆栈内存.用完了以后又恢复到 调用函数之前的位置:
最后的自绘图
在这里插入图片描述

总结:

1.调用函数的时候,堆栈会为函数执行开辟内存,

00401011   mov         ebp,esp
00401013   sub         esp,40h

2.调用函数时 用的是Call指令,并且会开辟空间,把函数的参数压栈,并且把后一个eip地址也压栈
特点:
1.会先将ebp栈底指针压栈,还有一些寄存器 首要特点push ebp ,mov ebp,esp
2.Call函数后面的那一句 一般是堆栈平衡的代码

//mai部分
0040B76E   push        1
0040B770   push        0
0040B772   call        @ILT+5(plus) (0040100a)
//函数部分
00401010   push        ebp
00401011   mov         ebp,esp
00401013   sub         esp,40h
00401016   push        ebx
00401017   push        esi
00401018   push        edi
00401019   lea         edi,[ebp-40h]

整理不易,花了好几个小时,有任何问题请在评论区指出,喜欢的话,点个关注或者喜欢哈

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值