1. 状态检测:EFLAGS 寄存器
CPU 不通过“思考”来做决定,而是通过“副作用”来标记状态。CMP 和 TEST 指令不改变寄存器值,只改变条件码。
| 标志位 | 名称 | 含义 (Meaning) | 触发场景 |
|---|---|---|---|
| ZF | Zero Flag | 结果为零 | a == b 或结果为 0 |
| SF | Sign Flag | 结果为负 | a < 0 (有符号) |
| OF | Overflow Flag | 有符号溢出 | 正+正=负,负+负=正 |
| CF | Carry Flag | 无符号进位/借位 | 无符号比较 a < b |
2. 跳转指令 (JUMP)
汇编中的跳转分为直接跳转(Label)和间接跳转(*Operand)。
- 条件跳转:
je(Equal),jne(Not Equal),jg(Greater),jle(Less or Equal).- 逆向直觉: 看到
cmp a, b紧接jge Target,意味着if (a >= b) goto Target。
- 逆向直觉: 看到
- 条件传送 (CMOV):
- 逻辑:
cmovg %rax, %rbx。 - 工程意义: 现代 CPU 讨厌分支预测失败。用数据流(计算两个结果选一个)代替控制流(跳转),流水线效率更高。
- 逻辑:
3. 循环机制 (Loops)
C 语言的 do-while, while, for 在汇编层殊途同归:
- Jump-to-Middle: 先跳到循环体末尾测试条件。
- Guarded-Do: 先测试初始条件,如果不成立直接跳过;否则进入
do-while结构。
4. Switch 语句与跳转表 (Jump Table)
- 机制: 当 case 数量较多且密集时,GCC 会生成一个跳转表(数组),里面存放着各个代码块的地址。
- 寻址:
jmp *Start(,%rdi,8)。利用索引直接查表跳转。 - 优势: O(1) 时间复杂度,与 case 数量无关。
- 逆向特征: 在
.rodata段看到一连串紧密的地址数据。