强化阶段笔记
1. x86 汇编
1.1. 汇编语言基础
通用寄存器:EAX,EBX,ECX,EDX。可以存放任何数据。
- A, B, C, D 为标号,
E = Extended表示 32 bit,X表示未知。 - 可以只用低 16 位,即 AX, BX, CX, DX。
- 低 16 位又进一步分为低 8 位和高 8 位,分别用 AL, AH, BL, BH 等表示。
- A, B, C, D 为标号,
变址寄存器:ESI,EDI。用于线性表(数组)和字符串处理。
I = Index,S = Source,D = Destination。
堆栈寄存器:EBP,ESP。实现函数调用。
- EBP 为栈基(栈底)指针,ESP 为栈顶指针。
寄存器常见用途

1.2. 数据传送指令
以下都用 Intel 格式为例(前面是目的数后面是源数)。若是 ATMT 格式则反过来。考试中会有说明。
- mov
- 格式:
mov 目的操作数, 源操作数 - 将源数(寄存器、主存或立即数)送至目的数(寄存器或主存)。
- 目的数不能为立即数,也不能直接指向内存地址,例如
mov [1234H], eax。必须用寄存器辅助。
- 格式:
- push
- 格式:
push 操作数 - 将操作数压入栈。常用于函数调用。
- RECALL 压栈过程:ESP 先减 4,再将操作数送 ESP 地址。出栈先读出 ESP,再 ESP 自增 4。
- 格式:
- pop
- 格式:
pop 目的操作数 - 和 push 相反。将栈顶数出栈,送到目的操作数(寄存器或主存)。目的数不能为立即数。
- 格式:
1.3. 算术运算和逻辑指令
- add/sub
- 格式:
add/sub 目的操作数, 源操作数 - 目的数加上/减去源操作数,结果送目的数。注意减法时目的数作为被减数。目的数不能为立即数。
- 格式:
- inc/dec
- 格式:
inc/dec 操作数 - 将操作数(寄存器或主存)自增或自减 1.
- 格式:
- imul
- 格式:(1)
imul 目的操作数, 源操作数,(2)imul 寄存器, 操作数 1, 操作数 2 - 带符号数相乘。
- 格式 1,两个数相乘,结果送目的数(寄存器或主存)。
- 格式 2,操作数 1 和 2 相乘,结果送寄存器。第一个数只能是寄存器。
- 溢出时,置 OF = 1,CPU 会调用溢出异常处理程序。
- 格式:(1)
- idiv
- 格式:
idiv 操作数 - 带符号整数除法。只有一个操作数作为除数,被除数默认为 eax。
- eax 视为 64 位,除法结果商送 eax,余数送 edx。
- 格式:
- and/or/xor
- 格式:
and/or/xor 目的操作数, 源操作数 - 两个操作数按位与/或/异或运算。结果送目的数(寄存器或主存)。
- 格式:
- not/neg
- 格式:
not/neg 操作数 - 将操作数按位取反/取负值。单操作数指令,操作数不能为立即数。
- 格式:
- shl/shr
- 格式:
shl/shr 目的操作数, 源操作数 - 将目的数左移/右移指定位数,用源数给出。源数一般为立即数,有时也用寄存器。
- 目的数一般为寄存器,不能为立即数。
- 格式:
1.4. 控制流指令
x86 维护一个寄存器 IP,等效于 PC。IP 不能直接操作,但可以用控制指令来修改。
- jmp
- 格式:
jmp label - 无条件跳转指令。将 IP 直接修改到 label 所指示的指令地址。
- 格式:
- j+condition
- 格式:
je/jne/jz/jnz/... label - 有条件跳转指令。满足条件时等价于 jmp,否则不执行任何操作。
- 本质上是对 PSW 中的标志位进行判断。所以通常需要在上一条指令用 cmp/test 指令修改 PSW。
- 格式:
- cmp/test
- 格式:
cmp/test 目的操作数, 源操作数 - cmp:比较目的操作数和源操作数,根据比较结果修改 PSW。
- test:两个操作数按位与运算。根据结果是否为 0 修改 ZF。
- 格式:
- call/ret
- 格式:
call [子程序],ret - 实现子程序的调用和返回。
- call:先将当前 IP 入栈,然后无条件跳转到子程序入口地址。
- ret:从栈顶弹出保存的 IP,然后无条件跳转到指向的地址。
- 格式:
1.5. 扩展:函数调用与栈帧
栈帧:函数运行过程中为了存储形参和一些局部变量,需要占据的一段栈地址范围。
enter 和 leave 指令:enter 用于为被调用的函数创建栈帧,修改 ebp 和 esp。leave 在函数返回时清理栈帧,恢复调用前的 ebp 和 esp。由基本的 mov 和 push 指令组成。
- enter 指令
asm; enter ; num_bytes 为被调用函数需要的栈空间大小 push ebp ; 当前 ebp 压栈保存 mov ebp, esp ; 将当前 ebp 置为 esp sub esp, num_bytes ; esp 减去 num_bytes, 指向新的栈顶. num_bytes 为新函数的栈帧大小 ; 新的 ebp 指向被保存的原来 ebp 的栈单元地址- leave 指令
asm; leave mov esp, ebp ; 将当前 esp 置为 ebp pop ebp ; 被保存的 ebp 出栈, 赋给当前 ebp, 同时 esp 自增, 恢复原来的状态一般函数调用过程
asm; 被调用函数 sub sub: enter ... ; 其他代码 ... leave ; 清理栈帧 ret ; 返回 ; 调用方 main main: ... ; 其他代码 call sub ; 调用 sub