前置条件
- 下载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堆栈的特点
- 先进后出
- 向低地址扩展