从零开始的 Pwn 之旅 - x86 汇编基础
汇编语言风格
| AT&T | Intel |
|---|---|
寄存器前缀 % | 寄存器无前缀 |
立即数前缀 $ | 立即数无前缀 |
16 进制数前缀 0x | 16 进制数后缀 h |
| 源操作数在前,目标操作数在后 | 源操作数在后,目标操作数在前 |
间接寻址使用 (%reg) | 间接寻址使用 [reg] |
间接寻址格式为 (%reg, %reg, scale) | 间接寻址格式为 [reg + reg * scale] |
操作数大小后缀 b、w、l | 操作数大小后缀 byte ptr、word ptr、dword ptr |
寄存器
寄存器是 CPU 内部的高速存储器,用于存储临时数据和指令执行状态。
如下图所示,以 r 开头的寄存器为 64 位寄存器,e 开头的寄存器为 32 位寄存器,ax、bx 等为 16 位寄存器。
在 64 位模式下,寄存器的高 32 位可以通过 e 前缀访问,例如 eax 是 rax 的低 32 位。
同样,寄存器的低 16 位可以通过 x 后缀访问,例如 ax 是 eax 的低 16 位。

特殊寄存器
段寄存器:用于存储内存段的基地址,常用的段寄存器有
cs(代码段)、ds(数据段)、ss(堆栈段)、es、fs、gs。标志寄存器:用于存储 CPU 的状态信息,包括零标志位、符号标志位、进位标志位等。
指令指针寄存器:
eip(32 位模式)或rip(64 位模式),用于存储下一条要执行的指令的地址。堆栈指针寄存器:
esp(32 位模式)或rsp(64 位模式),用于指向当前堆栈的顶部。基址寄存器:
ebp(32 位模式)或rbp(64 位模式),用于指向当前函数的堆栈帧基址。
常见指令
- MOV:数据传送指令,用于将数据从一个位置移动到另一个位置。
![NOTE] BYTE、WORD 和 DWORD 分别表示 1 字节、2 字节和 4 字节的数据类型。
| |
- LEA:加载有效地址指令,用于将内存地址加载到寄存器中。
lea 指令中的 [] 不用于取值,表示一个内存地址计算表达式
- INC:自增指令,用于将寄存器或内存中的值加 1。
- DEC:自减指令,用于将寄存器或内存中的值减 1。
- ADD:加法指令,用于将两个操作数相加。
- SUB:减法指令,用于将一个操作数从另一个操作数中减去。
- NEG:取反指令,用于将寄存器或内存中的值取反。
- XOR:按位异或指令,用于将两个操作数按位异或。
- XCHG:交换指令,用于交换两个操作数的值。
- CMP:比较指令,用于比较两个操作数的值。
CMP 指令不会修改操作数的值,只会设置标志寄存器中的标志位。
- TEST:按位与指令,用于将两个操作数按位与,并设置标志寄存器中的标志位。
- JZ/JNZ:条件跳转指令,用于根据标志寄存器中的零标志位进行跳转。
| 指令 | 英文全称 | 跳转条件(标志位) | 对应关系(易懂) | 常见用途(有/无符号) |
|---|---|---|---|---|
JE | Jump Equal | ZF=1 | 等于时跳转 | 有符号/无符号 |
JZ | Jump Zero | ZF=1 | 等于时跳转 | 有符号/无符号 |
JNE | Jump Not Equal | ZF=0 | 不等于时跳转 | 有符号/无符号 |
JNZ | Jump Not Zero | ZF=0 | 不等于时跳转 | 有符号/无符号 |
JA | Jump Above | CF=0 且 ZF=0 | (无符号)大于时跳转 | 无符号 |
JNBE | Jump Not Below or Equal | CF=0 且 ZF=0 | (无符号)大于时跳转 | 无符号(同 JA) |
JAE | Jump Above or Equal | CF=0 | (无符号)大于等于时跳转 | 无符号 |
JNB | Jump Not Below | CF=0 | (无符号)大于等于时跳转 | 无符号(同 JAE) |
JB | Jump Below | CF=1 | (无符号)小于时跳转 | 无符号 |
JNAE | Jump Not Above or Equal | CF=1 | (无符号)小于时跳转 | 无符号(同 JB) |
JBE | Jump Below or Equal | CF=1 或 ZF=1 | (无符号)小于等于时跳转 | 无符号 |
JNA | Jump Not Above | CF=1 或 ZF=1 | (无符号)小于等于时跳转 | 无符号(同 JBE) |
JG | Jump Greater | ZF=0 且 SF=OF | (有符号)大于时跳转 | 有符号 |
JNLE | Jump Not Less or Equal | ZF=0 且 SF=OF | (有符号)大于时跳转 | 有符号(同 JG) |
JGE | Jump Greater or Equal | SF=OF | (有符号)大于等于时跳转 | 有符号 |
JNL | Jump Not Less | SF=OF | (有符号)大于等于时跳转 | 有符号(同 JGE) |
JL | Jump Less | SF≠OF | (有符号)小于时跳转 | 有符号 |
JNGE | Jump Not Greater or Equal | SF≠OF | (有符号)小于时跳转 | 有符号(同 JL) |
JLE | Jump Less or Equal | ZF=1 或 SF≠OF | (有符号)小于等于时跳转 | 有符号 |
JNG | Jump Not Greater | ZF=1 或 SF≠OF | (有符号)小于等于时跳转 | 有符号(同 JLE) |
JC | Jump Carry | CF=1 | 有进位时跳转 | 无符号运算 |
JNC | Jump No Carry | CF=0 | 无进位时跳转 | 无符号运算 |
JO | Jump Overflow | OF=1 | 溢出时跳转 | |
JNO | Jump No Overflow | OF=0 | 无溢出时跳转 | |
JS | Jump Sign | SF=1 | 结果为负时跳转 | |
JNS | Jump No Sign | SF=0 | 结果为正时跳转 | |
JP | Jump Parity | PF=1 | 奇偶标志为1时跳转(偶数) | |
JPE | Jump Parity Even | PF=1 | 奇偶标志为1时跳转(偶数) | |
JNP | Jump No Parity | PF=0 | 奇偶标志为0时跳转(奇数) | |
JPO | Jump Parity Odd | PF=0 | 奇偶标志为0时跳转(奇数) | |
JMP | Jump | 无条件 | 直接跳转 | |
JMPF | Jump Far | 无条件 | 跳转到指定段/内存地址 |
- CALL:调用指令,用于调用子程序或函数。
call 指令可以拆解为 push 和 jmp 两条指令的组合:
- PUSH:压栈指令,用于将数据压入堆栈。
- POP:弹栈指令,用于从堆栈中弹出数据。
- RET:返回指令,用于从子程序或函数中返回。
| |
等价于 pop eip (eip 不允许被直接修改, 此条指令不存在)
- LEAVE:离开指令,用于从子程序或函数中返回,并清理堆栈。
一般配合 ret 使用
- INT:中断指令,用于触发软件中断。
| |
- SYSCALL:系统调用指令,用于调用操作系统提供的服务。(64 位模式)
| |
- LOOP:加载操作数指令,用于将数据从内存加载到寄存器。
标志位
- TODO: 标志位的详细介绍