StackFrame

栈帧

  1. 栈帧就是利用EBP寄存器访问栈内局部变量、参数、函数返回地址等的手段。
  2. 栈帧对应汇编代码
PUSH EBP
MOV EBP,ESP
...
MOV ESP,EBP
POP EBP
RETN

调试示例:stack frame.exe

  1. Stack Frame.cpp

#include "stdio.h"

long add(long a, long b)
{
    long x = a, y = b;
    return (x + y);
}

int main(int argc, char* argv[])
{
    long a = 1, b = 2;
    
    printf("%d\n", add(a, b));

    return 0;
}
  1. 使用OllyDbg调试工具打开Stack Frame.exe文件,按Ctrl+G快捷键转到401000地址处。
    在这里插入图片描述
  2. 函数main是程序开始执行的地方,在main函数的起始地址(401020)处,按F2键设置一个断点,然后按F9运行程序,程序运行到main函数的断点处暂停。
    在这里插入图片描述
  • 此时ESP的值为0019FF2C,EBP的值为0019FF70,返回地址00401250保存在ESP(0019FF2C)中。
00401020 PUSH EBP
  • 把EBP值压入栈,main函数中EBP为栈帧指针,用来把EBP之前的值备份到栈中。
00401021 MOV EBP,ESP
  • MOV是一条数据传送命令,上面这条MOV语句的命令是把ESP的值传送到EBP。

  • 执行完上面这两条语句,函数main的栈帧就生成了。
    在这里插入图片描述

  • 进入OllyDbg的栈窗口,点击右键Address->Relative to EBP,把地址转换成相对EBP的偏移后,可以看到当前EBP的值为0019FF70,跟ESP的值一样。

  1. 下面开始分析变量声明及赋值语句。
long a = 1, b = 2;
  • main函数中,上述语句用于在栈中为局部变量(a,b)分配空间,并赋初始值。
00401023 SUB ESP,8
  • SUB是汇编语言中的一条减法指令,上面这条语句用来将ESP的值减去8个字节,减去之后ESP的值变为0019FF20,ESP减去8个字节,实质是为函数的局部变量(a与b)开辟空间,以便将它们保存在栈中,由于局部变量a与b都是long型,它们分别占据4个字节,所以需要在栈中开辟8个字节的空间来保存这2个变量。
00401026  |. C745 FC 010000>MOV DWORD PTR SS:[EBP-4],1
0040102D  |. C745 F8 020000>MOV DWORD PTR SS:[EBP-8],2
  • 把数据1与2分别保存到[EBP-4]与[EBP-8]中,即[EBP-4]代表局部变量a,[EBP-8]代表局部变量b。
  1. add函数参数传递与调用
printf("%d\n", add(a,b));
00401034  |. 8B45 F8        MOV EAX,DWORD PTR SS:[EBP-8]
00401037  |. 50             PUSH EAX                                 ; /Arg2
00401038  |. 8B4D FC        MOV ECX,DWORD PTR SS:[EBP-4]             ; |
0040103B  |. 51             PUSH ECX                                 ; |Arg1
0040103C  |. E8 BFFFFFFF    CALL StackFra.00401000                   ; 
  • 地址40103C处为”Call 401000“命令,该命令用于调用401000处的函数,而401000处的函数即为add()函数。
  • 函数add()接收a、b这2个长整型参数,所以调用add()之前需要先把这2个参数压入栈,地址401034~40103B之间的代码即用于此。
  • 执行完00401034~0040103B,栈内情况如下:
    在这里插入图片描述
  • 执行CALL指令进入被掉用的函数之前,CPU会先把函数的返回地址压入栈,用作函数执行完毕后的返回地址,在地址40103C处调用了add()函数,它的下一条命令的地址为401041,函数add()执行完毕后,程序执行流应该返回到401041地址处,该地址即被称为add()函数的返回地址。
    在这里插入图片描述
  1. 开始执行add()函数&生成栈帧
  • 函数开始执行时,栈中会单独生成与其对应的栈帧。
00401000 PUSH EBP
00401001 MOV EBP,ESP
  • 代码与开始执行main()函数时的代码完全相同,先把EBP值保存到栈中,再把当前ESP存储到EBP中,这样函数add()的栈中就生成了。
    在这里插入图片描述
  • main函数使用的EBP的值(0019FF28)被备份到栈中,然后EBP的值被设置为一个新值0019FF10.
  1. 设置add()函数的局部变量(x,y)
00401003 SUB ESP,8
  • 在栈内存中为局部变量x、y开辟8个字节的空间。
00401006  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
00401009  |. 8945 F8        MOV DWORD PTR SS:[EBP-8],EAX
0040100C  |. 8B4D 0C        MOV ECX,DWORD PTR SS:[EBP+C]
0040100F  |. 894D FC        MOV DWORD PTR SS:[EBP-4],ECX
  • [EBP+8]与[EBP+C]分别指向参数a与b,而[EBP-8]与[EBP-4]分别指向add函数的2个局部变量x与y。
  1. ADD运算
00401012  |. 8B45 F8        MOV EAX,DWORD PTR SS:[EBP-8]
  • 变量x的值被传送到EAX中。
00401015  |. 0345 FC        ADD EAX,DWORD PTR SS:[EBP-4]
  • ADD指令为加法指令,上面这条语句中,变量y与EAX原值(x)相加,且运算结果被存储到EAX中,运算完成后EAX中的值为3.
  1. 删除函数add()的栈中&函数执行完毕
  • 执行加法运算后,要返回函数add(),在此之前删除函数add()的栈帧。
00401018 MOV ESP,EBP
  • 上面这条命令是把EBP的值赋给ESP,与地址40100处的MOV EBP,ESP命令向对应。
0040101A POP EBP
  • 回复add函数开始执行时备份到栈中的EBP的值,它与401000地址处的PUSH EBP相对应,EBP值恢复为0019FF28,它是main函数的EBP值,到此,add函数的栈帧就被完全删除了。
    在这里插入图片描述
  • 此时ESP的值为00401041,它是执行CALL 401000命令时CPU存储到栈中的返回地址
0040101B RETN
  • 执行RETN命令,存储在栈中的返回地址即被返回,调用栈已经返回调用add函数之前的状态。
    在这里插入图片描述
  1. 从栈中删除函数add()的参数
  • 程序执行流已经重新返回main函数中。
00401041  |. 83C4 08        ADD ESP,8
  • 0019FF18与0019FF1C处存储的时传递给函数add的参数a与b,函数add执行完毕后,就不再需要参数a与b了,所以要把ESP+8把它们从栈中清理掉,执行完上述命令后,栈内情况。
    在这里插入图片描述
  1. 调用printf()函数
printf("%d\n", add(a,b));
  • 汇编代码如下:
00401044  |. 50             PUSH EAX
00401045  |. 68 84B34000    PUSH StackFra.0040B384              ;  ASCII "%d"
0040104A  |. E8 18000000    CALL StackFra.00401067
0040104F  |. 83C4 08        ADD ESP,8
  • 地址401044处的EAX寄存器中存储着add的返回值,它是执行加法运算后的结果值3,地址40104A处的CALL 401067命令中调用的401067地址处的函数,它是一个C标准库函数printf(),由于上面的printf()函数有2个参数,大小8个字节,所以在0040104F地址使用ADD命令,将ESP加上8个字节,把函数的参数从栈中删除。
  1. 设置返回值
return 0;
  • XOR命令用来进行异或运算,其特点时2个相同的值进行XOR运算,结果为0,XOR命令比MOV EAX,0命令执行速度块,常用于寄存器的初始化操作。
  1. 删除栈帧&main函数终止
  • 最终主函数终止执行,同add()函数一样,其返回前要先从栈中删除与其对应的栈中。
00401020  /$ 55             PUSH EBP
00401021  |. 8BEC           MOV EBP,ESP
  • main函数的栈帧即被删除,且局部变量a、b也不再有效,执行至此,栈内情景如图.
    在这里插入图片描述
00401057  \. C3             RETN
  • 主函数执行完毕返回,程序执行流跳转返回地址00401250处,该地址执行Visual C++的启动函数区域,随后执行进程终止代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值