一. 汇编基础
1.1 计算机架构
| 架构 | 位数 | 指令集 | 特点 | 常见设备 |
|---|---|---|---|---|
| x86 | 32位 | CISC | 老架构,兼容性好 | 老电脑 |
| x86-64(AMD64) | 64位 | CISC | 当前PC主流,性能强 | 台式机、笔记本 |
| ARM | 32位 | RISC | 低功耗 | 移动设备 |
| ARM64 | 64位 | RISC | 高性能低功耗 | 新型笔记本、平板 |
32位程序的内存地址都是4个字节,64位程序的内存地址都是8个字节
1.2 汇编基础
1.2.1 CPU(中央处理器)
- CPU是计算机的“大脑”,负责指挥整个计算机系统的工作。它从内存中读取指令,并根据这些指令进行计算和数据处理。
1.2.2 寄存器(Registers)
- 寄存器是 CPU 内部的高速存储单元。它用于存放临时的数据和计算的中间结果,具有非常快的访问速度。
- 寄存器的数量和种类有限,但它们比内存更快,因为它们直接位于CPU 内部。
- 常见的寄存器有:
- 通用寄存器(如
EAX、EBX) - 指针寄存器(如
ESP、EBP) - 标志寄存器(如
EFLAGS)
- 通用寄存器(如
1.2.3内存(RAM 主存储器)
- 内存用于存放正在运行的程序的数据和指令。与寄存器相比,内存空间要大得多,但访问速度较慢。
- CPU 会从内存中读取指令,并在内存中读取或写入数据。
- 内存通过内存总线与 CPU 和其他硬件组件进行连接。
1.3 寄存器
1.3.1 32位
在32位 x86 架构中,有8个主要通用寄存器:
| 寄存器 | 名称 | 主要作用 | PWN/逆向中的用途 |
|---|---|---|---|
| EAX | 累加寄存器 | 存放计算结果、函数返回值 | 保存返回值、系统调用编号 |
| EBX | 基址寄存器 | 存放基地址、内存地址 | 保存数据地址 |
| ECX | 计数寄存器 | 循环计数、字符串操作 | 循环次数、rep指令 |
| EDX | 数据寄存器 | 辅助计算、存放数据 | 乘除法高32位 |
| ESI | 源变址寄存器 | 源地址指针 | 字符串复制源地址 |
| EDI | 目的变址寄存器 | 目标地址指针 | 字符串复制目标地址 |
| ESP | 栈指针寄存器 | 指向当前栈顶 | 栈操作、栈溢出 |
| EBP | 栈基址寄存器 | 指向当前栈底 | 函数栈帧、调试 |
1.3.2 标志寄存器
| 标志位 | 全称 | 说明 |
|---|---|---|
| CF | Carry Flag(进位标志) | 表示加法或移位操作是否产生进位 |
| ZF | Zero Flag(零标志) | 表示运算结果是否为零 |
| SF | Sign Flag(符号标志) | 表示运算结果的符号位 |
| OF | Overflow Flag(溢出标志) | 表示加法或减法是否发生了溢出 |
| PF | Parity Flag(偶校验标志) | 用于检查结果是否具有偶数个 1 |
| DF | Direction Flag(方向标志) | 用于字符串操作时的方向控制(增量或减量) |
| AF | Auxiliary Carry Flag(辅助进位标志) | 用于 BCD 运算 |
1.3.3fs寄存器
作用
- fs 用来定位当前线程的线程环境块 TEB(Thread Environment Block)
fs:[0]
- 存储当前线程 TEB 的地址
1.3.4 TEB
线程环境块:是 Windows 为每一个线程创建的数据结构。
在 32 位 Windows 中,TEB 结构大致如下:
typedefstruct_TEB{NT_TIB NtTib;// +0x0000 TIB用于线程基础信息CLIENT_ID ClientId;// +0x0020 当前线程的 ClientId(包含进程 ID 和线程 ID)PVOID ThreadLocalStoragePointer;// +0x002C 指向线程局部存储(TLS)的指针PVOID ProcessEnvironmentBlock;// +0x0030 指向进程环境块(PEB)的指针ULONG LastErrorValue;// +0x0034 最近错误值// 其他字段...}TEB;
1.3.5 PEB
进程环境块:一个进程是由多个线程构成的
在 32 位 Windows 中,TEB 结构大致如下:
typedefstruct_PEB{UCHAR BeingDebugged;// +0x002 是否正在调试 这两个都是用来检测的ULONG NtGlobalFlag;// +0x068 Windows调试相关全局标志// ...}PEB;
1.4dbg快捷键
| 快捷键 | 功能 |
|---|---|
| F9 | 运行程序直到断点(运行) |
| F7 | 进入函数内部(步入) |
| F8 | 执行当前指令,不进入函数(步过) |
| F4 | 运行到光标位置 |
| F12 | 暂停程序 |
| Ctrl + F2 | 重启程序(清除全部断点) |
| F2 | 设置/取消断点 |
| Ctrl + G | 跳转到地址 |
| Space | 修改汇编代码 |
| Enter | 跳转查看当前光标所在的地址 |
| ; | 添加注释 |
| + / - | 进函数/出函数 |
二. 汇编指令
2.1 数据传输指令
| 操作符 | 功能 | 示例 |
|---|---|---|
mov | 将数据从源传输到目的位置 | mov eax, ebx |
push | 将数据压入栈 | push eax |
pop | 从栈中弹出数据到目的位置 | pop eax |
xchg | 交换两个操作数的值 | xchg eax, ebx |
lea | 取地址 | lea eax, [ebx+4](中括号代表取地址) |
2.2 算数运算指令
| 操作符 | 功能 | 示例 |
|---|---|---|
add | 加法 | add eax, ebx |
sub | 减法 | sub eax, ecx |
mul | 无符号乘法 | mul ecx |
imul | 有符号乘法 | imul eax, ecx |
div | 无符号除法 | div ecx |
idiv | 有符号除法 | idiv ecx |
inc | 自增1 | inc eax |
dec | 自减1 | dec eax |
| 指令 | 类型 | 输入 | 输出 |
|---|---|---|---|
mul ecx | 无符号乘法 | EAX × ECX | EDX:EAX |
imul ecx | 有符号乘法 | EAX × ECX | EDX:EAX |
imul eax, ecx | 有符号乘法 | EAX × ECX | EAX |
imul eax, ecx, 10 | 有符号乘法 | ECX × 10 | EAX |
div ecx | 无符号除法 | EDX:EAX ÷ ECX | EAX=商,EDX=余数 |
idiv ecx | 有符号除法 | EDX:EAX ÷ ECX | EAX=商,EDX=余数 |
2.3 位运算指令
2.3.1 逻辑运算指令
| 指令 | 格式 | 功能说明 | 特殊要求 |
|---|---|---|---|
and | and target, source | 按位与:1 & 1 = 1,其余为0,结果保存到target | 操作数要求同MOV |
or | or target, source | 按位或:`1 | 1 = 1,1 |
xor | xor target, source | 按位异或:相同为0,不同为1,结果保存到target | 同上 |
not | not target | 按位非:1 → 0,0 → 1,结果保存到target | 操作数不能是立即数 |
注意事项:
- 通常使用AND指令对指定位清0
- 通常使用OR指令对指定位置1
- 通常使用NOT指令将某个数据取反码,再加1就得到补码
- 通常使用XOR指令来取反某位
- 非运算对标志位不影响,其余指令对标志均会产生影响
2.3.2 移位运算
- 非循环移位运算
| 指令 | 格式 | 类型 | 移动方向 | 补位方式 | 标志位影响 |
|---|---|---|---|---|---|
| SHL(逻辑左移) | SHL eax, 1 | 无符号数 | 左移 | 低位补0,最高位移入 CF | 移位后若 CF ≠ 最高位,则 OF=1,否则 OF=0 |
| SAL(算术左移) | SAL eax, 1 | 有符号数 | 左移 | 低位补0,最高位移入 CF | 与 SHL 相同(操作完全相同) |
| SHR(逻辑右移) | SHR eax, 1 | 无符号数 | 右移 | 高位补0,最低位移入 CF | — |
| SAR(算术右移) | SAR eax, 1 | 有符号数 | 右移 | 高位补符号位(原最高位),最低位移入 CF | — |
- 循环移位
| 指令 | 格式 | 名称 | 功能说明 |
|---|---|---|---|
| ROL | ROL eax, 1 | 循环左移(不带进位) | 最高位移入 CF,同时循环移回最低位 |
| ROR | ROR eax, 1 | 循环右移(不带进位) | 最低位移入 CF,同时循环移回最高位 |
| RCL | RCL eax, 1 | 带进位循环左移 | 最高位移入 CF,CF 原值移入最低位 |
| RCR | RCR eax, 1 | 带进位循环右移 | 最低位移入 CF,CF 原值移入最高位 |
2.4 逻辑比较指令
cmpcmp eax, ebx # eax - ebxtesttest eax, ebx # eax & ebx 按位与
都是不保存结果,只根据结果修改标志寄存器中的标志位。
2.5 控制转移指令
| 操作符 | 功能 | 示例 |
|---|---|---|
jmp | 无条件跳转 | jmp 0x00401000 |
call | 调用子程序 | call 0x00401000 |
ret | 返回到调用点 | ret |
je / jz | 如果相等(ZF 置位)跳转 | je 0x00401020 |
jne / jnz | 如果不等(ZF 未置位)跳转 | jne 0x00401020 |
jg | 如果大于跳转 | jg 0x00401020 |
jl | 如果小于跳转 | jl 0x00401020 |
jge | 如果大于等于跳转 | jge 0x00401020 |
jle | 如果小于等于跳转 | jle 0x00401020 |
loop | 循环跳转 | loop 0x00401020 |
int | 调用中断 | int 0x80 |
iret | 从中断返回 | iret |
巧计跳转:zero,equal,not,great,less
2.6 栈操作指令
| 指令 | 功能描述 | 栈指针变化(ESP) |
|---|---|---|
PUSH | 将寄存器值、立即数或内存操作数压入栈顶。例如:PUSH EAX、PUSH 5 | ESP -= 4 |
POP | 将栈顶值弹出并存入寄存器或内存操作数。例如:POP EAX | ESP += 4 |
PUSHAD | 按顺序将通用寄存器EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI的值压入栈 | ESP -= 32 |
POPAD | 按顺序从栈中弹出值,恢复通用寄存器:EDI、ESI、EBP、ESP(跳过)、EBX、EDX、ECX、EAX | ESP += 32 |
PUSHFD | 将 32 位标志寄存器EFLAGS压入栈 | ESP -= 4 |
POPFD | 从栈中弹出值并恢复到 32 位标志寄存器EFLAGS | ESP += 4 |
CALL | 调用子程序,将当前指令的返回地址压入栈顶,然后跳转到目标地址 | ESP -= 4 |
RET | 从栈中弹出返回地址,并跳转到该地址 | ESP += 4 |
ENTER | 设置栈帧:将当前EBP压入栈,然后设置新的EBP,并为局部变量分配空间。 | ESP -= 局部变量大小 |
LEAVE | 恢复栈帧:将EBP的值恢复到ESP,然后弹出原来的EBP。mov esp, ebp; pop ebp | ESP += 局部变量大小 |
INT imm8 | 软中断:将标志寄存器、代码段(CS)和指令指针(EIP)压入栈,然后跳转到中断向量表中的处理程序地址。 | ESP -= 12 |
IRET | 中断返回:从栈中弹出标志寄存器、代码段(CS)、指令指针(EIP),恢复中断前状态。 | ESP += 12 |
2.7 字符串操作指令
字符串指令的一个共同特点:
通常不需要把源地址、目标地址直接写出来,而是默认使用
ESI、EDI。
32 位模式下:
ESI → 源字符串 EDI → 目标字符串 ECX → 重复次数| 核心指令 | 功能 | 主要作用 |
|---|---|---|
MOVSB/MOVSW/MOVSD | 字符串传送 | 内存 → 内存 |
CMPSB/CMPSW/CMPSD | 字符串比较 | 比较两段内存 |
SCASB/SCASW/SCASD | 字符串扫描 | EAX 与内存比较 |
LODSB/LODSW/LODSD | 加载字符串 | 内存 → EAX |
STOSB/STOSW/STOSD | 存储字符串 | EAX → 内存 |
B = Byte 1字节 W = Word 2字节 D = Double Word 4字节这些命令常也与rep(重复)搭配使用
2.8 浮点运算指令
FPU 是硬件单元 → x87 是传统浮点指令集 → SSE 是基于 XMM 寄存器的 SIMD 浮点指令集。
1. FPU
- FPU:浮点运算单元,专门处理浮点数运算。
- 主要功能:加、减、乘、除、开方、三角函数等。
- 传统 x86 中 FPU 曾是独立协处理器,80486 起集成到 CPU 内部。
- 核心寄存器:
- ST0~ST7:8 个80 位浮点寄存器,以栈结构组织。
- 状态寄存器:记录 FPU 状态和 C0~C3 标志。
- 控制寄存器:控制精度、舍入方式等。
2. x87
- x87:早期 x86 的浮点指令集,主要操作ST0~ST7。
- 采用浮点寄存器栈。
- 常见指令:
FLD/FST:加载 / 存储FADD/FSUB:加 / 减FMUL/FDIV:乘 / 除FCOM/FUCOM:比较
- 最大特点:支持80 位扩展精度。
3. SSE
- **SSE: **Streaming SIMD Extensions,SIMD 扩展指令集。
- 使用XMM 128 位寄存器。
- 可以同时处理多个浮点数:
PS:Packed Single,多个32 位单精度SD:Scalar Double,单个64 位双精度
- 相比 x87:
- 不采用寄存器栈
- 可以并行处理多个数据
- 更适合多媒体、游戏、音视频和高性能计算。
三. 函数
3.1 嵌套栈结构
空函数汇编
; 函数头 push ebp ; 保存调用者函数的 ebp mov ebp esp ; 设置当前栈帧基值 ; 执行完上面两行时,ebp+4是返回地址,ebp+8是第一个参数,ebp-xxx都是局部数据 ; 返回值保存在eax中 ; 函数尾 leave ; mov esp, ebp; pop ebp ret空函数嵌套
push ebp mov ebp esp push xxx ; 参数入栈 call xxx ; 跳转到函数,并把下一条指令的地址压入栈中 leave ret ; 会回到执行call时保存的下一条语句的地址栈结构
Address Data 描述 Ox0018FF00 [局部变量2] ← EBP - 8 低地址(栈顶方向) Ox0018FF04 [局部变量1] ← EBP - 4 Ox0018FF08 [保存的EBP] ← EBP(当前栈帧基址) Ox0018FF0C [返回地址] ← EBP + 4 Ox0018FF10 [参数1 (a)] ← EBP + 8 Ox0018FF14 [参数2 (b)] ← EBP + 0xC 高地址(栈底方向)3.2 调用约定
cdecl(C调用约定)
push x push x call func add esp, 8 ; 平衡堆栈(外平栈),8对应两个参数 func: push ebp mov ebp esp ... leave retstacall(标准调用约定)
push x push x call func func: push ebp mov ebp esp ... leave retn 8 ; 平衡堆栈(内平栈),8对应两个参数fastcall
mov ecx, x ; 前两个参数放在ecx和edx,后面的依次入栈 mov edx, x push x call func func: push ebp mov ebp esp ... leave retn 4 ; 平衡堆栈(内平栈),4对应那一个push的参数thiscall(C++成员函数调用)
mov ecx, obj_ptr ; this指针存入ecx push x push x call func add esp, 8 ; 平衡堆栈(外平栈),8对应两个参数 func: push ebp mov ebp esp ... leave ret ; 可以是retn 8,可内可外