基础知识
原码、反码、补码
原码、反码、补码是表示带符号整数的不同表示方法。
-
原码:直接使用最高位表示数值的正负
5: 00000101
-5:10000101
-
反码
对原码的数值部分(符号位除外)取反表示负数。
+5:00000101,正数反码与原码相同,不取反。
-5:10000101,取反得到11111010
-
补码
正数的补码与原码相同。
-5:10000101,先取反11111010,然后加1得到补码11111011
十六进制
位数 | 术语 | 说明 | 常见数据类型 |
---|---|---|---|
8位 | BYTE | 基本数据单位,1字节 | char |
16位 | WORD | 2字节(通常是处理器的基本数据单位) | short |
32位 | DWORD | 4字节(双字) | int 、long |
64位 | QWORD | 8字节(四字) | long long |
128位 | (Octa Word) | 16字节(较少见,通常用于SIMD) | SIMD 寄存器 |
256位 | (YMM) | 32字节,通常用于SIMD(如AVX2) | SIMD 寄存器 |
512位 | (ZMM) | 64字节,通常用于SIMD(如AVX - 512) | SIMD 寄存器 |
x86架构
寄存器分类
通用寄存器32位
- EAX:累加寄存器,用于算术运算、I/O操作。Windows代码中通常用于存放返回值。
- EBX:基址寄存器
- ECX:计数寄存器,用于循环寄存器,移位操作。
- EDX:数据寄存器,存储乘法、除法结果
- ESI:源索引寄存器,指向源操作数,尤其是字符串操作。
- EDI:目的索引寄存器,指向目标操作数,尤其是字符串操作。
- EBP:基指针寄存器
- ESP:堆栈指针寄存器,指向栈顶。
al、ah、bl、bh、cl、ch、dl、dh
指针寄存器
-
EIP:指令指针寄存器,指向CPU正在执行的下一条指令的内存地址。在64位中,叫做RIP
EIP存在于CPU中,没有汇编指令可以直接修改它。
-
VT技术可以修改EIP
标志寄存器
缩写 | 全称 | 中文名称 | 功能说明 |
---|---|---|---|
CF | Carry Flag | 进位标志 | 如果运算的结果的最高位产生了一个进位或者借位,那么值为1,否则为0 |
ZF | Zero Flag | 零标志 | 表示运算结果是否为零 |
SF | Sign Flag | 符号标志 | 表示运算结果的符号位(可辅助判断正负,结合结果最高位,0 正、1 负等场景) |
OF | Overflow Flag | 溢出标志 | 表示加法或减法是否发生溢出(超出数据类型表示范围) |
PF | Parity Flag | 偶校验标志 | 用于检查运算结果二进制中 1 的个数是否为偶数 |
DF | Direction Flag | 方向标志 | 控制字符串操作方向(如增量/减量,决定指针移动方式) |
AF | Auxiliary Carry Flag | 辅助进位标志 | 常用于 BCD(二进制编码的十进制)运算,辅助判断低 4 位进位情况 |
- 溢出
正+正=负,有溢出
负+负=正,有溢出
正+负,无溢出
浮点寄存器
- ST-0到ST-7
- FPU:用于执行浮点数运算
调试寄存器
寄存器 | 用途说明 |
---|---|
DR0 ~ DR3 | 存储硬件断点地址,调试器可在此设断点,监视内存访问/指令执行 |
DR4, DR5 | 已保留,现代处理器中作保留字段,不再使用 |
DR6 | 调试状态寄存器,存储断点触发状态信息,用于判断哪个断点被触发 |
DR7 | 调试控制寄存器,设置硬件断点条件(读/写/执行等)及权限(全局/本地断点) |
FS和TEB寄存器
- fs[0]:默认值是TEB的基地址
x64寄存器参数顺序
rdi:第 1 个参数
rsi:第 2 个参数
rdx:第 3 个参数
rcx:第 4 个参数
r8:第 5 个参数
r9:第 6 个参数
内存分布

指令
操作符
算术操作符
操作符 | 功能说明 | 示例代码 |
---|---|---|
add | 加法 | add eax, ebx |
sub | 减法 | sub eax, ecx |
mul | 无符号乘法 | mul ecx |
imul | 有符号乘法 | imul eax, ecx |
div | 无符号除法 | div ecx |
idiv | 有符号除法 | idiv ecx |
inc | 自增(increase) | inc eax |
dec | 自减(decrease) | dec eax |
除法
asm86
div ax,r/m8 ;ax除以r/m8,al是商,ah是余数。
div eax,r/m32 ;eax是商,edx是余数。
位操作符
操作符 | 功能 | 示例 | 说明 |
---|---|---|---|
AND | 按位与 | ||
OR | 按位或 | ||
XOR | 按位异或 | ||
SHL | 逻辑左移 | shl eax,1 | 所有位向左移动一位,空出位置填0,最左边丢弃 |
SHR | 逻辑右移 | ||
sal | 有符号左移 | ||
sar | 有符号右移 | ||
rol | 循环左移 | rol eax,1 | 所有位向左移动一位,最左边移动到末尾 |
ror | 循环右移 | ror eax,1 |
数据传输指令
操作符 | 功能描述 | 示例代码 |
---|---|---|
mov | 将数据从源传输到目的位置 | mov eax, ebx |
push | 将数据压入栈 | push eax |
pop | 从栈中弹出数据到目的位置 | pop eax |
xchg | 交换两个操作数的值 | xchg eax, ebx |
lea | 取地址 | lea eax, [ebx+4] |
逻辑比较指令
操作符 | 功能描述 | 示例代码 |
---|---|---|
cmp | 比较,减法 | cmp eax, ebx |
test | (按位与,不存结果,仅设置标志) | test eax, eax |
bash
CPM eax,ebx // eax-ebx
//如果eax > ebx,通过JG跳转。
CF:(Carry Flag)
设置条件:发生借位,第一个操作数小于第二个。
清楚条件:未发生借位。
ZF:(Zero Flag)
设置条件:两个操作数相等(即结果为0)
清除条件:两个操作数不相等。
SF:(Sign Flag)
设置条件:结果为负(即最高有效位为1)
清除条件:结果为正或零
OF:(Overflow Flag)
设置条件:有符号比较时发生溢出(即正数减负数得到负数,或负数减正数得到正数)
清除条件:未发生溢出
PF:(Parity Flag)
设置条件:结果的最低字节中,1的位数是偶数。
清除条件:结果的最低字节中,1的位数是奇数。
AF:(Auxiliary Carry Flag)
设置条件:在低4位操作中发生借位。
清除条件:未发生借位。
bash
test eax,eax //若eax为0,JZ跳转
test eax,0x01 //若eax最低为为1,JZ跳转
控制转移指令
操作符 | 功能说明 | 依赖标志位及条件 | 示例代码 |
---|---|---|---|
call | 调用子程序(执行完返回原位置) | 无(直接调用,自动压栈返回地址) | call 0x00401000 |
ret | 返回调用点(配合 call) | 无(从栈弹出返回地址) | ret |
jmp | 无条件跳转(不依赖标志位) | 无(直接跳转) | jmp 0x00401000 |
jb | CF==1 | ||
jbe | CF1 or ZF1 | ||
je/jz | 相等/零标志置位时跳转 | ZF= 1(结果为 0/相等) | je 0x00401020 |
jne/jnz | 不等/零标志未置位时跳转 | ZF= 0(结果非 0/不等) | jne 0x00401020 |
jle/jng | ZF==1 or SF!=OF | ||
jg | 大于时跳转(有符号比较) | SF= OF or ZF=0 | jg 0x00401020 |
jl | 小于时跳转(有符号比较) | SF≠ OF | jl 0x00401020 |
jge/jnl | 大于等于时跳转(有符号比较) | SF= OF or ZF=1 | jge 0x00401020 |
jle | 小于等于时跳转(有符号比较) | SF≠ OF or ZF=1 | jle 0x00401020 |
loop | 循环跳转(配合计数器) | 计数寄存器(ECX)减 1 后 ≠ 0 | loop 0x00401020 |
int | 调用中断(触发系统/硬件中断) | 无(主动触发中断) | int 0x80 |
iret | 从中断返回(恢复状态) | 无(恢复 EFLAGS/CS/EIP 等) | iret |
栈操作指令
指令 | 功能描述 | ESP 变化(32位环境) | 补充说明 & 场景 |
---|---|---|---|
PUSH | 将寄存器、立即数、内存值压入栈顶(如 PUSH EAX PUSH 0x123 ) |
ESP -= 4 |
常用于保存临时数据、函数调用传参 |
POP | 从栈顶弹出数据,存入寄存器/内存(如 POP EBX ) |
ESP += 4 |
配合 PUSH 恢复数据,需注意栈平衡 |
PUSHAD | 按顺序压入通用寄存器:EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI |
ESP -= 32 (8个寄存器×4字节) |
快速保存全部通用寄存器(调试/上下文切换) |
POPAD | 按顺序弹出恢复通用寄存器:EDI, ESI, EBP, ESP(跳过), EBX, EDX, ECX, EAX |
ESP += 32 |
与 PUSHAD 配对,注意 ESP 会被跳过 |
PUSHFD | 将32位标志寄存器(EFLAGS)压入栈 | ESP -= 4 |
保存标志位状态(如调试、异常处理) |
POPFD | 从栈弹出值恢复32位EFLAGS寄存器 | ESP += 4 |
与 PUSHFD 配对,恢复标志位 |
CALL 地址 | 调用子程序:压入返回地址(EIP)→ 跳转到目标地址 | ESP -= 4 (压入返回地址) |
函数调用核心指令,返回地址用于 RET |
RET | 从栈弹出返回地址 → 跳转到该地址(可选带立即数平衡栈,如 RET 4 ) |
ESP += 4 (或更多) |
函数返回,RET N 用于跳过函数调用前压入的参数 |
ENTER | 设置栈帧:压入旧 EBP → 新 EBP = ESP → 分配局部变量空间(如 ENTER 8,0 ) |
ESP -= 4 + 局部变量大小 |
替代 MOV EBP, ESP + SUB ESP, N ,较少用 |
LEAVE | 恢复栈帧:ESP = EBP → 弹出旧 EBP |
ESP += 局部变量大小 + 4 |
与 ENTER 配对,快速清理栈帧 |
INT imm8 | 软中断:压入 EFLAGS、CS、EIP → 跳转到中断向量表 | ESP -= 12 (32位环境) |
触发系统调用(如 INT 0x80 是Linux传统系统调用) |
字符串操作指令
mov
nasm
mov eax,offset str_hello; 加载"hello"字符串的地址到EAX
mov [edi],eax ; 把这个地址存入到EDI指向的位置
movs
movs
是一组字符串操作指令,表示"移动字符串数据"。
nasm
movsb:移动一个字节
movsw:移动一个字
movsd:移动一个双字
movsq:(64位模式下使用)移动一个四字
repmovsb
rep movsb
:批量复制字符串数据。
nasm
mov ecx,length ;设置要复制的字节数
mov esi,source ;源字符串地址
mov edi,destination;目标字符串地址
rep movsb ;批量复制字符串
rep系列指令
nasm
rep:重复执行后续指令,直到ECX或CX寄存器的值为0
repe/repz:(Repeat While Equal/Repeat While Zero)两者等价在ZF为1,且ECX不为0时执行。
repne/repnz:(Repeat While Not Equal/Repeat While Not Zero)两者等价,当ZF不为0,且ECX不为0时执行。
cmp指令
nasm
mov esi,offset str_userinput ; 用户输入字符串
mov edi,offset str_password ; 目标字符串
mov ecx,length ; 设置比较的字节数
cld ; 清除方向标志
repe cmpsb ; 比较两段内存的字符串
je match ; 如果相等跳转
scas指令
扫描字符串中的特定字符
扫描方向由DF确定,cld清除DF从低地址扫描,std设置DF从高地址扫描。
nasm
mov edi,offset str_buffer ; EDI指向要扫描的字符串
mov al,'A' ; 查找指定字符
mov ecx,length_of_string ; ECX设置为字符串长度
repne scasb ; 扫描直到找到'A'或结束
jnz not_found ; 未找到执行
xxxx ; 找到执行
stos指令
向目标地址填充数据,通常用于清空字符串或填充特定值。
nasm
mov edi,offset buffer ; 目标地址
mov al,0 ; 填充为0
mov ecx,size ; 填充大小
cld ; 设置方向标志递增
req stosb ; 填充字符串
loads指令
nasm
lodsb:加载一个字节到AL,并将ESI自增1。
lodsw:加载一个字到AX,并将ESI自增2.
lodsd:加载一个双字到EAX,并将ESI自增4.
堆栈中的字符串操作
nasm
push offset str_hello ;将"hello"的地址压栈。
call printf ;调用函数
add esp,4 ;平衡堆栈
浮点运算指令
FPU
FPU(浮点运算单元)
- 定义:FPU是处理器中一个硬件模块,专门负责浮点运算。
- 功能:处理浮点数的加减乘除,平方根,三角函数等复杂操作。
- 实现:现在FPU集成到CPU内核中。
FPU组成
- 寄存器堆栈:8个80位宽的浮点寄存器(ST0-ST7),以堆栈形式组织。
- 通过push和pop操作
x87指令集
- 定义:x87是专门位FPU涉及的一套指令集,早期专用于处理浮点运算。
- 特点:
- 操作对象是FPU的堆栈寄存器
- 指令风格是Fxxx开头。
- 支持扩展精度(80位),比SSE的单精度和双精度浮点数高。
加载与存储指令
指令 | 功能描述 |
---|---|
FLD |
从内存加载浮点数到 FPU 栈顶(支持单精度、双精度、长双精度) |
FST |
将 FPU 栈顶浮点数复制到内存(栈顶数据保留) |
FSTP |
将 FPU 栈顶浮点数存储并弹出(栈顶数据移除,栈指针 +1) |
FLDENV |
从内存加载 FPU 环境(控制字、状态字、标记字等) |
FSTENV |
将 FPU 环境(控制字、状态字等)保存到内存 |
FLDCW |
从内存加载 FPU 控制字(设置舍入模式、精度等) |
FSTCW |
将 FPU 控制字保存到内存 |
算术运算指令
指令 | 功能描述 |
---|---|
FADD |
栈顶两数相加(FADD st(0), st(1) 或简写 FADD ,结果存栈顶) |
FSUB |
栈顶两数相减(FSUB st(0), st(1) → st(0)-st(1) ,结果存栈顶) |
FSUBR |
反向相减(FSUBR st(0), st(1) → st(1)-st(0) ,结果存栈顶) |
FMUL |
栈顶两数相乘(结果存栈顶) |
FDIV |
栈顶两数相除(FDIV st(0), st(1) → st(0)/st(1) ,结果存栈顶) |
FDIVR |
反向相除(FDIVR st(0), st(1) → st(1)/st(0) ,结果存栈顶) |
FABS |
取栈顶数的绝对值 |
FNEG |
取栈顶数的相反数 |
FSQRT |
计算栈顶数的平方根 |
FSCALE |
栈顶数乘以 2 的(次顶数)次方(st(0) *= 2^st(1) ,然后弹出次顶数) |
转换指令
指令 | 功能描述 |
---|---|
FIST |
将栈顶浮点数复制为整数存入内存(按当前 FPU 控制字舍入) |
FISTP |
将栈顶浮点数转换为整数并存出,然后弹出栈顶(清理栈空间) |
FCVT |
浮点数类型转换(如单精度→双精度,或反向) |
FILD |
从内存加载整数到 FPU 栈顶(自动转换为浮点数) |
FIST |
将栈顶浮点数转换为整数并存入内存(同 FIST ,需区分操作数大小) |
控制与比较指令
指令 | 功能描述 |
---|---|
FCOM |
比较栈顶两数(st(0) vs st(1) ),设置 FPU 状态位(不修改栈) |
FCOMI |
比较栈顶两数,结果写入 CPU 通用寄存器 EFLAGS (支持 JE /JL 等指令) |
FUCOM |
同 FCOM ,但处理非规范数(NaN、无穷大) |
FUCOMI |
同 FCOMI ,但处理非规范数(NaN、无穷大) |
FTST |
比较栈顶数与 0(判断正负、是否为 0) |
FBSTP |
将栈顶浮点数转换为 BCD 码(十进制)并存入内存,然后弹出栈 |
FINCSTP |
递增 FPU 栈指针(手动调整栈顶,风险高) |
FDECSTP |
递减 FPU 栈指针(手动调整栈顶,风险高) |
特殊指令
指令 | 功能描述 |
---|---|
FNINIT |
初始化 FPU(重置控制字、状态字等) |
FNOP |
空操作(无实际功能,用于填充指令周期) |
FWAIT |
等待 FPU 完成当前指令(旧版兼容,现代 CPU 多自动同步) |
FXSAVE |
保存 SSE/FPU 状态到内存(支持 SIMD 扩展) |
FXRSTOR |
从内存恢复 SSE/FPU 状态(支持 SIMD 扩展) |
SSE
- 定义:SSE是Intel在x86处理器上引用的一套SIMD(单指令多数据)扩展指令集。
- 特点:
- 采用xmm寄存器(每个128位宽)。
- 支持并行处理多个单精度(32位)或双精度(64位)浮点数。
- 指令风格是 xxxPs(处理单精度矢量)或xxxSD(处理双精度标量)。
数据扩展与传送指令
指令 | 全称 | 功能描述 | 指令格式示例 | 适用场景 |
---|---|---|---|---|
movsx |
Move with Sign Extension | 将窄宽度有符号数扩展为宽宽度有符号数(高位填充符号位) | movsx 存储位置, 源操作数 |
有符号整数类型转换(如char→int ) |
movzx |
Move with Zero Extension | 将窄宽度无符号数扩展为宽宽度数(高位填充0) | movzx reg_dest, reg_src/mem_src |
无符号整数类型转换(如unsigned char→unsigned int ) |
cbw |
Convert Byte to Word | 将8位寄存器al 中的有符号数扩展为16位ax (al 符号位扩展到ah ) |
cbw (无操作数,固定使用al 和ax ) |
int8_t→int16_t 转换 |
cwd |
Convert Word to Doubleword | 将16位寄存器ax 中的有符号数扩展为32位dx:ax (ax 符号位扩展到dx ) |
cwd (无操作数,固定使用ax 和dx:ax ) |
int16_t→int32_t 转换 |
cdq |
Convert Doubleword to Quadword | 将32位寄存器eax 中的有符号数扩展为64位edx:eax (eax 符号位扩展到edx ) |
cdq (无操作数,固定使用eax 和edx:eax ) |
int32_t→int64_t 转换 |
cqo |
Convert Quadword to Octword | 将64位寄存器rax 中的有符号数扩展为128位rdx:rax (rax 符号位扩展到rdx ) |
cqo (无操作数,固定使用rax 和rdx:rax ) |
int64_t→int128_t 转换 |
函数
函数特征代码
nasm
;函数头部
push ebp ;保存上一函数的栈基指针。
mov ebp,esp ;设置当前栈帧基址。
sub esp,0x10;为局部变量分配空间
;函数退出
mov esp,ebp ;
pop ebp ;
ret ;
函数参数和局部变量
nasm
push 3 ;第二个参数
push 2 ;第一个参数
call add ;如果发现call上面有push,那些就是函数调用用到的参数。
当在内层函数看到ebp + xxx
是函数的参数;ebp - xxx
是函数的局部变量。
局部变量在栈上的位置(下图)
- x64
nasm
lea rdx, [rbp+input_flag]//参数
lea rax, [rbp+var_90] //参数
mov rsi, rdx
mov rdi, rax
call function
调用约定
cdecl
又叫做c调用约定,在Windows下都是外平栈。
特点:
- 参数传递:参数从右到左依次压栈
- 栈清理:由调用者(Caller)清理栈(add esp,x)
- 返回值:eax寄存器
- 常见场景:c语言默认约定
nasm
push 3 ;第三个参数
push 2 ;
push 1 ;第一个参数
call my_function
add esp,0xC ;调用者清理栈(3个参数 x 4个字节 = 12个字节)
my_function:
push ebp ;保存旧的栈帧基址
mov ebp,esp ;建立新栈帧
sub esp,8 ;分配局部变量空间
mov dword ptr [ebp - 4],10 ;局部变量a = 10
mov dword ptr [ebp - 8],20 ;局部变量b = 20
; 访问参数
mov eax,[ebp + 8] ;访问第一个参数
mov eax,[ebp + 12] ;访问第二个参数
mov eax,[ebp + 16] ;访问第三个参数
;尾部
mov esp,ebp ;恢复栈指针
pop ebp ;恢复旧的栈帧基址
ret ;返回(无栈清理)
stdcall
叫做标准调用约定,windows下内平栈
特点:
- 参数传递:参数从右到左压栈
- 栈清理:由被调用者清理栈
- 返回值:eax寄存器
- 常见场景:Windows API
nasm
push 3 ;第三个参数
push 2 ;
push 1 ;第一个参数
call my_function
;无需清理栈
my_function:
push ebp ;保存旧的栈帧基址
mov ebp,esp ;建立新栈帧
sub esp,8 ;分配局部变量空间
mov dword ptr [ebp - 4],10 ;局部变量a = 10
mov dword ptr [ebp - 8],20 ;局部变量b = 20
; 访问参数
mov eax,[ebp + 8] ;访问第一个参数
mov eax,[ebp + 12] ;访问第二个参数
mov eax,[ebp + 16] ;访问第三个参数
;尾部
mov esp,ebp ;恢复栈指针
pop ebp ;恢复旧的栈帧基址
ret 0xC ;被调用者清理栈
fastcall
windows下内平栈
特点:
- 参数传递:前两个参数通过寄存器(ecx和edx),其余参数从右到左压栈
- 栈清理:由被调用者清理栈(同stdcall)
- 返回值:eax寄存器
- 常见场景:性能敏感的函数
nasm
mov ecx,1 ;第一个参数,放入ecx
mov edx,2 ;第二个参数,放入edx
push 3 ;第三个参数(最右侧)
call my_function ;
my_function:
push ebp ;保存旧的栈帧基址
mov ebp,esp ;建立新栈帧
sub esp,4 ;分配局部变量空间
; 访问参数
mov eax,ecx ;访问第一个参数
mov eax,edx ;访问第二个参数
mov eax,[ebp + 8] ;访问第三个参数
;尾部
mov esp,ebp ;恢复栈指针
pop ebp ;恢复旧的栈帧基址
ret 4 ;被调用者清理栈
thiscall
特点:
- 参数传递:this指针通过ecx传递,其余参数从右到左入栈
- 栈清理:由调用者或者编译器确定
- 返回值:eax寄存器
- 常见场景:C++类成员函数
nasm
mov ecx,obj_ptr ;this指针通过ecx传递,其余参数从右到左压栈
push 2 ;第二个参数
push 1 ;第一个参数
call obj.method ;调用成员函数
add esp,8 ;调用者清栈