文章目录
-
- 一、先把原始代码和汇编摆在桌面上
-
- [原始 C 代码(不改)](#原始 C 代码(不改))
- 对应的关键汇编(不改,仅截取核心逻辑)
- [二、第一眼扫过去:这不是 switch,是一条 if-else 链](#二、第一眼扫过去:这不是 switch,是一条 if-else 链)
- [三、从第一个 if 开始,对着汇编"反着读逻辑"](#三、从第一个 if 开始,对着汇编“反着读逻辑”)
- [四、else if 的本质:上一个条件失败后,立刻 cmp 下一个阈值](#四、else if 的本质:上一个条件失败后,立刻 cmp 下一个阈值)
- 五、看到补码就要警觉:开始进入负数区间判断
- [六、没有 cmp 的那一段:这就是 else](#六、没有 cmp 的那一段:这就是 else)
- [七、把整个 if-else 链"反推"成一张逻辑表](#七、把整个 if-else 链“反推”成一张逻辑表)
- 八、一句话总结这类结构的"逆向直觉"
一、先把原始代码和汇编摆在桌面上
原始 C 代码(不改)
c
int checkNumber(int num)
{
int result;
if (num > 1000)
result = 10000;
else if (num > 500)
result = 5000;
else if (num > 100)
result = 1000;
else if (num > 50)
result = 500;
else if (num > 0)
result = 100;
else if (num == 0)
result = 0;
else if (num > -50)
result = -100;
else if (num > -100)
result = -500;
else if (num > -500)
result = -1000;
else
result = -5000;
return result;
}
对应的关键汇编(不改,仅截取核心逻辑)
asm
002F1576 cmp dword ptr [ebp+8],3E8h
002F157D jle 002F158B
002F157F mov dword ptr [ebp-4],2710h
002F1586 jmp 002F1610
002F158B cmp dword ptr [ebp+8],1F4h
002F1592 jle 002F159D
002F1594 mov dword ptr [ebp-4],1388h
002F159B jmp 002F1610
002F159D cmp dword ptr [ebp+8],64h
002F15A4 jle 002F15AF
002F15A6 mov dword ptr [ebp-4],3E8h
002F15AD jmp 002F1610
002F15AF cmp dword ptr [ebp+8],32h
002F15B6 jle 002F15C1
002F15B8 mov dword ptr [ebp-4],1F4h
002F15BF jmp 002F1610
002F15C1 cmp dword ptr [ebp+8],0
002F15C5 jle 002F15CF
002F15C7 mov dword ptr [ebp-4],64h
002F15CE jmp 002F1610
002F15CF cmp dword ptr [ebp+8],0
002F15D3 jne 002F15D9
002F15D5 mov dword ptr [ebp-4],0
002F15DC jmp 002F1610
002F15D9 cmp dword ptr [ebp+8],0FFFFFFCEh
002F15DD jle 002F15E8
002F15DF mov dword ptr [ebp-4],0FFFFFF9Ch
002F15E6 jmp 002F1610
002F15E8 cmp dword ptr [ebp+8],0FFFFFF9Ch
002F15EC jle 002F15F7
002F15EE mov dword ptr [ebp-4],0FFFFFE0Ch
002F15F5 jmp 002F1610
002F15F7 cmp dword ptr [ebp+8],0FFFFFE0Ch
002F15FB jle 002F1606
002F15FD mov dword ptr [ebp-4],0FFFFFC18h
002F1604 jmp 002F1610
002F1606 mov dword ptr [ebp-4],0FFFFEC78h
002F1610 mov eax,dword ptr [ebp-4]
二、第一眼扫过去:这不是 switch,是一条 if-else 链
我在 IDA 里第一眼看到这段的时候,并没有逐条翻译,而是先看结构:
-
[ebp+8]被反复cmp -
每次
cmp后面紧跟条件跳转 -
每个分支一旦命中,都会:
-
mov [ebp-4], 常量 -
jmp 002F1610
-
-
002F1610是所有路径都会汇合的统一出口
这在逆向里基本是一个信号:
这是"线性 if → else if → else"的经典编译形态。
三、从第一个 if 开始,对着汇编"反着读逻辑"
asm
cmp dword ptr [ebp+8],3E8h
jle 002F158B
这里非常关键的一点是:
-
C 里写的是
if (num > 1000) -
汇编里判断的是 "不满足就跳走"
也就是说:
-
jle跳走 → 条件失败 → 去试下一个 else if -
不跳 → 条件成立 → 执行赋值
asm
mov dword ptr [ebp-4],2710h ; result = 10000
jmp 002F1610
到这里,第一个 if 分支已经完全闭合。
四、else if 的本质:上一个条件失败后,立刻 cmp 下一个阈值
asm
002F158B cmp dword ptr [ebp+8],1F4h
002F1592 jle 002F159D
我在这里通常会做一件事:
看上一个
jle的目标地址,是不是正好跳到这里。
一对上,结构就彻底清楚了:
-
失败 → 跳到下一个 cmp
-
成功 → 赋值 → 跳统一出口
asm
mov dword ptr [ebp-4],1388h ; result = 5000
jmp 002F1610
这就是:
c
else if (num > 500)
后面的 num > 100 / 50 / 0 分支,全都是同一个套路,只是阈值和结果不同。
五、看到补码就要警觉:开始进入负数区间判断
asm
cmp dword ptr [ebp+8],0FFFFFFCEh
jle 002F15E8
0xFFFFFFCE 是什么?
-
补码
-
转成有符号:-50
再看赋值:
asm
mov dword ptr [ebp-4],0FFFFFF9Ch ; -100
这一步在逆向时非常"顺":
结构没变,只是常量换成了负数。
说明 C 里的逻辑大概是:
c
else if (num > -50)
result = -100;
后面的 -100 / -500 分支,完全同理。
六、没有 cmp 的那一段:这就是 else
asm
002F1606 mov dword ptr [ebp-4],0FFFFEC78h
这里有两个非常明显的信号:
-
前面没有任何比较
-
执行完就自然落到返回点
这说明:
这是所有条件都失败之后的兜底分支
也就是:
c
else
result = -5000;
七、把整个 if-else 链"反推"成一张逻辑表
这是我在逆向里经常做的最后一步,用来确认自己没有看漏条件:
| 条件 | result |
|---|---|
| num > 1000 | 10000 |
| num > 500 | 5000 |
| num > 100 | 1000 |
| num > 50 | 500 |
| num > 0 | 100 |
| num == 0 | 0 |
| num > -50 | -100 |
| num > -100 | -500 |
| num > -500 | -1000 |
| else | -5000 |
如果这张表能完整整理出来,说明这个函数已经被你完全逆向成功。
八、一句话总结这类结构的"逆向直觉"
只要在反汇编里看到:同一个变量被一串
cmp连续比较,每个分支内部"赋值 + jmp 同一个出口",最后还有一个自然落地的分支,那它几乎一定是一个多条件 if-else if-else。
cpp
#include<iostream>
int checkNumber(int num) {
int result;
// 多条件 if-else if-else 语句
if (num > 1000) {
result = 10000;
}
else if (num > 500) {
result = 5000;
}
else if (num > 100) {
result = 1000;
}
else if (num > 50) {
result = 500;
}
else if (num > 0) {
result = 100;
}
else if (num == 0) {
result = 0;
}
else if (num > -50) { // 负数的判断,从靠近0的开始
result = -100;
}
else if (num > -100) {
result = -500;
}
else if (num > -500) {
result = -1000;
}
else {
result = -5000;
}
return result;
}
int main() {
// 测试不同的数字
int testNumbers[] = { 1500, 750, 200, 75, 25, 0, -25, -75, -150, -300, -1000 };
int size = sizeof(testNumbers) / sizeof(testNumbers[0]);
std::cout << "多条件判断测试结果:" << std::endl;
std::cout << "====================" << std::endl;
for (int i = 0; i < size; i++) {
int result = checkNumber(testNumbers[i]);
std::cout << "checkNumber(" << testNumbers[i] << ") = " << result << std::endl;
}
return 0;
}