尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

函数堆栈图

函数堆栈图
📅 发布时间:2026/7/26 11:46:53

进入函数之前

EAX = 0041C077 EBX = 002FA000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 004118B6 ESP = 0019FE04 EBP = 0019FEE8 EFL = 00000246
47:intmain(){00411890push ebp00411891mov ebp,esp00411893sub esp,0D8h00411899push ebx0041189A push esi0041189B push edi0041189C lea edi,[ebp-18h]0041189Fmov ecx,6004118A4 mov eax,0CCCCCCCCh004118A9 rep stos dword ptr es:[edi]004118AB mov ecx,41C077h004118B0 call00411325004118B5 nop48:intstart=0;//断点在此处004118B6 mov dword ptr[ebp-8],049:func();004118BD call004112E9004118C2 nop50:intend=0;004118C3 mov dword ptr[ebp-14h],051:return0;004118CA xor eax,eax52:}
5:voidfunc(){00411820push ebp00411821mov ebp,esp00411823sub esp,0CCh00411829push ebx0041182A push esi0041182B push edi0041182C lea edi,[ebp-0Ch]0041182Fmov ecx,300411834mov eax,0CCCCCCCCh00411839rep stos dword ptr es:[edi]0041183B mov ecx,41C077h00411840call0041132500411845nop6:intnum=0;00411846mov dword ptr[ebp-8],07:num++;0041184D mov eax,dword ptr[ebp-8]00411850add eax,100411853mov dword ptr[ebp-8],eax8:return;9:}

将断点下在 函数汇编的第一行

00411820 push ebp

执行到函数内部第一行汇编处 的 变化

EAX = 0041C077 EBX = 002FA000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 00411820 ESP = 0019FE00 EBP = 0019FEE8 EFL = 00000246

其中 变了的两个寄存器

EIP = 00411820 ESP = 0019FE00

原因:

在计算机架构与汇编语言中,call指令在执行时主要产生以下两个核心效果:

  1. 保存返回地址:
    将call指令下一条指令的地址(即当前IP/EIP/RIP的值)压入栈(Stack)中。这样做的目的是为了在子程序执行完毕后,程序能够通过ret指令返回到调用处继续执行 [1, 2]。
  2. 转移控制权:
    将指令指针寄存器(IP/EIP/RIP)的内容修改为被调用子程序的入口地址。这会导致处理器跳转到目标代码段开始执行任务 [2, 3]。

此时 ESP 的值变小了 ,说明栈被抬升了,因为栈增长的方向是小地址,ESP作为栈顶指针,每次栈填充数据,栈顶指针就要指向下一个位置,
位置的偏移量取决于填充的数据大小(和可能的数据对齐)

此时ESP 减小的大小是4字节

执行push ebp

这个条指令只是把 ebp 的值 压入栈中,并不修改ebp的值

EAX = 0041C077 EBX = 002FA000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 00411821 ESP = 0019FDFC EBP = 0019FEE8 EFL = 00000246

修改的寄存器是

EIP = 00411821 ESP = 0019FDFC

此时ESP 减小的大小是4字节,因为 EBP是4字节大小的

到目前为止,没有改变EBP,现在考虑下EBP的值是什么,EBP存储的是函数的栈底部,相当于一个基准点 ,- 通过EBP可以访问函数的参数和局部变量,- 每个函数都有自己的EBP值,指向自己的栈帧开始位置

在main函数中:

  • [ebp-8]访问局部变量start

  • [ebp-14h]访问局部变量end

  • EBP指向main的栈帧底部

这种相对关系存在于 main 函数中,也就是说,我只需要获取到 EBP的值,就可以获取到其他局部变量的值

现在我在 func 函数中保存了 EBP的值,所以实际上现在 在 func 中是可以通过 EBP 的值去访问 main中存储的局部变量的

执行 mov ebp,esp

EAX = 0041C077 EBX = 00352000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 00411823 ESP = 0019FDFC EBP = 0019FDFC EFL = 00000246

变化的寄存器是

EIP = 00411823 EBP = 0019FDFC

现在开始真正改变 EBP了,EBP的值如果被改变了,那当函数结束后,之前 局部变量再通过之前 和 EBP的关系 不就定位不到了吗,其实不用关心,上一步已经保存了 上一个函数 的EBP, 当这个函数将要结束时,会取出保存的EBP

这里修改EBP,而且将 EBP 指向和ESP指向的同一块位置 ,现在EBP ESP都位于 上一个函数的栈帧的顶部

ESP指向的是栈顶而不是 栈顶的下一个位置 把 ESP 想象成**书堆的顶部**: - 你放一本新书时:先把手指移到新位置(ESP移动),然后放书(写入数据) - 你的手指总是指向最上面的那本书(最后一个有效数据) - 不是指向书上面的空气(下一个空闲位置)

执行 sub esp,0CCh

EAX = 0041C077 EBX = 0027A000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 00411829 ESP = 0019FD30 EBP = 0019FDFC EFL = 00000206

改变的寄存器是

EIP = 00411829 ESP = 0019FD30 EFL = 00000206

为什么mov ebp,esp 之后是 sub esp,0CCh ,现在不是ebp指向了和esp相同的位置了吗,按道理说只需要push push 就可以抬升 esp,为什么使用sub操作抬升

原因是使用sub esp, N一次性分配所有局部变量空间比用多个push指令更高效,

1.效率考量

  • sub esp, 0CCh:1条指令,分配 204 字节

  • 用push:需要约 51 条push指令(204÷4)

  • 节省指令数,执行更快

2.访问方式不同

用push分配的局部变量:

assembly

push 0 ; [esp] 是变量1
push 0 ; [esp] 是变量2,[esp+4] 是变量1
push 0 ; [esp] 是变量3,[esp+4] 是变量2,[esp+8] 是变量1

问题:ESP 一直在变,访问局部变量时偏移计算复杂!

用sub esp, N分配:

assembly

sub esp, 12 ; 分配12字节
mov [ebp-4], 0 ; 局部变量1
mov [ebp-8], 0 ; 局部变量2
mov [ebp-12], 0 ; 局部变量3

优势:EBP 固定,通过固定偏移访问局部变量

执行保存易变寄存器

00411829 push ebx 0041182A push esi 0041182B push edi
EAX = 0041C077 EBX = 0027A000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 0041182C ESP = 0019FD24 EBP = 0019FDFC EFL = 00000206

变化的寄存器是

EIP = 0041182A ESP = 0019FD2C EIP = 0041182B ESP = 0019FD28 EIP = 0041182C ESP = 0019FD24

执行 lea edi,[ebp-0Ch]

EAX = 0041C077 EBX = 0027A000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FDF0 EIP = 0041182F ESP = 0019FD24 EBP = 0019FDFC EFL = 00000206

变化的寄存器是

EDI = 0019FDF0 EIP = 0041182F

执行 mov ecx,3

EAX = 0041C077 EBX = 0027A000 ECX = 00000003 EDX = 00000001 ESI = 00411023 EDI = 0019FDF0 EIP = 00411834 ESP = 0019FD24 EBP = 0019FDFC EFL = 00000206

变化的寄存器是

ECX = 00000003 EIP = 00411834

执行 mov eax,0CCCCCCCCh

EAX = CCCCCCCC EBX = 0027A000 ECX = 00000003 EDX = 00000001 ESI = 00411023 EDI = 0019FDF0 EIP = 00411839 ESP = 0019FD24 EBP = 0019FDFC EFL = 00000206

变化的寄存器是

EAX = CCCCCCCC EIP = 00411839

执行 rep stos dword ptr es:[edi]

EAX=CCCCCCCC EBX=0027A000 ECX=00000000EDX=00000001ESI=00411023EDI=0019FDFC EIP=0041183B ESP=0019FD24 EBP=0019FDFC EFL=00000206

变化的寄存器是

ECX = 00000000 EDI = 0019FDFC EIP = 0041183B

STOS(Store String)是x86汇编中的字符串存储指令,用于将累加器(AL/AX/EAX)的值存储到内存中,

综合上述的 mov eax,0CCCCCCCCh 和 mov ecx,3 和 lea edi,[ebp-0Ch]

可知是以 ebp为基点,向上抬升3个字节大小(0ch是12),同时指定循环次数是3次,同时向 edi 指向的 处开始填充数据 0CCCCCCCCh,每次填充数据,会自动移动填充数据的指向指针

执行安全检查函数

0041183B mov ecx,41C077h ; 将安全 Cookie 值加载到 ECX 00411840 call 00411325 ; 调用 __security_check_cookie 函数 00411845 nop ; 对齐填充(无操作)

这是 Windows 程序的栈溢出保护机制:

工作原理:
  1. 函数开始时:在栈帧中插入一个随机值(Cookie)

  2. 函数结束时:检查这个值是否被改变

  3. 如果被改变:说明发生了栈溢出,程序立即终止

执行 mov dword ptr [ebp-8],0

使用ebp作为基点,在一个字节的偏移处设置数据为 0

执行自增操作

0041184D mov eax,dword ptr [ebp-8] 00411850 add eax,1 00411853 mov dword ptr [ebp-8],eax

将局部变量存储到 临时寄存器,对寄存器自增,将自增的值放回局部变量

执行恢复易变寄存器

00411856 pop edi 00411857 pop esi 00411858 pop ebx

还原 Esp到 ebp的初始位置

add esp,0CCh

注意从 执行 mov ebp,esp (不包括这个指令) 到现在 ,ebp作为基础点都没有修改过

验证ESP 是否真的还原成功

cmp ebp,esp

现在如果正确执行的话, EBP 和 ESP 都是指向同一个位置的

验证ESP的值是否有效

call 00411244

打开显示符号名

00411861 call __RTC_CheckEsp (0411244h)

执行一个看起来没什么用的指令

mov esp,ebp

因为 esp 和ebp 在之前都指向同一个位置了

执行 pop ebp

pop指令负责从栈中取出一个元素,并使得 esp +4,栈的高度下降,同时 这里是放到ebp中,

此时的寄存器的值是

EAX = 00000001 EBX = 0027A000 ECX = 0041C077 EDX = 00000001 ESI = 00411023 EDI = 0019FEE8 EIP = 00411869 ESP = 0019FE00 EBP = 0019FEE8 EFL = 00000246

刚好和 执行完执行 push ebp 保存 ebp 时, EBP 寄存器的数据相同

此时堆栈平衡,现在的EBP的值就是上一个函数的EBP的值了

相关新闻

  • TMS320C6743 DSP硬件设计:引脚配置、电源时序与时钟系统实战指南
  • Docker环境部署与优化实战指南
  • TMS320C5504 DSP电源、时钟与启动配置实战指南

最新新闻

  • 如何用GetQzonehistory一键备份QQ空间所有说说:终极免费指南
  • 告别风扇噪音:FanControl温控软件完整使用指南
  • 2026新手快速上手门店系统热门款深度测评 - 南溪村的小陈子
  • S5933 PCI总线主控DMA:寄存器详解与C62x DSP高效数据传输实战
  • GPU显存压力测试终极指南:用memtest_vulkan快速诊断显卡健康状态
  • 2026 届考生注意,武汉科谷技工学校招生电话公示 - 武汉中职最新信息发布

日新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号