堆栈中的 参数与局部变量 (逆向分析)

目录

C代码与汇编映射

汇编流程

栈帧结构与内存布局

[参数 局部变量 esp ebp的关联说明](#参数 局部变量 esp ebp的关联说明)

[func 函数执行期间](#func 函数执行期间)

[EBP 的核心作用:栈帧基址锚点](#EBP 的核心作用:栈帧基址锚点)

[参数访问:EBP + 正偏移(在 EBP 上方)](#参数访问:EBP + 正偏移(在 EBP 上方))

[局部变量访问:EBP - 负偏移(在 EBP 下方)](#局部变量访问:EBP - 负偏移(在 EBP 下方))

[ESP 的动态变化](#ESP 的动态变化)


C代码与汇编映射

cpp 复制代码
int func(int a, int b) {
    int x = a + 1;    // 局部变量x
    int y = b * 2;    // 局部变量y
    return x + y;
}

int main() {
    int result = func(20, 42);
    return result;
}
  • 参数压栈顺序

    • 从右向左(先压42,再压20),确保参数a在低地址,b在高地址;
  • 栈帧建立

    • 通过push ebp与mov ebp, esp构建当前函数的栈帧基址;
  • 局部变量分配

    • sub esp, 8在EBP下方预留8字节空间(x与y各4字节)。
汇编流程
cpp 复制代码
; === main 函数(调用者) ===
main:
    push ebp           ; 保存main的旧EBP(上下文保护)
    mov  ebp, esp      ; 建立main的栈帧
    sub  esp, 16       ; 为局部变量分配16字节空间(含对齐)

    ; 压栈参数(从右向左):
    push 42            ; 参数2: b = 42(先压入,位于高地址)
    push 20            ; 参数1: a = 20(后压入,位于低地址)
    call func          ; 压入返回地址(EIP),跳转到func
    add  esp, 8        ; 清理参数(cdecl约定由调用者清理)
    
    mov  DWORD PTR [ebp-4], eax  ; 保存返回值到result
    mov  eax, 0        ; 返回0
    leave              ; 等价于: mov esp, ebp; pop ebp
    ret

; === func 函数(被调用者) ===
func:
    push ebp           ; 保存调用者(main)的EBP(关键!形成EBP链)
    mov  ebp, esp      ; 设置当前栈帧基址(此时EBP指向旧EBP位置)
    sub  esp, 8        ; 分配8字节局部变量空间(x和y各4字节)

    ; --- 栈帧结构关键点 ---
    ; [ebp]       = 保存的旧EBP(main的EBP)
    ; [ebp+4]     = 返回地址(main中call后的下一条指令)
    ; [ebp+8]     = 第1个参数 a = 20
    ; [ebp+12]    = 第2个参数 b = 42
    ; [ebp-4]     = 局部变量 x
    ; [ebp-8]     = 局部变量 y

    ; 访问参数并计算局部变量:
    mov  eax, DWORD PTR [ebp+8]   ; eax = a (20)
    add  eax, 1                   ; eax = a+1 (21)
    mov  DWORD PTR [ebp-4], eax   ; x = 21

    mov  eax, DWORD PTR [ebp+12]  ; eax = b (42)
    add  eax, eax                 ; eax = b*2 (84)
    mov  DWORD PTR [ebp-8], eax   ; y = 84

    ; 返回值计算(通过EAX传递):
    mov  eax, DWORD PTR [ebp-4]   ; eax = x
    add  eax, DWORD PTR [ebp-8]   ; eax = x+y (105)

    ; --- 上下文恢复关键步骤 ---
    mov  esp, ebp      ; 1. ESP回到当前帧基址(清除局部变量)
    pop  ebp           ; 2. 恢复调用者EBP(从栈中弹出旧EBP)
    ret                ; 3. 弹出返回地址到EIP(自动ESP+=4)
栈帧结构与内存布局
  • 当func函数执行时,堆栈呈现清晰的层次结构(高地址→低地址)
cpp 复制代码
高地址(内存高位)
+---------------------+ ← 初始 ESP (main 调用前)
| main 的局部变量      |  (16字节,main 中 sub esp,16 分配)
| ... (result 等)      |
+---------------------+ ← main 的 EBP (0x0FFC)
| 保存的旧 EBP (main)  |  ← [main_ebp] = 0x2000 (main 的调用者帧基址)
+---------------------+
| 参数 b = 42         |  ← [ebp + 12] (main 中 push 42)
+---------------------+
| 参数 a = 20         |  ← [ebp + 8]  (main 中 push 20)
+---------------------+
| 返回地址 (EIP)      |  ← [ebp + 4]  (call func 压入)
+---------------------+ ← func 的 EBP (0x0FD0) ← EBP 寄存器当前值
| 保存的旧 EBP (main) |  ← [ebp]      (func 中 push ebp)
+---------------------+
| 局部变量 y = 84     |  ← [ebp - 4]  (func 中分配)
+---------------------+
| 局部变量 x = 21     |  ← [ebp - 8]  (func 中分配)
+---------------------+ ← func 的 ESP (0x0FC8) ← ESP 寄存器当前值
|                      |
|       空闲栈空间     |
|                      |
低地址(内存低位)
参数 局部变量 esp ebp的关联说明
  1. EBP 是栈帧基址指针 :在函数内部 固定不变 ,作为访问参数/局部变量的 唯一基准

  2. ESP 是栈顶指针 动态变化 ,随压栈/出栈操作实时移动

  3. 参数在 EBP 上方 (正偏移):因为调用者先压参数,再压返回地址

  4. 局部变量在 EBP 下方 (负偏移):被调用者通过 sub esp, N 分配

func 函数执行期间

EBP 的核心作用:栈帧基址锚点
  • EBP 寄存器值 = 0x0FD0(指向当前函数的栈帧基址)

  • EBP 指向的位置 :保存了调用者(main)的 EBP 值0x0FFC

  • 为什么固定? mov ebp, esp 执行后,EBP 不再随局部变量分配而改变,始终指向:

cpp 复制代码
[ebp] = 保存的旧 EBP (main 的帧基址)
参数访问:EBP + 正偏移(在 EBP 上方)
cpp 复制代码
偏移量	计算方式	物理位置	值	说明
4	[ebp + 4]	0x0FD4	EIP	返回地址(call 指令压入)
8	[ebp + 8]	0x0FD8	20	第1个参数 a(main 后压)
12	[ebp + 12]	0x0FDC	42	第2个参数 b(main 先压)
  • 第 N 个参数 = EBP + 4(N+1) * (因为 [ebp+4] 是返回地址,参数从 [ebp+8] 开始)
局部变量访问:EBP - 负偏移(在 EBP 下方)
cpp 复制代码
偏移量	计算方式	物理位置	值	说明
-4	[ebp - 4]	0x0FCC	21	局部变量 x(先分配)
-8	[ebp - 8]	0x0FC8	84	局部变量 y(后分配)
  • 第 K 个局部变量 = EBP - 4\*K (按声明顺序从高地址向低地址分配)
ESP 的动态变化

|--------------------|-----------|--------|--------------------|
| 指令 | ESP 变化 | ESP 值 | 说明 |
| push ebp (func 开始) | ESP -= 4 | 0x0FD0 | 压入旧 EBP(main 的帧基址) |
| mov ebp, esp | 无变化 | 0x0FD0 | EBP 锚定当前帧基址 |
| sub esp, 8 | ESP -= 8 | 0x0FC8 | 分配局部变量空间(x,y 各4字节) |
| mov esp, ebp (返回时) | ESP = EBP | 0x0FD0 | 丢弃局部变量(ESP 回到帧基址) |
| pop ebp | ESP += 4 | 0x0FD4 | 恢复调用者 EBP(加载到寄存器) |
| ret | ESP += 4 | 0x0FD8 | 弹出返回地址(跳转回 main) |

相关推荐
ThornArmor4 天前
【工具篇·番外】跨语言生态的主权回收:基于 ISA 说明书的 4-bit 双向汇编系统全线封顶
c语言·开发语言·汇编·c++·重构·架构
是星辰吖~5 天前
WIN32_线程(下)
汇编
是星辰吖~5 天前
WIN32_线程(上)
汇编
夜猫逐梦5 天前
【UE基础】01.环境与引擎心智模型
游戏·逆向·ue·unreal·actionrpg
AI科技星6 天前
数术工坊 · 第四卷 橡皮泥江湖(拓扑学)【完整定稿】
c语言·开发语言·汇编·electron·概率论·拓扑学
iCxhust6 天前
C# 生成命令行程序 将hex格式烧录程序转换成bin烧录格式
开发语言·汇编·单片机·嵌入式硬件·c#·微机原理
iCxhust7 天前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
hhcgchpspk7 天前
汇编语言传递数据和地址的误区
汇编·笔记·nasm·masm
iCxhust7 天前
MTK8088单板机制作(一)时钟电路
汇编·单片机·嵌入式硬件·微机原理·8088单板机
iCxhust7 天前
8086 汇编位测试使用方法
汇编·单片机·嵌入式硬件·微机原理·8088单板机