一、x86基础寄存器(逆向工程核心基础)
x86架构的寄存器是指令操作的核心载体,逆向中需精准掌握寄存器的用途、分类及数据流向,32位x86核心寄存器分为以下几类:
1. 通用寄存器(数据操作核心)
通用寄存器用于存储数据、地址、参数等,32位寄存器可拆分为16位/8位子寄存器(如EAX→AX→AH/AL),逆向中需关注子寄存器的操作(如单字节/双字节数据处理)。
| 32位寄存器 | 16位子寄存器 | 8位子寄存器 | 核心用途(逆向场景) |
|---|---|---|---|
| EAX | AX | AH/AL | 累加器:算术运算、函数返回值(Windows API返回值默认存EAX)、系统调用号 |
| EBX | BX | BH/BL | 基址寄存器:全局变量指针、内存基址(如PEB/TEB偏移计算) |
| ECX | CX | CH/CL | 计数器:循环计数(REP指令)、Fastcall/Thiscall参数传递(第1参数/this指针) |
| EDX | DX | DH/DL | 数据寄存器:大数运算(除法余数、64位数据高32位)、Fastcall第2参数 |
| ESI | SI | SIL | 源变址寄存器:字符串操作(MOVS/LODS)源地址、数据拷贝源指针 |
| EDI | DI | DIL | 目的变址寄存器:字符串操作(MOVS/STOS)目的地址、数据拷贝目的指针 |
| EBP | BP | BPL | 基址指针:栈帧基址(逆向中通过EBP偏移定位局部变量/参数) |
| ESP | SP | SPL | 栈指针:始终指向栈顶(逆向中跟踪栈帧变化、栈平衡) |
逆向关键:
- EAX是逆向中最常关注的寄存器(函数返回值、运算结果、系统调用号);
- EBP/ESP是栈帧分析的核心,通过
EBP+偏移定位参数,EBP-偏移定位局部变量; - ESI/EDI是字符串/内存操作的专属寄存器,REP指令依赖这两个寄存器+ECX(长度)。
2. 段寄存器(内存寻址辅助)
段寄存器在保护模式下用于定位内存段,逆向中需重点关注FS寄存器(关联TEB/PEB):
| 寄存器 | 核心用途(逆向场景) |
|---|---|
| CS | 代码段:指向当前执行指令的代码段(.text段) |
| DS | 数据段:指向全局数据段(.data/.rdata) |
| SS | 栈段:指向栈段(栈帧所在内存区域) |
| ES/FS/GS | 附加段:FS在Windows中指向TEB(线程环境块),是反调试分析的核心 |
3. 指令指针寄存器(执行流核心)
| 寄存器 | 32位名称 | 核心用途(逆向场景) |
|---|---|---|
| IP | EIP | 指令指针:指向当前即将执行的指令地址(逆向中跟踪执行流、断点设置核心) |
逆向关键:EIP无法直接修改(仅能通过JMP/CALL/RET/INT等指令间接修改),是控制流分析的核心。
4. 标志寄存器(状态判断核心)
标志寄存器(EFLAGS)存储指令执行后的状态,是条件跳转(Jcc)、条件移动(CMOVcc)的判断依据,核心标志位如下:
| 标志位缩写 | 名称 | 含义 |
|---|---|---|
| ZF | 零标志 | 运算结果为0则置1,否则置0(JE/JNE的判断依据) |
| SF | 符号标志 | 运算结果最高位为1(负数)则置1,否则置0(JG/JL的判断依据) |
| CF | 进位标志 | 无符号运算产生进位/借位则置1(JA/JB/ADC/SBB的判断依据) |
| OF | 溢出标志 | 有符号运算溢出则置1(JO/JNO/JG/JL的判断依据) |
| PF | 奇偶标志 | 运算结果低8位中1的个数为偶数则置1(位运算/字节操作辅助判断) |
| IF | 中断标志 | 控制CPU是否响应可屏蔽中断(反调试中可能通过CLI/STI修改) |
二、汇编基本结构(x86实模式/保护模式)
x86汇编程序的核心结构分为段定义、数据段、代码段、栈段三大部分,逆向中需重点关注内存布局与指令执行流:
1. 段结构(保护模式下)
; 典型NASM格式结构section .data ; 数据段:只读/可读写常量(全局变量、字符串、常量) msg db "hello",0x00 num dd 123 ; 4字节整型常量
section .bss ; 未初始化数据段:逆向中常为全局未初始化变量 buf resb 100 ; 预留100字节缓冲区
section .text ; 代码段:指令执行区,逆向核心分析区域 global _start ; 入口点(Linux)/ main(Windows)_start: ; 指令执行流 mov eax, 1 ; sys_exit调用 int 0x80 ; 系统调用2. 逆向视角的结构要点
- 数据段(.data/.rdata):逆向中通过常量、字符串定位功能(如错误提示、密钥);
- 代码段(.text):指令的机器码与汇编对应,是反汇编、动态调试的核心;
- 栈段(stack):运行时动态分配,存储局部变量、函数调用上下文,逆向中需跟踪栈帧变化。
三、核心指令体系(逆向高频指令)
1. 数据传输指令(逆向最基础、最高频)
数据传输指令负责在寄存器、内存、立即数间移动数据,逆向中需关注数据流向(如参数传递、加密数据搬运)。
| 指令 | 格式 | 功能 | 逆向场景 |
|---|---|---|---|
| MOV | MOV reg, mem/imm/reg | 数据移动(不影响标志位) | 变量赋值、参数传递 |
| LEA | LEA reg, mem | 加载有效地址(计算地址) | 数组索引、指针运算 |
| PUSH/POP | PUSH reg/mem; POP reg | 入栈/出栈 | 函数调用、栈帧构建 |
| XCHG | XCHG reg, reg/mem | 交换数据 | 加密算法、寄存器暂存 |
| CMOVcc | CMOVZ reg, mem | 条件移动(依标志位) | 无分支条件判断(反调试) |
| MOVSX/MOVZX | MOVSX reg, reg8/16 | 符号/零扩展移动 | 整型类型转换(如char→int) |
逆向示例:
lea eax, [ebp+var_4] ; 计算局部变量var_4的地址(而非取值)movsx ebx, byte ptr [eax] ; 单字节符号扩展为4字节(处理负数)2. 算数运算指令(逆向中加密/校验/数值计算核心)
| 指令 | 格式 | 功能 | 影响标志位 |
|---|---|---|---|
| ADD/SUB | ADD reg, mem/imm | 加减运算 | ZF/SF/CF/OF/AF/PF |
| MUL/IMUL | IMUL reg, mem/imm | 无符号/有符号乘法 | CF/OF(MUL) |
| DIV/IDIV | DIV reg | 无符号/有符号除法 | 无(商→AX,余数→DX) |
| INC/DEC | INC reg | 自增/自减(不影响CF) | ZF/SF/OF/AF/PF |
| NEG | NEG reg | 取反(等价于0 - reg) | 同SUB |
| ADC/SBB | ADC reg, imm | 带进位/借位加减 | 同ADD/SUB |
逆向关键:
- IMUL/DIV常出现在加密算法(如RC4、AES)的数值计算;
- ADC/SBB用于大数运算(如64位以上整数加减,逆向中关注CF标志);
- INC/DEC不影响CF,需注意与ADD reg,1的区别(后者影响CF)。
3. 位操作指令(逆向中加密/压缩/硬件交互核心)
| 指令 | 格式 | 功能 | 影响标志位 |
|---|---|---|---|
| AND/OR/XOR | XOR reg, mem/imm | 按位与/或/异或 | ZF/SF/PF(CF/OF=0) |
| NOT | NOT reg | 按位取反 | 无 |
| SHL/SHR | SHL reg, imm | 逻辑左移/右移(空位补0) | CF/ZF/SF/OF/PF |
| SAL/SAR | SAR reg, imm | 算术左移/右移(SAR补符号位) | 同SHL/SHR |
| ROL/ROR | ROL reg, imm | 循环左移/右移(进位参与循环) | 同SHL |
| BT/BTC/BTR | BT reg, imm | 位测试/测试并取反/测试并复位 | CF(目标位值) |
逆向示例:
xor eax, eax ; 快速清零寄存器(比mov eax,0更高效)shl ebx, 3 ; 等价于ebx *= 8(逆向中识别乘除优化)bt ecx, 7 ; 测试ecx第7位是否为1(结果存CF)4. 逻辑比较指令(控制流分支的基础)
比较指令本质是“减法但不保存结果,仅更新标志位”,逆向中需结合标志位分析分支条件。
| 指令 | 格式 | 功能 | 影响标志位 |
|---|---|---|---|
| CMP | CMP reg, mem/imm | 比较(reg - mem/imm) | 同SUB |
| TEST | TEST reg, mem/imm | 按位与(仅更新标志位) | 同AND |
| SCAS | SCASB/SCASW/SCASD | 串比较(AL/AX/EAX - [EDI]) | 同CMP |
逆向关键:
- TEST eax, eax:快速判断eax是否为0(等价于CMP eax,0,更高效);
- CMP后紧跟Jcc(条件跳转),是逆向中分析分支逻辑的核心;
- SCAS常用于字符串长度计算、内存数据校验(如缓冲区对比)。
5. 控制转移指令(逆向中分析执行流的核心)
控制转移指令改变EIP(指令指针),分为无条件跳转、条件跳转、函数调用/返回三类:
(1)无条件转移
| 指令 | 功能 | 逆向场景 |
|---|---|---|
| JMP | 无条件跳转 | 分支、循环、壳的跳转 |
| CALL | 调用函数(PUSH EIP+JMP) | 函数调用、API调用 |
| RET/RETN | 函数返回(POP EIP) | 函数结束、栈平衡 |
(2)条件跳转(Jcc)
基于标志寄存器的组合判断,是逆向中分析逻辑的核心:
| 指令 | 别名 | 条件 | 标志位组合 | 典型逆向场景 |
|---|---|---|---|---|
| JE | JZ | 相等/零 | ZF=1 | 字符串比较结束、循环终止 |
| JNE | JNZ | 不等/非零 | ZF=0 | 循环继续、条件分支 |
| JG | JNLE | 有符号大于 | SF=OF 且 ZF=0 | 数值范围判断(如x>10) |
| JGE | JNL | 有符号大于等于 | SF=OF | 数值判断(x≥0) |
| JL | JNGE | 有符号小于 | SF≠OF | 数值判断(x<5) |
| JLE | JNG | 有符号小于等于 | SF≠OF 或 ZF=1 | 数值判断(x≤0) |
| JA | JNBE | 无符号高于 | CF=0 且 ZF=0 | 地址/长度比较(如buf_len>100) |
| JAE | JNB | 无符号高于等于 | CF=0 | 地址判断(ptr≥0x400000) |
| JB | JNAE | 无符号低于 | CF=1 | 长度判断(len<256) |
| JBE | JNA | 无符号低于等于 | CF=1 或 ZF=1 | 长度判断(len≤512) |
| JS | - | 结果为负 | SF=1 | 负数处理、符号校验 |
| JNS | - | 结果非负 | SF=0 | 正数判断 |
| JO | - | 溢出 | OF=1 | 溢出异常检测、数值越界 |
| JNO | - | 无溢出 | OF=0 | 正常数值运算 |
| JC | - | 有进位 | CF=1 | 大数运算、校验和计算 |
| JNC | - | 无进位 | CF=0 | 大数运算正常分支 |
6. 栈操作指令(逆向中栈帧分析核心)
栈是x86逆向的核心(函数调用、局部变量、参数传递均依赖栈),栈操作指令需结合栈帧结构分析:
| 指令 | 格式 | 功能 | 逆向注意点 |
|---|---|---|---|
| PUSH | PUSH reg/mem/imm | 栈顶指针ESP-4,数据入栈 | 逆向中跟踪栈内容变化 |
| POP | POP reg | 栈顶数据出栈,ESP+4 | 注意POP后寄存器值变化 |
| PUSHA/POPA | - | 所有通用寄存器入栈/出栈 | 保护现场(壳/调试器常用) |
| ENTER/LEAVE | ENTER imm16, imm8 | 构建/销毁栈帧(等价于PUSH EBP; MOV EBP,ESP; SUB ESP,imm) | 函数入口/出口快速识别 |
逆向核心:栈的生长方向为高地址→低地址,ESP始终指向栈顶,EBP为栈帧基址(逆向中通过EBP偏移定位参数/局部变量)。
7. 字符串操作指令(逆向中字符串处理/内存拷贝核心)
字符串指令通过ESI(源地址)、EDI(目的地址)、ECX(长度)、EAX(比较值)配合,批量处理内存数据,逆向中常出现在字符串拷贝、加密、校验场景:
| 指令 | 格式 | 功能 | 逆向场景 |
|---|---|---|---|
| MOVS | MOVSB/MOVSW/MOVSD | 串拷贝([ESI]→[EDI]) | 内存拷贝、数据搬运 |
| LODS | LODSB/LODSW/LODSD | 串加载([ESI]→AL/AX/EAX) | 读取字符串/数据块 |
| STOS | STOSB/STOSW/STOSD | 串存储(AL/AX/EAX→[EDI]) | 内存填充(如memset) |
| REP | REP MOVSB | 重复执行(ECX≠0) | 批量拷贝(如strcpy) |
| REPE/REPNE | REPE SCASB | 相等/不等时重复 | 字符串比较(如strcmp) |
逆向示例:
; 等价于memset(edi, 0, ecx)xor eax, eax ; 填充值为0rep stosb ; 重复将AL写入[EDI],ECX次,EDI自动递增四、TEB/PEB与反调试技术(逆向核心对抗场景)
TEB(线程环境块)、PEB(进程环境块)是Windows系统中存储进程/线程信息的核心结构,反调试技术常通过读取这些结构中的标志位检测调试器。
1. 核心结构关系
- PEB:位于进程地址空间(32位:0x7FFDF000),存储进程基本信息(如是否调试、模块列表);
- TEB:每个线程独有(32位:FS:[0]指向TEB),TEB偏移0x30指向PEB;
- 关键偏移(32位Windows):
TEB + 0x30 → PEBPEB + 0x02 → BeingDebugged(1字节,调试时为1)PEB + 0x68 → Ldr(模块加载器)PEB + 0x10 → ProcessHeap(进程堆)
2. 基于TEB/PEB的反调试方法(逆向需识别)
| 反调试方式 | 汇编实现 | 逆向识别要点 |
|---|---|---|
| 检测BeingDebugged标志 | mov eax, fs:[0x30] mov al, [eax+0x02] test al, al jnz Debugged | 跟踪FS:[0x30]访问,检查PEB+0x02 |
| 检测PEB堆标志 | mov eax, fs:[0x30] mov eax, [eax+0x10] test dword ptr [eax+0x14], 0x40 | 访问ProcessHeap+0x14(HeapFlags) |
| 检测调试端口(PEB+0xBC) | mov eax, fs:[0x30] cmp dword ptr [eax+0xBC], 0 jne Debugged | PEB+0xBC为调试端口,非0则调试 |
3. 逆向应对思路
- 动态调试:修改BeingDebugged标志为0,或Hook相关内存访问;
- 静态分析:识别FS:[0x30]、PEB偏移访问指令,定位反调试分支。
五、函数调用与栈帧(逆向中函数分析核心)
1. 函数的标准汇编结构(32位x86,以Stdcall为例)
一个完整的函数在汇编层面分为函数序言、函数体、函数尾声三部分,逆向中可通过这三部分快速识别函数边界:
| 函数阶段 | 典型汇编指令 | 核心作用 | 逆向识别特征 |
|---|---|---|---|
| 函数序言(Prologue) | push ebp mov ebp, esp sub esp, n push ebx/esi/edi(可选) | 1. 保存旧栈帧基址(EBP) 2. 建立新栈帧(EBP=ESP) 3. 分配局部变量空间(ESP-n) 4. 保护非易失性寄存器(EBX/ESI/EDI) | 函数开头固定出现push ebp + mov ebp, esp,是识别函数入口的核心特征 |
| 函数体(Body) | 各类业务指令(运算、分支、调用子函数等) | 实现函数核心逻辑 | 包含业务相关的指令流,如参数访问(EBP+8/12)、局部变量访问(EBP-4/8)、子函数CALL等 |
| 函数尾声(Epilogue) | pop ebx/esi/edi(可选) mov esp, ebp pop ebp retn n(Stdcall)/ ret(Cdecl) | 1. 恢复非易失性寄存器 2. 销毁栈帧(ESP=EBP) 3. 恢复旧EBP 4. 函数返回并平衡栈 | 函数结尾固定出现mov esp, ebp + pop ebp + ret/retn,是识别函数出口的核心特征 |
逆向示例:标准函数结构汇编代码
; 函数:int Add(int a, int b) (Stdcall调用约定)Add: ; 函数序言 push ebp ; 保存旧EBP mov ebp, esp ; 建立新栈帧 sub esp, 0x4 ; 分配1个局部变量(4字节)空间 push esi ; 保护非易失性寄存器ESI
; 函数体 mov eax, [ebp+8] ; 读取参数a(EBP+8为第一个参数) mov esi, [ebp+12] ; 读取参数b(EBP+12为第二个参数) add eax, esi ; a + b,结果存EAX(返回值) mov [ebp-4], eax ; 临时存入局部变量(可选)
; 函数尾声 pop esi ; 恢复ESI mov esp, ebp ; 销毁局部变量空间(ESP回到EBP) pop ebp ; 恢复旧EBP retn 0x8 ; 返回,平衡栈(参数a+b共8字节,Stdcall由被调用者平衡)2. 函数嵌套堆栈结构(32位x86)
以“函数A调用函数B,B有2个局部变量、2个参数”为例,栈帧结构(高地址→低地址):
| 后 | 函数A的栈帧 |
|---|---|
| 函数B的局部变量2 | |
| 函数B的局部变量1 | |
| 上一层的ebp | |
| 返回地址 | |
| 参数1 | |
| 参数2 | |
| 先 | 函数B的栈顶(ESP) |
3. 函数调用约定(逆向中识别参数传递/栈平衡)
调用约定决定参数传递顺序、栈平衡责任、寄存器使用,是逆向中还原函数原型的关键:
| 调用约定 | 参数传递顺序 | 栈平衡责任 | 寄存器使用 | 典型场景 |
|---|---|---|---|---|
| Cdecl | 右→左 | 调用者 | 无 | C/C++默认、可变参数函数(printf) |
| Stdcall | 右→左 | 被调用者 | 无 | Windows API(如MessageBoxA) |
| Fastcall | 右→左 | 被调用者 | ECX(第1参数)、EDX(第2参数) | 快速调用(编译器优化) |
| Thiscall | 右→左 | 被调用者 | ECX=this指针 | C++类成员函数 |
| Syscall | 寄存器 | 内核 | EAX(系统调用号)、EBX/ECX/EDX(参数) | Windows内核系统调用(如NtReadFile) |
逆向识别要点:
- Cdecl:函数返回后调用者执行
ADD ESP, n平衡栈; - Stdcall:函数返回时执行
RETN n(n为参数总字节数); - Fastcall:优先看ECX/EDX是否传递参数;
- Thiscall:C++代码中ECX在函数开头赋值,大概率为this指针。
六、标志寄存器的高级用法(逆向中分析分支/运算)
1. 高级应用场景
- 无分支条件判断:通过CMOVcc/SETcc指令替代Jcc,减少分支(反调试/壳常用):
; 等价于if (eax == 0) ebx = 1; else ebx = 2;xor ecx, ecxcmp eax, 0mov ecx, 2cmovz ecx, 1 ; ZF=1时,ecx=1mov ebx, ecx
- 大数运算校验:ADC/SBB结合CF标志实现64位以上整数运算,逆向中需跟踪CF的传递;
- 溢出检测:JO指令捕获有符号运算溢出(如int32超出范围),逆向中识别异常处理逻辑;
- 反调试混淆:故意修改标志位(如CLI/STI修改IF标志),干扰调试器跟踪。
七、逆向工程核心技巧总结
- 指令流分析:从入口点开始,跟踪EIP变化,结合Jcc/Call/Ret分析执行流;
- 栈帧还原:通过EBP偏移定位参数(EBP+8开始)、局部变量(EBP-4开始),还原函数原型;
- 标志位跟踪:CMP/TEST后紧跟Jcc,需先分析标志位组合,再判断分支逻辑;
- 反调试识别:关注FS:[0x30](TEB)、PEB偏移访问、标志位异常修改指令;
- 调用约定还原:通过栈平衡指令(ADD ESP/RETN n)、寄存器使用(ECX/EDX)识别调用约定。
Some information may be outdated