逆向工程实战手册 第 38 章

第 38 章 汇编语言精讲(读懂工具输出的底层)

反编译器输出再漂亮,关键时候还是要回汇编确认。本章系统讲透 x86-64 汇编的核心知识:寻址、数据传送、算术、控制流、函数调用、调用约定、栈布局。这是逆向的"语言课"。

📍 知识点地图 | 主题:汇编语言精讲 | 前置:第3章 | 后续:第18章 | 核心概念:寄存器、寻址、调用约定、栈帧

38.1 x86-64 寄存器模型

寄存器 用途 逆向中常见含义
RAX 累加器/返回值 函数返回值、运算结果
RBX 通用(callee-saved) 跨调用保存的值
RCX/RDX 参数/计数/数据 Windows 第 1/2 参数
RSI/RDI 源/目标 strcmp/memcpy 参数、Linux 参数
RBP 帧指针 栈帧基址(优化代码可能不用)
RSP 栈指针 栈顶
R8-R11 通用 第 3/4 参数(64 位约定)
R12-R15 通用(callee-saved) 循环变量/基址
RIP 指令指针 当前指令(相对寻址)
RFLAGS 标志位 ZF/CF/SF/OF → 比较结果
32 位: EAX/EBX/...(RAX 的低 32 位)
16 位: AX/BX/...
8 位:  AL(AX 低 8 位)、AH(AX 高 8 位)
规则: 写 EAX 会清零 RAX 高 32 位;写 AL 只动低 8 位

38.2 寻址模式(一切操作的基础)

; 立即数(常量)
mov eax, 0x42

; 寄存器
mov eax, ebx

; 内存寻址: [base + index*scale + disp]
mov eax, [rbx]               ; 基址
mov eax, [rbx + 0x10]        ; 基址 + 偏移(结构体字段访问!)
mov eax, [rbx + rcx*4]       ; 数组元素(index*scale)
mov eax, [rbx + rcx*8 + 0x10] ; 结构体数组
lea rax, [rbx + 0x20]        ; 计算地址(不访问内存!常用于指针运算)

; RIP 相对寻址(64 位程序最常见)
lea rax, [rip + 0x1234]      ; 访问全局变量/字符串(位置无关代码)

逆向速读[reg + 0x10] 一眼看出"结构体第 0x10 偏移的字段"。

38.3 常用指令族

数据传送

mov dst, src           ; 拷贝
movzx / movsx          ; 零扩展/符号扩展(8/16 → 32/64)
movsxd                 ; 32 → 64 符号扩展(关键坑!)
lea                    ; 算地址不访存
xchg                   ; 交换
push / pop             ; 压栈/弹栈(栈操作)

算术逻辑

add / sub / inc / dec  ; 加减
imul / mul             ; 乘(imul 有符号)
idiv / div             ; 除(商在 RAX,余数在 RDX)
and / or / xor / not   ; 逻辑运算
shl / shr / sar / rol / ror  ; 移位/循环移位
neg                    ; 取反

识别加密特征:大量 xor/shl/add 组合 = DIY 加密;mul+add 连用 = 线性变换。

比较与跳转(控制流核心)

cmp a, b       ; 计算 a-b,设置标志位(不保存结果)
test a, a      ; 计算 a&a,设置标志(等价 cmp a, 0)

; 条件跳转(记住"J after CMP")
je/jz           相等则跳      jne/jnz   不等则跳
jg/jge          大于(有符号) jl/jle   小于(有符号)
ja/jae          高于(无符号) jb/jbe   低于(无符号)
js/jns          符号位         jc/jnc   进位
jmp             无条件跳转
call / ret      调用/返回

逆向速读

cmp eax, 0x1337
jne fail        →  "如果 eax != 0x1337 就失败" → 成功条件是 eax == 0x1337

38.4 函数调用与栈帧(最重要的章节)

调用约定(64 位)

平台 参数传递 返回值 说明
System V(Linux/macOS) RDI, RSI, RDX, RCX, R8, R9 RAX 前 6 个整数参数
Windows x64 RCX, RDX, R8, R9 RAX 前 4 个;还有 shadow space
浮点 XMM0-XMM7 XMM0 两种约定一致

标准函数序言/尾声

; 序言(prologue)
push rbp               ; 保存调用者帧指针
mov rbp, rsp           ; 建立新帧
sub rsp, 0x40          ; 分配局部变量空间

; 函数体...

; 尾声(epilogue)
leave                  ; = mov rsp, rbp; pop rbp
ret                    ; 返回(弹出返回地址到 RIP)

栈帧布局图(必须画过的图)

高地址
┌──────────────┐
│ 调用者局部变量 │
├──────────────┤
│ 返回地址      │ ← call 压入
├──────────────┤
│ 保存的 RBP    │ ← push rbp
├──────────────┤ ← RBP 指向这里
│ 局部变量区    │
│ (sub rsp 分配)│
├──────────────┤
│ 参数区/临时区  │
└──────────────┘ ← RSP
低地址

常见漏洞在栈帧中的表现

sub rsp, 0x40    + 读入 0x200 字节 → 栈溢出(缓冲区超过分配空间)
canary(金丝雀): [rbp-0x8] 处有随机值 → 溢出检测
泄露地址 → 绕过 ASLR/PIE 的基础

38.5 常见代码模式的汇编识别(翻译训练)

C 代码 汇编模式
if (a == b) cmp a,b; jne skip
switch(x) 跳转表 jmp [table + x*8]
for(i=0;i<n;i++) xor eax,eax; loop: cmp eax,n; jge done; ... inc eax; jmp loop
strcpy(dst, src) mov rdi,dst; mov rsi,src; call strcpy
p = obj->field mov rax, [obj + offset]
arr[i] mov rax, [arr_base + i*elemsize]
三元/布尔 setne al; movzx eax, al(setcc 模式)
字符串比较 call strcmp; test eax,eax; jne fail

38.6 ARM64 快速对照(移动端/嵌入式必懂)

寄存器: X0-X30(64 位),W0-W30(低 32 位)
  X0-X7 = 参数/返回值(AAPCS)
  X29 = 帧指针(FP),X30 = 链接寄存器(LR,返回地址)
  XSP = 栈指针

核心指令:
  LDR/STR     加载/存储(str/ldr 是"存储/加载",与 x86 相反!)
  ADRP + ADD  加载全局地址(相当于 lea)
  B / BL      分支/带链接分支(BL = call)
  BR x30      ret(返回)
  CBZ/CBNZ    为零跳/非零跳
  CMP/B.EQ    比较+条件分支

与 x86 的核心差异:
  x86: mov 指令多(mov dest, src)
  ARM: 操作数寄存器化(add x0, x1, x2 三操作数)
  条件执行: csel/ccmp(无分支条件逻辑)

38.7 汇编阅读训练法(如何练成条件反射)

1. 大量阅读: 每分析一个函数,先读汇编再对反编译(两种视角交替)
2. 模式卡片: 把 38.5 的表做成记忆卡,看到即认
3. 自我测试: 给一段汇编写 C 等价物,再给 C 写汇编要点
4. 关注差异: 编译器优化(-O2)会让源码模式变形,学会认"优化特征"
5. 不追求每行都懂: 先抓控制流骨架(函数边界/分支/循环),再补细节

动手练习

  1. 写一个含 if/switch/for/结构体访问的 C 程序,编译后对照汇编逐行翻译。
  2. 用 GDB 观察一个函数的 prologue/epilogue,画出它的栈帧(对照 38.4 图)。
  3. 找 5 段陌生汇编,先自己翻译成 C,再用反编译器验证。
  4. 编译同一程序到 x86-64 和 ARM64(交叉编译),对照两种架构的实现差异。

深入阅读

  • 仓库:skills/reverse-engineering/SKILL.md(快速工具参考)
  • 仓库:skills/reverse-engineering/elf-analysis.md(ARM64 寄存器/指令模式)
  • 仓库:skills/reverse-engineering/platforms-hardware.md(ARM64 gadget/ROP)
  • 仓库:skills/reverse-engineering/kernel-driver-reverse.md(C/C++ 模式识别表)
  • 仓库:skills/reverse-engineering/languages-compiled.md(C++ vtable/STL 汇编特征)