条件跳转字节码速查(x86 / x64)
一、条件跳转短编码(rel8)总表
rel8:relative 相对8位,即1字节 短跳转的操作码范围是 0x70 ~ 0x7F,指令长度 2 字节(操作码 + 1 字节相对偏移,例:74 08),偏移范围 -128 ~ +127 字节。
| 操作码 | 助记符 | 跳转条件 | 含义 |
|---|---|---|---|
| 70 | JO | OF=1 | 溢出 |
| 71 | JNO | OF=0 | 无溢出 |
| 72 | JB / JC / JNAE | CF=1 | 低于 / 有进位 |
| 73 | JAE / JNB / JNC | CF=0 | 高于等于 / 无进位 |
| 74 | JE / JZ | ZF=1 | 相等 / 为零 |
| 75 | JNE / JNZ | ZF=0 | 不相等 / 非零 |
| 76 | JBE / JNA | CF=1 或 ZF=1 | 低于等于 |
| 77 | JA / JNBE | CF=0 且 ZF=0 | 高于 |
| 78 | JS | SF=1 | 符号为负 |
| 79 | JNS | SF=0 | 符号非负 |
| 7A | JP / JPE | PF=1 | 奇偶性为偶 |
| 7B | JNP / JPO | PF=0 | 奇偶性为奇 |
| 7C | JL / JNGE | SF≠OF | 有符号小于 |
| 7D | JGE / JNL | SF=OF | 有符号大于等于 |
| 7E | JLE / JNG | ZF=1 或 SF≠OF | 有符号小于等于 |
| 7F | JG / JNLE | ZF=0 且 SF=OF | 有符号大于 |
二、近跳转编码(rel32,0F 8x 系列)
近跳转以 0F 为前缀,第二字节在 0x80 ~ 0x8F,共 6 字节指令(2 字节操作码 + 4 字节偏移),偏移范围约 ±2GB。这是 32 位和 64 位模式下编译器为较远目标生成的默认编码。
| 操作码(十六进制字节序列) | 助记符 | 跳转条件 |
|---|---|---|
| 0F 80 cd | JO | OF=1 |
| 0F 81 cd | JNO | OF=0 |
| 0F 82 cd | JB / JC | CF=1 |
| 0F 83 cd | JAE / JNC | CF=0 |
| 0F 84 cd | JE / JZ | ZF=1 |
| 0F 85 cd | JNE / JNZ | ZF=0 |
| 0F 86 cd | JBE | CF=1 或 ZF=1 |
| 0F 87 cd | JA | CF=0 且 ZF=0 |
| 0F 88 cd | JS | SF=1 |
| 0F 89 cd | JNS | SF=0 |
| 0F 8A cd | JP / JPE | PF=1 |
| 0F 8B cd | JNP / JPO | PF=0 |
| 0F 8C cd | JL | SF≠OF |
| 0F 8D cd | JGE | SF=OF |
| 0F 8E cd | JLE | ZF=1 或 SF≠OF |
| 0F 8F cd | JG | ZF=0 且 SF=OF |
三、x32 与 x64 的关键差异(Patch 时必看)
1. rel8 短跳转在两种架构下完全相同。 0x74 / 0x75 在 x86 和 x64 下含义一致,补丁方式完全一样。
2. 0F 84 / 0F 85 的近跳转在 64 位模式下有效,但操作数大小有限制。
- rel16 编码在 64 位模式下不支持。Intel 手册明确标注
JE rel16(0F 84 cw)在 64-bit mode 下为 Not Supported。 - rel32 编码在 64 位模式下正常支持。
0F 84 cd(JE rel32)和0F 85 cd(JNE rel32)在 x64 下完全可用,偏移量会符号扩展为 64 位。 - 如果在 64 位代码中看到
0F 84后跟 4 字节偏移(共 6 字节指令),直接 patch 为0F 85即可反转条件;如果0F 84后跟的是 2 字节偏移(rel16),那是 16 位兼容模式的编码,x64 下不会出现。
3. 64 位模式下新增的专用条件跳转:
- JRCXZ(操作码
E3):检查 RCX 寄存器是否为 0,仅在 64 位模式可用。 - JECXZ(操作码同为
E3):检查 ECX 寄存器是否为 0,在 32 位和 64 位模式下都有效。 - 在 64 位模式下,
E3默认按 JRCXZ 解释(取决于地址大小前缀)。
四、patch实战速查
最常用的反转对
| 原始 | 目标 | Patch 方式 |
|---|---|---|
| 74 (JZ short) | 75 (JNZ short) | 改 1 字节 |
| 75 (JNZ short) | 74 (JZ short) | 改 1 字节 |
| 0F 84 (JZ near) | 0F 85 (JNZ near) | 改第 2 字节 |
| 0F 85 (JNZ near) | 0F 84 (JZ near) | 改第 2 字节 |
其他常用反转对
72↔73(JB ↔ JNB)76↔77(JBE ↔ JA)7C↔7D(JL ↔ JGE)7E↔7F(JLE ↔ JG)
Patch 注意事项
- 先确认是短跳转还是近跳转:看指令起始字节是单字节操作码(74/75)还是
0F前缀。74 XX是 2 字节短跳转,0F 84 XX XX XX XX是 6 字节近跳转。 - 仅反转条件时不要改变指令长度:短跳转改短跳转,近跳转改近跳转。把
0F 84改成74会破坏后续字节的解析。 - 偏移量是相对当前指令末尾的:短跳转的 rel8 从下一条指令的地址开始计算,补丁后偏移量不变,目标地址不变。
五、标志位速记
| 标志位 | 名称 | 置 1 条件 | 常用于 |
|---|---|---|---|
| ZF | 零标志 | 结果为 0 | JE/JZ、JNE/JNZ |
| CF | 进位标志 | 无符号运算产生进位/借位 | JB/JC、JAE/JNC |
| SF | 符号标志 | 有符号结果为负 | JS、JNS |
| OF | 溢出标志 | 有符号运算溢出 | JO、JNO |
| PF | 奇偶标志 | 结果低字节中 1 的个数为偶 | JP、JNP |
JZ/JE 只关心 ZF,与 CF、SF、OF 无关。 无论前面的 cmp 是有符号还是无符号比较,只要结果相等(差值为 0),ZF 就置 1。
六、简单总结
- JZ 就是 JE,操作码 74(短)/ 0F 84(近),条件 ZF=1。
- JNZ 就是 JNE,操作码 75(短)/ 0F 85(近),条件 ZF=0。
- x32 和 x64 下这两个指令的操作码完全一样,区别仅在于近跳转不支持 rel16 编码,但 rel8 和 rel32 都正常可用。
- Patch 时看前缀:单字节直接改,
0F前缀改第二个字节。
// comments
评论
正在读取评论…