X86汇编PUSH与POP操作对于变量在内存中高低地址的转换分析

一、高低地址的核心概念

内存可以看作是一段线性的、按字节编号的存储空间,就像一条无限长的街道,每个字节都是一个"房子",每个房子有唯一的"门牌号"(地址):

  • 低地址 :地址数值小的内存位置(比如 0x00001000)。
  • 高地址 :地址数值大的内存位置(比如 0x00002000)。

举个通俗例子:把内存地址想象成楼层号,低地址是1楼、2楼(数值小),高地址是10楼、20楼(数值大)。

关键特性(x86架构,逆向最常用):

栈的生长方向是从高地址向低地址扩展 ------压栈(push)数据时,栈顶寄存器esp的数值会减小 (指向更低的地址);出栈(pop)数据时,esp数值会增大(指向更高的地址)。

二、结合栈帧的具体地址数值举例

以32位x86系统(逆向最经典场景)为例,用main函数调用add(a=1, b=2)的过程,一步步展示栈地址的变化,所有地址均为十六进制(逆向标准表示)。

前提约定:
  • 32位系统中,每个栈操作(push/pop)占用4字节(地址增减步长为4)。
  • 调用约定为cdecl(C语言默认):参数从右到左入栈,返回地址由call指令保存。
  • 初始状态:main函数的栈帧基址ebp=0x0012FF80(栈帧底部,高地址),esp=0x0012FF80(栈顶,初始与底部重合)。
步骤1:参数入栈(main调用add前)

cdecl约定下,参数从右到左压栈(先压b,再压a):

  1. 压入参数b=2:
    • 执行push 2,esp = 0x0012FF80 - 4 = 0x0012FF7C(地址减小,指向更低地址)。
    • 内存地址0x0012FF7C(高地址)存储数值2。
  2. 压入参数a=1:
    • 执行push 1,esp = 0x0012FF7C - 4 = 0x0012FF78。
    • 内存地址0x0012FF78(低地址)存储数值1。

此时栈地址分布(高→低):

地址(十六进制) 存储内容 说明
0x0012FF7C 2(参数b) 高地址
0x0012FF78 1(参数a) 低地址(栈顶当前)
步骤2:保存返回地址(call指令)

执行call add时,CPU会先把main函数下一条指令的地址(返回地址,假设为0x00401020)压入栈,再跳转到add函数:

  • esp = 0x0012FF78 - 4 = 0x0012FF74。
  • 内存地址0x0012FF74存储返回地址0x00401020。
步骤3:建立add函数的栈帧

进入add函数后,首先执行栈帧初始化指令:

  1. 保存旧ebp(main的ebp):
    • 执行push ebp,esp = 0x0012FF74 - 4 = 0x0012FF70。
    • 内存地址0x0012FF70存储0x0012FF80(main的ebp值)。
  2. 设置新ebp(add的栈帧底部):
    • 执行mov ebp, esp,此时ebp = 0x0012FF70(add栈帧的底部,高地址)。
  3. 分配局部变量(比如add的局部变量c):
    • 执行sub esp, 4(分配4字节),esp = 0x0012FF70 - 4 = 0x0012FF6C(add栈帧的顶部,低地址)。
    • 内存地址0x0012FF6C存储局部变量c(初始值随机,比如0)。
最终add栈帧的完整地址分布(高→低)
地址(十六进制) 存储内容 角色 地址属性
0x0012FF70 0x0012FF80(main的ebp) add栈帧底部(ebp) 高地址
0x0012FF74 0x00401020(返回地址) 返回地址 -
0x0012FF78 1(参数a) 函数参数 -
0x0012FF7C 2(参数b) 函数参数 -
0x0012FF6C 0(局部变量c) add栈帧顶部(esp) 低地址

三、补充说明

  1. 上述地址是示例值 ,实际系统中地址会因操作系统(Windows/Linux)、编译器(MSVC/GCC)、ASLR(地址随机化)等因素变化,但高低地址的相对关系永远不变。
  2. 64位系统的栈操作逻辑与32位一致,仅地址宽度变为64位(比如0x000000000012FF80),每次push/pop的步长为8字节。
  3. ebp(栈帧基址)的核心作用是"锚定"栈帧:通过ebp+偏移量可以精准访问参数(如ebp+8是参数a)、局部变量(如ebp-4是局部变量c),这是逆向中定位参数/变量的关键。

总结

  1. 内存地址数值越大,地址越高;数值越小,地址越低,栈在x86架构下从高地址向低地址生长。
  2. 栈帧中ebp指向栈帧底部(高地址),esp指向栈帧顶部(低地址),push操作使esp减小,pop使esp增大。
  3. 函数调用时,栈地址从高到低依次存储:旧ebp → 返回地址 → 函数参数 → 局部变量。
相关推荐
Hi202402171 天前
同一个 NMS 算子,两种语言实现:CUDA C 与 Rust 生成 LLVM IR 和 Vortex 汇编的差异
c语言·汇编·rust·gpgpu·vortex·cudac
lhbweilai1 天前
深挖FreeRTOS内核实现(二)
c语言·汇编·stm32·单片机
水饺编程2 天前
第1章:开发环境搭建,在 Windows 中安装 VS 2019
linux·c语言·汇编·visual studio
水饺编程3 天前
第1章:下载 Linux 0.12 内核
linux·c语言·汇编·ubuntu
水饺编程3 天前
第1章:开发环境搭建,在 Windows 中安装 Git
linux·c语言·汇编·git·ubuntu
水饺编程3 天前
第1章:在 Windows 中安装 GCC 套件
c语言·汇编
水饺编程4 天前
第1章,了解乌班图环境,使用浏览器和邮箱
c语言·汇编·ubuntu
水饺编程4 天前
第1章:开发环境搭建,在 Windows 中安装 QEMU
c语言·汇编
AC赳赳老秦4 天前
OpenClaw 数据引用规范自动生成:为公开数据构建可信来源标注与标准引用体系
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw
AC赳赳老秦5 天前
数据采集全链路审计留痕:用 OpenClaw 实现合规审计与追溯
开发语言·汇编·python·php·swift·deepseek·openclaw