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

目录

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) |

相关推荐
小杨不想秃头19 小时前
信息安全工程师 历年真题原题汇编(2016–2025)
汇编
字节暗面3 天前
SO 加固强度自查 Checklist:静态、加载链、运行时看哪几项
安全·逆向
sysinside9 天前
Binary Ninja 6.0 (macOS, Linux, Windows) 发布 - 逆向平台
逆向·反编译
2601_9622965110 天前
C/C++:编译全过程——预处理、编译、汇编、链接(包含预处理指令:宏定义,文件包括、条件编译)
汇编·预处理·链接·c/c++·编译过程
Freak嵌入式11 天前
Pico PIO 汇编语法深度详解:标识符、指令、伪指令全解析
汇编·stm32·单片机·嵌入式硬件·fpga开发·硬件架构
0xBADCODE12 天前
动态DEX加载+反射+DES硬编码密钥:安卓三层逆向实战
android·java·python·安全·网络安全·逆向·ctf
深念Y12 天前
B站封面与元数据抓取完整方案
逆向
kyle~16 天前
x86 汇编LOCK前缀 --- 硬件架构向软件提供的核心同步原语
汇编·c++·性能优化·硬件架构·实时系统
典典分享指南17 天前
飞书 + 企业微信 + 微信文档多端协同实践指南
汇编·flask·intellij-idea·fastapi
安全小王子17 天前
nssctf_chicken_soup
网络安全·逆向·ctf·nssctf