跳到主要内容

基础指令

本章介绍 x86 汇编中最常用的基础指令,包括数据传送、算术运算、逻辑运算等。

数据传送指令

MOV 指令

MOV 是最基本的数据传送指令,用于在寄存器和内存之间复制数据。

; 寄存器到寄存器
mov eax, ebx ; 将 EBX 的值复制到 EAX
mov rax, rbx ; 64 位传送

; 立即数到寄存器
mov eax, 100 ; 将 100 加载到 EAX
mov rax, 0x12345678 ; 将十六进制数加载到 RAX

; 寄存器到内存
mov [ebx], eax ; 将 EAX 存储到 EBX 指向的内存
mov dword [rsi], 100 ; 将 100 存储到 RSI 指向的内存

; 内存到寄存器
mov eax, [ebx] ; 将 EBX 指向的内存值加载到 EAX
mov eax, [value] ; 将变量 value 的值加载到 EAX

MOV 指令的限制:

; 不能直接在内存之间传送
; mov [ebx], [esi] ; 错误!

; 正确做法:通过寄存器中转
mov eax, [esi]
mov [ebx], eax

; 不能直接将立即数传送到段寄存器
; mov ds, 0x1000 ; 错误!

; 正确做法
mov ax, 0x1000
mov ds, ax

MOV 扩展指令

MOVZX - 零扩展传送

将较小的操作数零扩展后传送到较大的目标:

movzx eax, al          ; 8 位 -> 32 位,高位填 0
movzx eax, ax ; 16 位 -> 32 位
movzx rax, byte [rsi] ; 8 位 -> 64 位

; 示例
mov al, 0xFF
movzx eax, al ; EAX = 0x000000FF = 255

MOVSX - 符号扩展传送

将较小的操作数符号扩展后传送到较大的目标:

movsx eax, al          ; 8 位 -> 32 位,高位填符号位
movsx eax, ax ; 16 位 -> 32 位
movsx rax, byte [rsi] ; 8 位 -> 64 位

; 示例
mov al, -5 ; AL = 0xFB
movsx eax, al ; EAX = 0xFFFFFFFB = -5

MOVSXD - 符号扩展双字到四字

专门用于 32 位到 64 位的符号扩展:

movsxd rax, eax        ; 32 位 -> 64 位符号扩展
movsxd rax, dword [rsi]

交换指令

XCHG - 交换

交换两个操作数的值:

xchg eax, ebx          ; 交换 EAX 和 EBX
xchg [mem], eax ; 交换内存和寄存器

; 原子操作(带 LOCK 前缀)
lock xchg [mem], eax ; 原子交换

BSWAP - 字节交换

反转寄存器中的字节顺序:

mov eax, 0x12345678
bswap eax ; EAX = 0x78563412(字节序转换)

栈操作指令

PUSH - 压栈

将操作数压入栈:

push rax               ; RSP -= 8,将 RAX 存入栈
push qword [mem] ; 将内存值压栈
push 100 ; 将立即数压栈

; 32 位模式
push eax ; ESP -= 4

POP - 出栈

从栈中弹出数据:

pop rax                ; 从栈读取到 RAX,RSP += 8
pop qword [mem] ; 弹出到内存

PUSHF/POPF - 标志寄存器操作

pushf                  ; 压入 FLAGS(16 位)
pushfd ; 压入 EFLAGS(32 位)
pushfq ; 压入 RFLAGS(64 位)

popf ; 弹出到 FLAGS
popfd ; 弹出到 EFLAGS
popfq ; 弹出到 RFLAGS

地址加载指令

LEA - 加载有效地址

计算有效地址而不是加载内存值:

lea eax, [ebx + ecx*4 + 0x100]  ; EAX = EBX + ECX*4 + 0x100

; 常用于获取变量地址
lea rax, [buffer] ; RAX = buffer 的地址

; 常用于算术运算
lea eax, [eax + eax*2] ; EAX = EAX * 3
lea eax, [eax + eax*4] ; EAX = EAX * 5
lea eax, [eax + eax*8] ; EAX = EAX * 9

LEA 与 MOV 的区别:

mov eax, [ebx]         ; 将 EBX 指向的内存值加载到 EAX
lea eax, [ebx] ; 将 EBX 的值加载到 EAX(相当于 mov eax, ebx)

mov eax, [ebx + 4] ; 加载内存值
lea eax, [ebx + 4] ; EAX = EBX + 4(计算地址)

算术运算指令

加法指令

ADD - 加法

add eax, ebx           ; EAX = EAX + EBX
add eax, 100 ; EAX = EAX + 100
add [mem], eax ; 内存值 += EAX
add eax, [mem] ; EAX += 内存值

; 影响标志位:CF, ZF, SF, OF, AF, PF

ADC - 带进位加法

用于多精度加法:

; 64 位加法(在 32 位模式下)
add eax, [low] ; 加低 32 位
adc edx, [high] ; 加高 32 位(带进位)

; 128 位加法
add rax, rbx ; 加低 64 位
adc rdx, rcx ; 加高 64 位(带进位)

INC - 自增

inc eax                ; EAX++
inc byte [mem] ; 内存字节值加 1

; 注意:INC 不影响 CF 标志

减法指令

SUB - 减法

sub eax, ebx           ; EAX = EAX - EBX
sub eax, 100 ; EAX = EAX - 100
sub [mem], eax ; 内存值 -= EAX

; 影响标志位:CF, ZF, SF, OF, AF, PF

SBB - 带借位减法

用于多精度减法:

sub eax, [low]         ; 减低 32 位
sbb edx, [high] ; 减高 32 位(带借位)

DEC - 自减

dec eax                ; EAX--
dec byte [mem] ; 内存字节值减 1

; 注意:DEC 不影响 CF 标志

取补指令

NEG - 取补(取负)

neg eax                ; EAX = -EAX(取二进制补码)

; 示例
mov eax, 5
neg eax ; EAX = -5

mov eax, -5
neg eax ; EAX = 5

比较指令

CMP - 比较

执行减法但不保存结果,只设置标志位:

cmp eax, ebx           ; 比较 EAX 和 EBX
cmp eax, 100 ; 比较 EAX 和 100
cmp [mem], eax ; 比较内存值和 EAX

; 根据结果设置标志位
; EAX == EBX -> ZF = 1
; EAX < EBX -> CF = 1(无符号)或 SF ≠ OF(有符号)
; EAX > EBX -> CF = 0 且 ZF = 0

常用比较后的条件跳转:

cmp eax, ebx
je equal ; 相等跳转
jne not_equal ; 不相等跳转
jl less ; 小于跳转(有符号)
jg greater ; 大于跳转(有符号)
jb below ; 小于跳转(无符号)
ja above ; 大于跳转(无符号)

乘法指令

MUL - 无符号乘法

; 8 位乘法:AL * r/m8 -> AX
mul bl ; AX = AL * BL

; 16 位乘法:AX * r/m16 -> DX:AX
mul bx ; DX:AX = AX * BX

; 32 位乘法:EAX * r/m32 -> EDX:EAX
mul ebx ; EDX:EAX = EAX * EBX

; 64 位乘法:RAX * r/m64 -> RDX:RAX
mul rbx ; RDX:RAX = RAX * RBX

IMUL - 有符号乘法

; 单操作数形式(与 MUL 相同格式)
imul ebx ; EDX:EAX = EAX * EBX(有符号)

; 双操作数形式
imul eax, ebx ; EAX = EAX * EBX
imul eax, [mem] ; EAX = EAX * 内存值
imul eax, 100 ; EAX = EAX * 100

; 三操作数形式
imul eax, ebx, 100 ; EAX = EBX * 100
imul rax, rbx, 10 ; RAX = RBX * 10

除法指令

DIV - 无符号除法

; 8 位除法:AX / r/m8 -> AL=商, AH=余数
div bl ; AX / BL

; 16 位除法:DX:AX / r/m16 -> AX=商, DX=余数
div bx ; DX:AX / BX

; 32 位除法:EDX:EAX / r/m32 -> EAX=商, EDX=余数
div ebx ; EDX:EAX / EBX

; 64 位除法:RDX:RAX / r/m64 -> RAX=商, RDX=余数
div rbx ; RDX:RAX / RBX

IDIV - 有符号除法

idiv ebx               ; EDX:EAX / EBX(有符号)

除法注意事项:

; 除法前必须正确设置高位寄存器
mov eax, 100
xor edx, edx ; 清零 EDX(无符号)
div ebx

mov eax, -100
cdq ; 符号扩展 EAX 到 EDX:EAX(有符号)
idiv ebx

逻辑运算指令

基本逻辑运算

AND - 与运算

and eax, ebx           ; EAX = EAX & EBX
and eax, 0xFF ; EAX = EAX & 0xFF(掩码操作)

; 清零特定位
and eax, ~0x0F ; 清零低 4 位

; 测试特定位
and eax, 0x80
jnz bit_set ; 如果第 7 位为 1,跳转

OR - 或运算

or eax, ebx            ; EAX = EAX | EBX
or eax, 0x0F ; 设置低 4 位

; 设置特定位
or eax, 0x80 ; 设置第 7 位

XOR - 异或运算

xor eax, ebx           ; EAX = EAX ^ EBX
xor eax, eax ; EAX = 0(快速清零)

; 翻转特定位
xor eax, 0x80 ; 翻转第 7 位

NOT - 取反

not eax                ; EAX = ~EAX(按位取反)

TEST 指令

执行 AND 运算但不保存结果,只设置标志位:

test eax, eax          ; 测试 EAX 是否为 0
jz is_zero ; 如果为 0,跳转

test al, 0x80 ; 测试 AL 的第 7 位
jnz bit_set ; 如果第 7 位为 1,跳转

test eax, 0x0F ; 测试低 4 位是否有 1
jnz has_bits ; 如果有,跳转

移位指令

逻辑移位

SHL/SAL - 左移

shl eax, 1             ; EAX 左移 1 位(乘以 2)
shl eax, 4 ; EAX 左移 4 位(乘以 16)
shl eax, cl ; EAX 左移 CL 位

; 示例:乘以 8
mov eax, 10
shl eax, 3 ; EAX = 80

SHR - 逻辑右移

shr eax, 1             ; EAX 右移 1 位(无符号除以 2)
shr eax, 4 ; EAX 右移 4 位

; 示例:无符号除以 4
mov eax, 100
shr eax, 2 ; EAX = 25

算术移位

SAR - 算术右移

保持符号位不变:

sar eax, 1             ; EAX 右移 1 位(有符号除以 2)

; 示例:有符号除以 4
mov eax, -100
sar eax, 2 ; EAX = -25

循环移位

ROL - 循环左移

rol eax, 1             ; 循环左移 1 位

; 示例
mov al, 0x85 ; AL = 10000101
rol al, 1 ; AL = 00001011(最高位移到最低位)

ROR - 循环右移

ror eax, 1             ; 循环右移 1 位

RCL/RCR - 带进位循环移位

rcl eax, 1             ; 带进位循环左移
rcr eax, 1 ; 带进位循环右移

小结

本章介绍了 x86 的基础指令:

  • 数据传送:MOV、MOVZX、MOVSX、XCHG、PUSH、POP、LEA
  • 算术运算:ADD、ADC、SUB、SBB、INC、DEC、NEG、CMP、MUL、IMUL、DIV、IDIV
  • 逻辑运算:AND、OR、XOR、NOT、TEST
  • 移位操作:SHL、SHR、SAR、ROL、ROR

这些指令是编写汇编程序的基础,下一章将学习控制流指令。