一、上节课作业回顾
- 寄存器结构:32位、16位、8位寄存器的包含关系(如 EAX → AX → AH/AL)。
- MOV、ADD、SUB 等指令的所有合法形式,强调操作数宽度一致。
- 内存读写练习:加深对数据宽度和内存编号的理解。
二、内存数据窗口显示
数据窗口与堆栈窗口的数值顺序:
- 在寄存器窗口和反汇编窗口中,多字节数值按“高位在前,低位在后”显示(与我们书写习惯一致)。
- 在数据窗口(内存 dump)中,顺序相反:低位在前,高位在后,以字节为单位倒序排列。
- 例如:内存地址
0x12FFDC处若存储0x12345678,则数据窗口中显示为78 56 34 12。
内存编号与宽度:
- 每个内存编号对应 1 字节。
- 通过
MOV读/写内存时,用BYTE、WORD、DWORD指定操作宽度,会同时影响连续的几个字节。 - 示例:
MOV BYTE PTR DS:[地址], 值→ 只改动 1 字节。MOV WORD PTR DS:[地址], 值→ 改动连续 2 字节。MOV DWORD PTR DS:[地址], 值→ 改动连续 4 字节。
三、内存寻址公式
核心:所有复杂的地址表达式最终都只为了得到一个内存编号。以下五种方式涵盖了 CPU 能识别的所有寻址格式。
1. 寻址公式一:[立即数]
1 | ; 读取内存的值 |
2. 寻址公式二:[reg]
reg 代表任意 8 个 32 位通用寄存器之一。
1 | MOV ECX, 0x13FFD0 |
3. 寻址公式三:[reg + 立即数]
1 | MOV ECX, 0x13FFD0 |
4. 寻址公式四:[reg + reg * {1,2,4,8}]
乘数只能是 1, 2, 4, 8。
1 | MOV EAX, 0x13FFC4 |
5. 寻址公式五:[reg + reg * {1,2,4,8} + 立即数]
1 | MOV EAX, 0x13FFC4 |
四、堆栈
1. 为什么需要堆栈?
当需要临时存储大量数据(远超 8 个寄存器)时,必须有规律地使用内存,且满足:
- 记录存了多少数据。
- 快速定位任意一个数据。
- 使用完毕能方便地“丢弃”。
2. 堆栈的基本设计
用两个寄存器分别记录起始地址(栈底)和当前地址(栈顶)。
- 栈底(BASE)固定不动。
- 栈顶(TOP)随数据的存入和弹出而上下移动。
- 每次存取 4 字节(演示用),栈顶的值减 4(向低地址增长)表示入栈,加 4 表示出栈。
- 读取中间数据时,可以用
BASE + 偏移或TOP + 偏移来定位。
| TOP:0X100C | |
| BASE:0X1000 |
3. 手动模拟堆栈操作
压入数据(入栈)的多种实现方式:
1 | ; 假设 EBX 存栈底,EDX 存栈顶,初始都指向 0x13FFDC |
读取中间数据:
1 | ; 通过栈底加偏移:读第 1 个压入的数(最后一个位置) |
弹出数据(出栈):
1 | ; 方式一:先读值,后移动栈顶 |
4. 正式堆栈操作:PUSH / POP
CPU 提供了专门的寄存器(ESP 栈顶,EBP 栈底)和指令,但我们自己也可以用任何寄存器模拟。
PUSH 指令(可压入寄存器、内存、立即数):
1 | PUSH EAX ; 压入 32 位寄存器 |
功能等价于:SUB ESP,4 + MOV [ESP], 源操作数
POP 指令:
1 | POP EAX ; 弹出到 32 位寄存器 |
功能等价于:MOV 目标, [ESP] + ADD ESP,4
PUSHAD / POPAD:
PUSHAD:将所有 8 个 32 位通用寄存器按固定顺序压栈。POPAD:按相反顺序弹出,恢复寄存器。
五、课后作业
- 内存寻址练习:对五种寻址公式,逐一实现:读取内存、写入内存、获取内存地址编号。
- 堆栈模拟练习:
- 用 EBX 做栈底、EDX 做栈顶,连续压入 5 个不同的数。
- 分别通过栈底加偏移和栈顶加偏移的方式读取这 5 个数到寄存器中。
- 将这 5 个数依次弹出,并恢复栈顶到初始位置。
- 指令变形挑战(选做):
- 用至少两种不同的指令组合,实现
PUSH ECX的功能。 - 用至少两种不同的指令组合,实现
POP ECX的功能。 - 思考:
PUSH ESP和POP ESP如何用其他指令等效替代?
- 用至少两种不同的指令组合,实现










