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