逆向学习之画堆栈图一
字数 1109 2025-08-03 16:49:13

逆向工程学习:画堆栈图教学文档

一、准备工作

1. 工具与程序

  • 调试工具: DTDebug
  • 调试程序: helloword.exe

2. 初始设置

  1. 打开DTDebug并将helloword.exe拖入调试器
  2. 修改内存显示设置(从77开头改为00开头)
  3. 重启调试器使设置生效

二、定位与断点设置

  1. 使用Control+G跳转到地址0x401168
  2. 在起始位置按下F2设置断点(背景变红表示断点已设置)
  3. 点击运行箭头让程序执行到断点处(背景变黑表示到达断点)

三、堆栈图绘制步骤

初始状态

  • 记录当前ESP和EBP的值

1. 执行PUSH指令

PUSH 2
PUSH 1
  • 堆栈变化:两个值依次压入栈顶
  • 验证:检查堆栈指针和栈顶值

2. 函数调用

  • 按下F7进入函数(不是F8单步)
  • 效果:
    • 返回地址0x00401171被压入栈中
    • EIP变为0040100A(函数入口地址)

3. 函数序言

PUSH EBP
MOV EBP,ESP
SUB ESP,40
  • 堆栈变化:
    1. 保存原EBP值
    2. EBP指向当前栈帧
    3. 为局部变量分配64字节空间(0x40)

4. 保存寄存器现场

PUSH EBX
PUSH ESI
PUSH EDI
  • 堆栈变化:三个寄存器值依次压入栈中

5. 初始化局部变量空间

LEA EDI,DWORD PTR SS:[EBP-40]
MOV ECX,10
MOV EAX,CCCCCCCC
REP STOS DWORD PTR ES:[EDI]
  • 作用:用0xCCCCCCCC填充局部变量空间(16次,共64字节)
  • 注意:此操作不直接影响堆栈指针

6. 函数内部操作

MOV EAX,DWORD PTR SS:[EBP+8]
ADD EAX,DWORD PTR SS:[EBP+C]
  • 操作:
    • 从栈中取出两个参数(偏移EBP+8和EBP+C)
    • 相加后结果存入EAX
  • 堆栈不变

7. 恢复寄存器现场

POP EDI
POP ESI
POP EBX
  • 堆栈变化:三个寄存器值依次弹出

8. 函数收尾

MOV ESP,EBP
POP EBP
RETN
  • 操作:
    1. 恢复ESP到当前栈帧开始处
    2. 恢复原EBP值
    3. RETN弹出返回地址到EIP

9. 堆栈平衡

ADD ESP,8
  • 作用:清除调用函数时压入的两个参数

四、关键知识点总结

  1. 堆栈基本概念

    • ESP:栈指针,指向栈顶
    • EBP:基址指针,指向当前栈帧
    • 栈生长方向:从高地址向低地址
  2. 函数调用约定

    • 参数从右向左压栈
    • 调用者负责堆栈平衡
    • 返回地址自动压栈
  3. 栈帧结构

    | ...          |
    | 参数2        |
    | 参数1        |
    | 返回地址      |
    | 保存的EBP     | ← EBP
    | 局部变量      |
    | 保存的寄存器  |
    | ...          | ← ESP
    
  4. 重要指令

    • PUSH/POP:栈操作
    • CALL/RET:函数调用/返回
    • MOV EBP,ESP:建立栈帧
    • LEAVE:等效于MOV ESP,EBP + POP EBP
  5. 调试技巧

    • 使用F7进入函数,F8单步执行
    • 注意观察寄存器值的变化
    • 内存窗口查看堆栈内容
  6. 堆栈平衡原则

    • 函数调用前后堆栈指针必须一致
    • 调用者或被调用者负责清理参数

五、实践建议

  1. 从简单函数开始练习画堆栈图
  2. 对比调试器显示验证自己的堆栈图
  3. 注意不同调用约定(stdcall/cdecl等)的差异
  4. 练习识别常见的函数序言和收尾模式
  5. 记录每次栈操作后的ESP/EBP变化

通过系统性地绘制和分析堆栈图,可以深入理解程序执行时的内存布局,为逆向工程打下坚实基础。

逆向工程学习:画堆栈图教学文档 一、准备工作 1. 工具与程序 调试工具 : DTDebug 调试程序 : helloword.exe 2. 初始设置 打开DTDebug并将helloword.exe拖入调试器 修改内存显示设置(从77开头改为00开头) 重启调试器使设置生效 二、定位与断点设置 使用 Control+G 跳转到地址 0x401168 在起始位置按下 F2 设置断点(背景变红表示断点已设置) 点击运行箭头让程序执行到断点处(背景变黑表示到达断点) 三、堆栈图绘制步骤 初始状态 记录当前ESP和EBP的值 1. 执行PUSH指令 堆栈变化:两个值依次压入栈顶 验证:检查堆栈指针和栈顶值 2. 函数调用 按下 F7 进入函数(不是 F8 单步) 效果: 返回地址 0x00401171 被压入栈中 EIP变为 0040100A (函数入口地址) 3. 函数序言 堆栈变化: 保存原EBP值 EBP指向当前栈帧 为局部变量分配64字节空间(0x40) 4. 保存寄存器现场 堆栈变化:三个寄存器值依次压入栈中 5. 初始化局部变量空间 作用:用 0xCCCCCCCC 填充局部变量空间(16次,共64字节) 注意:此操作不直接影响堆栈指针 6. 函数内部操作 操作: 从栈中取出两个参数(偏移EBP+8和EBP+C) 相加后结果存入EAX 堆栈不变 7. 恢复寄存器现场 堆栈变化:三个寄存器值依次弹出 8. 函数收尾 操作: 恢复ESP到当前栈帧开始处 恢复原EBP值 RETN弹出返回地址到EIP 9. 堆栈平衡 作用:清除调用函数时压入的两个参数 四、关键知识点总结 堆栈基本概念 : ESP:栈指针,指向栈顶 EBP:基址指针,指向当前栈帧 栈生长方向:从高地址向低地址 函数调用约定 : 参数从右向左压栈 调用者负责堆栈平衡 返回地址自动压栈 栈帧结构 : 重要指令 : PUSH/POP:栈操作 CALL/RET:函数调用/返回 MOV EBP,ESP:建立栈帧 LEAVE:等效于MOV ESP,EBP + POP EBP 调试技巧 : 使用F7进入函数,F8单步执行 注意观察寄存器值的变化 内存窗口查看堆栈内容 堆栈平衡原则 : 函数调用前后堆栈指针必须一致 调用者或被调用者负责清理参数 五、实践建议 从简单函数开始练习画堆栈图 对比调试器显示验证自己的堆栈图 注意不同调用约定(stdcall/cdecl等)的差异 练习识别常见的函数序言和收尾模式 记录每次栈操作后的ESP/EBP变化 通过系统性地绘制和分析堆栈图,可以深入理解程序执行时的内存布局,为逆向工程打下坚实基础。