前置条件
- 下载HelloWorld程序(压缩包需要进行解压)
- OllyDBG
- 调整
Options
→Preferences
→Events
→Break on
中取消勾选System Breakpoint
- 调整
- 测试初始断点是否与下图一致
任务位置
- 0x401168 (演示)
- 0x401174
- 0x401182
- 0x40118E
跳转到上述地址后,在运行前画出堆栈图,分析代码的功能。
演示:0x401168
跳转到任务地址
在OllyDBG
中使用Ctrl+G
快捷键或者右键当前断点选择Go to
→Expression
,输入0x401168
后点击OK
。
移动到任务位置后,使用F2
快捷键下断点。
然后使用F9
,或者菜单中Debug
→Run
使程序运行至该断点。
观察栈顶、栈底地址
此处演示的地址可能与实际运行时存在差异。
记录上一步中程序的栈顶、栈底地址。
地址 | 描述 |
---|---|
0019FEE8 | ESP 栈顶 |
– | |
0019FF34 | EBP 栈底 |
行为分析
F8单步执行后
可以观察到,PUSH 2
指令执行后,ESP位置发生了改变,从0019FEE8
变为了0019FEE4
,栈顶位置中已经存入2
,这个步骤称之为压栈。
地址 | 值 | 描述 |
---|---|---|
0019FEE4 | 2 | ESP 栈顶 |
0019FEE8 | 指令执行前ESP位置 | |
– | – | |
0019FF34 | EBP 栈底 |
再次单步执行
可以观察到,PUSH 1
指令执行后,ESP位置发生了改变,从0019FEE4
变为了0019FEE0
,栈顶位置中已经存入1
。
地址 | 值 | 描述 |
---|---|---|
0019FEE0 | 1 | ESP 栈顶 |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 | EBP 栈底 |
CALL运行前分析
运行完PUSH 1
、PUSH 2
指令后,再次单步运行后程序将执行CALL
指令,但是F8单步运行后,程序将直接运行至00401171
,所以下一步使用F7
或菜单中的Debug
→Setp into
步入。
CALL
指令与JMP
指令不同,JMP
指令相当于MOV
地址到EIP
,但是CALL
不但修改EIP
的值,还会将其下一行指令的地址压入栈。
- 结果/行为预测
地址 | 值 | 描述 |
---|---|---|
0019FEDC | 00401171 | ESP 栈顶, CALL返回地址(斜体) |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 | EBP 栈底 |
EIP:0040100A
- F7步入后结果如下
进入CALL的函数后
通过观察,EIP指向的指令为JMP
,使用Enter
键进入其跳转的地址(此时CPU尚未执行到JMP的地址,需要通过F8进行跳转)。
此时可以观察到EIP的值发生了变化(JMP
修改)。
PUSH EBP
地址 | 值 | 描述 |
---|---|---|
0019FED8 | 0019FF34 | ESP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 | EBP |
MOV EBP, ESP
地址 | 值 | 描述 |
---|---|---|
0019FED8 | 0019FF34 | ESP, EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
SUB ESP, 40
地址 | 值 | 描述 |
---|---|---|
0019FE98 | ESP,缓冲区 | |
– | 缓冲区 | |
0019FED8 | 0019FF34 | EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
缓冲区的大小是不确定的,一般比用户需要的多一些。
PUSH EBX/ESI/EDI
- PUSH EBX
地址 | 值 | 描述 |
---|---|---|
0019FE94 | 002C3000 | ESP,EBX的值 |
0019FE98 | 缓冲区 | |
– | 缓冲区 | |
0019FED8 | 0019FF34 | EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
- PUSH ESI
地址 | 值 | 描述 |
---|---|---|
0019FE90 | 00401220 | ESP,ESI的值 |
0019FE94 | 002C3000 | EBX的值 |
0019FE98 | 缓冲区 | |
– | 缓冲区 | |
0019FED8 | 0019FF34 | EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
- PUSH EDI
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | ESP,EDI的值 |
0019FE90 | 00401220 | ESI的值 |
0019FE94 | 002C3000 | EBX的值 |
0019FE98 | 缓冲区 | |
– | 缓冲区 | |
0019FED8 | 0019FF34 | EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
这三步操作可以理解为保存现场,保存上一阶段的位置信息。
LEA EDI,DWORD PTR SS:[EBP-40]
取
SS:[EBP-40]
的地址编号到EDI
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | ESP |
0019FE90 | 00401220 | ESI的值 |
0019FE94 | 002C3000 | EBX的值 |
0019FE98 | 缓冲区,EDI的值 | |
– | 缓冲区 | |
0019FED8 | 0019FF34 | EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
EDI: 0019FE98
EIP不变
MOV ECX, 0x10
此部分解析以下命令,这些命令用于填充缓冲区,防止缓冲区溢出并清理内存中的垃圾。
|
|
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | ESP |
0019FE90 | 00401220 | ESI的值 |
0019FE94 | 002C3000 | EBX的值 |
0019FE98 | CCCCCCCC | 缓冲区 |
– | CCCCCCCC | 缓冲区 |
0019FED8 | 0019FF34 | EBP,EDI的值 |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
STOSD: 复制EAX存储的值到EDI指定的内存单元
REP: 重复执行命令 ECX次,注意:ECX的值是16进制的。
值
CCCCCCCC
是断点,防止缓冲区溢出。
MOV EAX, DWORD PTR SS:[EBP+0x8]
此命令用于读取参数。
EAX: 1
命令执行完成后堆栈没有变化。
ADD EAX, DWORD PTR SS:[EBP+0xC]
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | ESP |
0019FE90 | 00401220 | ESI的值 |
0019FE94 | 002C3000 | EBX的值 |
0019FE98 | CCCCCCCC | 缓冲区 |
– | CCCCCCCC | 缓冲区 |
0019FED8 | 0019FF34 | EBP,EDI的值 |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
EAX:3
命令执行完成后堆栈没有变化。
POP EDI/ESI/EBX
此命令用于恢复现场。
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | 恢复到EDI |
0019FE90 | 00401220 | 恢复到ESI |
0019FE94 | 002C3000 | 恢复到EBX |
0019FE98 | CCCCCCCC | ESP,缓冲区 |
– | CCCCCCCC | 缓冲区 |
0019FED8 | 0019FF34 | EBP,EDI的值 |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
MOV ESP, EBP
恢复堆栈
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | |
0019FE90 | 00401220 | |
0019FE94 | 002C3000 | |
0019FE98 | CCCCCCCC | |
– | CCCCCCCC | |
0019FED8 | 0019FF34 | ESP, EBP |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 |
POP EBP
重新定位栈底
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | |
0019FE90 | 00401220 | |
0019FE94 | 002C3000 | |
0019FE98 | CCCCCCCC | |
– | CCCCCCCC | |
0019FED8 | 0019FF34 | |
0019FEDC | 00401171 | ESP |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 | EBP |
RET
相当于POP EIP
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | |
0019FE90 | 00401220 | |
0019FE94 | 002C3000 | |
0019FE98 | CCCCCCCC | |
– | CCCCCCCC | |
0019FED8 | 0019FF34 | |
0019FEDC | 00401171 | |
0019FEE0 | 1 | ESP |
0019FEE4 | 2 | |
0019FEE8 | ||
– | – | |
0019FF34 | EBP |
EIP: 0019FF34
堆栈平衡
程序的函数执行后,其栈顶指针与栈底指针应该保持一致。
上述程序中,函数返回后没有平衡堆栈,所以需要执行ADD ESP, 8
地址 | 值 | 描述 |
---|---|---|
0019FE8C | 0019FF34 | |
0019FE90 | 00401220 | |
0019FE94 | 002C3000 | |
0019FE98 | CCCCCCCC | |
– | CCCCCCCC | |
0019FED8 | 0019FF34 | |
0019FEDC | 00401171 | |
0019FEE0 | 1 | |
0019FEE4 | 2 | |
0019FEE8 | ESP | |
– | – | |
0019FF34 | EBP |
外平栈: 在函数外部对堆栈进行平衡。(谁调用谁平衡)
程序分析
通过对上述程序的分析,可以得知其行为:1+2
|
|
函数
概念
计算机的函数,是一个固定的一个程序段,或称其为一个子程序,它在可以实现固定运算功能的同时还带有一入口和一个出口,所谓的入口,就是函数所带的各个参数,我们可以通过这个入口,把函数的参数值代入子程序,供计算机处理,所谓出口,就是指函数的计算结果,也称为返回值,在计算机求得之后,由此口带回给调用它的程序。
汇编中的函数
提升堆栈,为函数执行提供空间。
|
|
保留现场:函数在执行的时候会用到一些寄存器,但这些寄存器中的值很可能会被后面的程序用到,所以要先存储到内存中。
|
|
向分配的空间填充数据。
|
|
该函数的功能。
|
|
恢复现场:将之前保留的寄存器的值恢复。
|
|
降低堆栈。
|
|
恢复栈底。
|
|
函数执行完毕,返回到调用处。
|
|
函数的入口
|
|
其中,第一行和第二行为函数的参数。
也可以通过其他方式传入参数。
函数的出口
|
|
也可以通过其他方式返回参数。
堆栈的特点
Windows堆栈的特点
- 先进后出
- 向低地址扩展