用汇编的角度学C

我首先看一下vs2013,简单写的C代码:

void cainiao2022(int a, int b)
{
    int c = a + b;
}

void main()
{
    cainiao2022(1, 2);
}
 

一个简单的有2个形参的cainiao2022()函数,接着在main()主函数调用cainiao2022()函数

然后我们debug看看,F10单步,然后右击鼠标,查看汇编。

void    main()
7:    {
009E1400   push     ebp        ;保存ebp,   执行这句前ESP = 0x003bfb78,EBP = 0x003bfbc4
                    ;push的结果是esp总减少,执行后ESP = 0x003bfb74,EBP = 0x003bfbc4
009E1401   mov   ebp,esp        ;将esp放入ebp中此时ebp和esp相同,即执行后ESP = EBP = 0x003bfb74
;原EBP值已经被压栈(位于栈顶),而新的EBP又恰恰指向栈顶。
;此时EBP寄存器就已经处于一个非常重要的地位,该寄存器中存储着栈中的一个地址(原EBP入栈后的栈顶),
;从该地址为基准,向上(栈底方向)能获取返回地址、参数值,向下(栈顶方向)能获取函数局部变量值,
;而该地址处又存储着上一层函数调用时的EBP值!

------------------------------------------------------------------------------

009E1403   sub         esp,0C0h       ;把esp往上移动一个范围
                    ;等于在栈中空出一片空间来存局部变量
                    ;执行这句后ESP = 0x003bfab4
009E1409   push        ebx        ;下面3句都是保存3个寄存器
009E140A   push        esi
009E140B   push        edi
009E140C   lea         edi,[ebp-0C0h]    ;把ebp-40h加载到edi中,目的是保存局部变量的区域
009E1412   mov         ecx,10h
009E1417   mov         eax,0CCCCCCCCh    ;从ebp-0C0h开始的区域初始化成全部0CCCCCCCCh,就是int3断点
009E141C   rep stos    dword ptr [edi]    ;拷贝字符串,初始化局部变量空间

;以上的语句就是在栈中开辟一块空间放局部变量
;然后把这块空间都初始化为0CCCCCCCCh,就是int3断点,一个中断指令。
;因为局部变量不可能被执行,执行了就会出错,这时候发生中断提示开发者。
;到时候我们可以用od很直观的看到

------------------------------------------------------------------------------

8:        cainiao2022(1,2);
009E141E      push   2      ;参数2入栈,执行前ESP = 0x003bfaa8,执行后ESP = 0x003bfb74
00401420   push        1            ;参数1入栈,执行后ESP = 0x003bfaa4
00401422   call        cainiao2022 (0FC108Ch)    ;调用cainiao2022()函数,可以按F11进
00401427   add         esp,8            ;调用完函数后恢复/释放栈,执行后ESP = 0x003bfaa8


;其实call 指令调用一个过程, 但它有一个小动作
;在参数入栈以后, 被调用函数执行 之前, 它会将当前函数的下一条指令地址, 即EIP的值压入

;在 c/c++ 中, 函数的默认调用约定为 cdecl, 它约定参数从右到左入栈, 
;由调用者清理堆栈, 所谓清理, 即调整ESP的值, 使得原来的局部数据不再属于栈


------------------------------------------------------------------------------

9:    }

0040142A  xor        eax , eax;   
0040142C   pop         edi            ;下面3句都是恢复寄存器,上面怎样push,这里就要对应反过来pop
0040142D   pop         esi            ;简单来说就是先进来最后才出去,最后进来的先出去
0040142E   pop         ebx
0040142F   add         esp,0C0h            ;恢复esp,对应上面的sub esp,0C0h
00401435   cmp         ebp,esp            ;检查esp是否恢复正常,不正常就进入下面的call里面debug
00401437   call        __chkesp (OFC113Bh)    ;处理可能出现的堆栈错误(如果出错,将陷入debug)。
0040143C   mov         esp,ebp            ;将栈顶指针放回esp
0040143E   pop         ebp            ;恢复原来的ebp和esp,让上一个调用的函数正常使用
0040143F   ret                    ;将返回地址存入EIP, 转移流程

下面我们看看cainiao2022()有那些不同就行了

------------------------------------------------------------------------------

1:    void    cainiao2022(int a,int b)
2:    {
00FC13C0   push        ebp
00FC13C1   mov         ebp,esp
00FC13C3   sub         esp,0CCh
00FC13C9   push        ebx
00FC13CA   push        esi
00FC13CB   push        edi
00FC13CC   lea         edi,[ebp-0CCh]
00FC13D2   mov         ecx,33h
00FC13D7   mov         eax,0CCCCCCCCh
00FC13DC   rep stos    dword ptr es:[edi]

;上面的代码跟前面介绍过的几乎一样了

------------------------------------------------------------------------------

3:        int c=a+b;
00FC13DE   mov         eax,dword ptr [a]    ;取第一个参数a放在eax里面 ,这个a的值在ebp+8
00FC13E1   add         eax,dword ptr [b]    ;取第二个参数b,加上a的值放在eax中,这个b的值在ebp+0Ch
00FC13E4   mov         dword ptr [c],eax    ;将最终结果放在c中,应该在ebp-8

;一般而言,ss:[ebp+4]处为返回地址
;ss:[ebp+8]处为第一个参数值(这里是a),ss:[ebp+0Ch]处为第二个参数(这里是b,这里8+4=12=0Ch)
;ss:[ebp-8]处为第一个局部变量(这里是c),ss:[ebp]处为上一层EBP值
;ebp和函数返回值是32位,所以占4个字节


------------------------------------------------------------------------------

4:    }
00FC13E7   pop         edi
00FC13E8   pop         esi
00FC13E9   pop         ebx
00FC13EA   mov         esp,ebp
00FC13EC   pop         ebp
00FC13ED   ret


;恢复性的操作,只是比前面少了检查esp的语句而已

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟来了2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值