白城市网站建设_网站建设公司_Node.js_seo优化
2026/1/15 20:24:42 网站建设 项目流程

文章目录

  • 逆向实战:我是怎么从这段汇编一眼看出它是 `switch`,而不是一串 `if/else`
    • 1. 先把源码和目标放在脑子里
    • 2. 第一眼看到的异常感:**比较链太“整齐”了**
      • 2.1 同一个变量,被连续拿来和多个“干净的常量”比较
    • 3. 再往下看:每个分支都“自己干完事就跳走”
      • 为什么?
    • 4. 最后一个确认点:那个“兜底的 jmp”
    • 5. 到这里,我在脑子里已经还原出完整结构了
    • 6. 顺手对照一下完整示例代码(你给的)
    • 7. 我自己在逆向里总结的“线性 switch 判断口诀”

逆向实战:我是怎么从这段汇编一眼看出它是switch,而不是一串if/else

有时候在逆向里,最关键的不是“会不会写 C”,而是看到一坨汇编时,你脑子里第一反应是什么结构

这篇就直接拿你这段汇编来说:我是怎么一步步判断它是switch-case,而不是普通if / else if / else的。


1. 先把源码和目标放在脑子里

源码是一个非常简单的 switch:

intvar=10;switch(var){case1:printf("1\r\n");break;case2:printf("2\r\n");break;case3:printf("3\r\n");break;default:printf("default\r\n");break;}

逆向时我并不是一开始就假设它是 switch,而是先看汇编在“干什么”。

关键汇编如下(无关代码已省略):

; var = 10; 00C114FE mov dword ptr [ebp-4],0Ah ; switch (var) 00C11505 mov eax,dword ptr [ebp-4] 00C11508 mov dword ptr [ebp-8],eax ; 依次比较 1 / 2 / 3 00C1150B cmp dword ptr [ebp-8],1 00C1150F je 00C1151F ; case 1 00C11511 cmp dword ptr [ebp-8],2 00C11515 je 00C1152E ; case 2 00C11517 cmp dword ptr [ebp-8],3 00C1151B je 00C1153D ; case 3 00C1151D jmp 00C1154C ; default

2. 第一眼看到的异常感:比较链太“整齐”了

我在逆向里看到这段时,第一个感觉是:

这不像人手写的 if/else,更像编译器生成的结构。

为什么?

2.1 同一个变量,被连续拿来和多个“干净的常量”比较

cmp [ebp-8], 1 je case_1 cmp [ebp-8], 2 je case_2 cmp [ebp-8], 3 je case_3

注意几个细节:

  • 比较对象完全一样:全是[ebp-8]
  • 比较值非常规整:1、2、3
  • 每次 cmp 后立刻 je 跳走

这在逆向里是一个非常强的信号。

如果是普通 if/else:

  • 有可能是<><=
  • 有可能中途就return
  • 有可能比较对象变来变去

但这里是一模一样的比较模板重复 N 次,只是立即数不同。

👉这非常像 switch 的 case 常量。


3. 再往下看:每个分支都“自己干完事就跳走”

我顺着je跳到每个目标地址去看:

; case 1: 00C1151F push 0C17B30h ; "1\r\n" 00C11524 call printf 00C11529 add esp,4 00C1152C jmp 00C11559 ; ← 关键 ; case 2: 00C1152E push 0C17B34h ; "2\r\n" 00C11533 call printf 00C11538 add esp,4 00C1153B jmp 00C11559 ; case 3: 00C1153D push 0C17B38h ; "3\r\n" 00C11542 call printf 00C11547 add esp,4 00C1154A jmp 00C11559

这一步我基本已经 80% 确认是 switch 了。

为什么?

因为它们全部在做一件事

执行各自的 case 代码 →无条件跳到同一个地址

这在 C 里只有一个非常典型的来源:

caseX:...break;

也就是说:

  • jmp 00C11559break;
  • 00C11559≈ switch 之后的统一出口

如果是 if/else,很多时候:

  • 不需要手动jmp
  • 可以顺着自然流控制流往下走

switch + break 的汇编特征就是:多个分支 → 多个 jmp → 同一出口


4. 最后一个确认点:那个“兜底的 jmp”

回头再看比较链的结尾:

cmp [ebp-8], 3 je case_3 jmp default

这条jmp default非常关键。

因为它说明一件事:

所有 case 都没命中 → 强制跳到某一个固定分支

这在 C 里,只能是:

default:

而且这个default块:

00C1154C push 0C17B3Ch ; "default\r\n" 00C11551 call printf 00C11556 add esp,4
  • 没有被任何je直接命中
  • 只通过这一条 fallbackjmp进来

👉100% 的 default 分支形态。


5. 到这里,我在脑子里已经还原出完整结构了

在没看源码前,我在逆向里已经可以直接写出这样的伪代码:

if(v==1){printf("1");}elseif(v==2){printf("2");}elseif(v==3){printf("3");}else{printf("default");}

然后再结合:

  • 比较对象完全一致
  • 常量是离散整数
  • 所有分支都有 break 行为

👉这不是人手写的 if/else,而是编译器生成的线性 switch。


6. 顺手对照一下完整示例代码(你给的)

#include<stdio.h>voidfunc(){intvar=10;switch(var){case1:printf("1\r\n");break;case2:printf("2\r\n");break;default:printf("default\r\n");break;}}intmain(){func();return0;}

和汇编一一对应,没有任何矛盾点。


7. 我自己在逆向里总结的“线性 switch 判断口诀”

最后用人话总结一下这类 switch,我在逆向时脑子里的判断流程是这样的:

  1. 同一个变量被连续 cmp 多次
  2. cmp 的都是干净的常量(1、2、3…)
  3. 每个 cmp 后面都是 je,直接跳走
  4. 比较链最后有一个无条件 jmp → 兜底分支
  5. 每个分支末尾都 jmp 到同一个地址

只要这五条同时出现,我几乎不会再怀疑:

这是 switch,而且是 case 数量少、没用跳转表的那种。


如果你愿意,下一步我们可以直接接着写一篇:

  • “从汇编里区分:线性 switch vs 跳转表 switch”
  • 或者“switch 没有 break(fall-through)在汇编里的真实样子”

这些在真实逆向里,比语法本身更容易踩坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询