反逆向技术分类速查表
分类依据:crackmes.one 搜索页官方标签体系
适用场景:逆向分析时快速识别目标样本采用的保护手段
范围:涵盖反调试、反反汇编、控制流混淆、代码虚拟化、加壳、加密算法、环境检测等 9 大类、90+ 种技术
一、反调试(Anti-debugging)
反调试技术的核心目标是:检测调试器是否存在,或使调试器无法正常附加/断点/单步。常用思路分为三类------API 探测、底层标志位探测、行为差异探测。
| 标签 | 原理简述 |
|---|---|
| IsDebuggerPresent | 调用 kernel32!IsDebuggerPresent(),该 API 本质是读取当前进程 PEB(进程环境块)偏移 0x02 处的 BeingDebugged 字节。绕过方式:修改 PEB 标志或 hook API。 |
| Debugger/tool window detection | 通过 FindWindow / EnumWindows 检索调试器特征窗口类名或标题(如 OLLYDBG、x64dbgClass、ID),也可遍历进程名/窗口标题关键字。 |
| Timing (rdtsc/GetTickCount) | 利用 rdtsc 指令(读取 TSC 时间戳计数器)或 GetTickCount / QueryPerformanceCounter 计时:正常执行两条指令间隔纳秒级,若被单步调试则间隔被拉长数个数量级,据此判定。 |
| PEB BeingDebugged / NtGlobalFlag | 不经过 API,直接从 FS:[0x30](32 位)或 GS:[0x60](64 位)取 PEB 基址,检查 BeingDebugged 字节;同时检查 NtGlobalFlag(偏移 0xBC):若进程由调试器创建,该字段会带有 `FLG_HEAP_ENABLE_TAIL_CHECK |
| INT3 / 0xCC breakpoint scan | 计算目标函数/代码段校验和,或直接扫描内存字节,检测是否被调试器写入了 0xCC(INT3 断点指令)。属于典型的代码自校验手段。 |
| ptrace (Linux) | 利用 Linux ptrace 的排他性:一个进程同一时刻只能被一个 tracer 调试。程序在入口处调用 ptrace(PTRACE_TRACEME) 使自身被父进程跟踪,此后调试器再 attach 时返回失败(EPERM)。 |
| Exception-based (SEH/VEH/INT2D) | 主动触发异常(如 INT2D、INT3、除零)并注册 SEH/VEH 处理器:正常情况下异常被程序自己的处理器捕获后继续执行;若调试器附加,调试器会优先拦截异常(且 INT2D 在调试模式下寄存器行为不同),程序据此判断异常被"抢走"了。 |
| Hardware breakpoints (DRx) | 读取调试寄存器 DR0--DR3(断点线性地址)与 DR7(启用标志位)。硬件断点不修改内存,无法被 0xCC 扫描发现,因此必须通过 DRx 寄存器检测。用户态可用 GetThreadContext 读取。 |
| NtQueryInformationProcess | 调用 ntdll!NtQueryInformationProcess,查询以下信息类: • ProcessDebugPort (0x07):被调试时返回调试器端口,否则为 0 • ProcessDebugObjectHandle (0x1E):被调试时返回调试对象句柄 • ProcessDebugFlags (0x1F):返回 NoDebugInherit 反相值,被调试时为 0 |
| OutputDebugString | 正常进程调用 OutputDebugString 会通过 RaiseException(DBG_PRINTEXCEPTION_C) 抛出异常并由系统捕获;调试器附加时该异常被调试器拦截。程序触发异常前后检查 GetLastError 的差异(通常调试器存在时错误码残留 0),以此探测。 |
| CheckRemoteDebuggerPresent | 内部调用 NtQueryInformationProcess(ProcessDebugPort) 的官方封装版本,检测是否有调试器远程附加到本进程。 |
| Self-debug / block debugger | 进程派生(或创建)一个子进程作为自身的调试器来 attach 父进程。由于调试端口被占用,外部调试器无法再附加。属于"占坑式"防护,还需子进程转发/处理父进程的调试事件。 |
| TLS callback | TLS 回调函数在 PE 主入口(AddressOfEntryPoint)之前执行,且在调试器下断于 OEP 前已运行完毕。将反调试代码置于 TLS callback 可抢在调试器设置断点前完成检测。 |
| Anti-dump | 防止攻击者从内存中转储(dump)出未加壳的程序映像:常见手法包括运行时抹除/加密 PE 头、破坏 SizeOfImage、EAT(导出表)地址、修改 PEB 的 ImageBaseAddress 等,使 dump 工具无法重建合法 PE 文件。 |
| Parent-process check | 通过 NtQueryInformationProcess(ProcessBasicInformation) 读取父进程 PID,再 QueryFullProcessImageName 校验父进程是否为合法启动源(如 explorer.exe、cmd.exe)。若父进程是调试器,则拒绝运行。 |
| Anti-attach / thread suspension | 阻止调试器"附加"到已运行进程:典型手法是创建一个持续轮询线程,检测到线程被外部挂起(NtQuerySystemInformation 遍历线程状态为 Waiting/Suspended)即终止进程或抹除数据;也可在 ntdll!DbgUiConnectToDbg 层面破坏 attach 流程。 |
| DbgBreakPoint/DbgUiRemoteBreakin patch | 对 ntdll 中调试器依赖的关键函数进行运行时补丁: • DbgBreakPoint → 改为 ret,使 INT3 断点失效 • DbgUiRemoteBreakin → 改为 ExitProcess,使调试器 attach 时直接导致进程退出 属于"反向 patch 调试器基础设施"的思路。 |
| CloseHandle invalid-handle | 传入故意非法的句柄调用 CloseHandle:正常环境下该调用仅返回 FALSE;而当进程被调试时,内核会额外抛出 EXCEPTION_INVALID_HANDLE 异常。未处理该异常或异常被调试器拦截,即可判定存在调试器。 |
二、字符串 / 数据加密(String / data encryption)
目的:避免明文字符串或敏感数据(序列号、URL、提示信息)成为逆向切入点。运行时解密,用后即弃或常驻内存加密态。
| 标签 | 原理简述 |
|---|---|
| XOR | 以单字节或多字节密钥对数据逐字节异或。加密解密同一函数,开销极低;多字节循环密钥可抵抗简单频率分析。是 crackme 中最常见的基础加密手段。 |
| Base64 (encryption) | 将二进制数据编码为 64 字符可打印文本(非真正加密,仅混淆)。常与 XOR 等组合成多层数据保护。 |
| AES (encryption) | 高级加密标准(Rijndael),128/192/256 位密钥分组加密。用于保护较敏感的注册数据或配置文件,通常需逆向密钥派生逻辑。 |
| RC4 (encryption) | 流密码:用密钥初始化 S-Box(256 字节置换表),再以伪随机字节流与明文异或。实现极简(十几行代码),crackme 常用。 |
| TEA / XTEA (encryption) | 微型加密算法:64 位分组、128 位密钥、32 轮 Feistel 结构;XTEA 为其改进版。代码量极小(约 20 行),常用于 key 校验场景。 |
| Substitution / table | 通过静态替换表(S-Box)对字节做非线性映射,如自定义字符映射表、查表变换。通常需动态跟踪才能还原映射关系。 |
三、加壳(Packer)
加壳 = 压缩/加密原始 PE + 注入 Stub(引导段)。程序入口先执行 Stub:解密/解压原始节区到内存 → 重建导入表(IAT)→ 跳转 OEP(原入口点)。逆向者的目标是定位 OEP 并 dump + 重建导入表。
压缩壳(以减小体积为主要目的,保护力弱)
| 标签 | 简述 |
|---|---|
| UPX | 经典开源压缩壳,-d 参数即可解壳,入门级样本常用 |
| FSG | 高压缩比小型壳,需要手动 ESP 定律定位 OEP |
| ASPack | 老牌商业压缩壳,典型 IAT 加密 |
| MPRESS | 免费压缩壳,支持较新 PE 格式 |
| PECompact | 商业压缩壳,含少量反调试选项 |
| exepack | 早期 DOS/Windows 压缩工具,遗留样本常见 |
| PKLite | 极早期 DOS 压缩壳 |
| MEW | 小型压缩壳 |
| PE-PACK | 小众 PE 压缩壳 |
| Petite | 早期小型压缩壳 |
| Sixxpack | 针对 .NET 程序集的压缩壳 |
| ARM Protector | 针对 ARM 平台的保护壳 |
加密/保护壳(含反调试、IAT 加密等保护功能)
| 标签 | 简述 |
|---|---|
| tElock | 兼具压缩与加密的壳,含多层反调试 |
| VMProtect | 商业顶级保护壳,主打代码虚拟化,兼有反调试、完整性校验 |
| Yoda | Yoda 系列壳,含多种反调试/反 dump 选项 |
| ASProtect | 老牌商业保护壳,含加密、授权系统、反调试 |
| Enigma | 商业保护壳,含内置虚拟机、许可系统、资源保护 |
| Themida | 强力商业保护壳,多层反调试 + 代码虚拟化(WinVm) |
| ExeCryptor | 商业加密保护壳,含 IAT 混淆 |
| WinLicense | 与 Themida 同源(Oreans),侧重授权/license 保护 |
| PELock | 商业保护壳,含加密与授权功能 |
| CodeVirtualizer | Oreans 出品的代码虚拟化保护工具,可与 Themida 配合 |
| Other named (Morphine/Neolite/PEtite...) | 其他命名壳的统称 |
| BJFnt | 少见的小众保护壳 |
.NET / 脚本类保护
| 标签 | 简述 |
|---|---|
| .NET Reactor | 针对 .NET 程序集的商业保护:混淆 + 符号重命名 + 控制流混淆 + 反编译器检测 |
| ConfuserEx | 开源 .NET 保护工具,支持控制流混淆、常量加密、反篡改 |
| Confuser | ConfuserEx 的前身 |
| SmartAssembly | Red Gate 出品的 .NET 混淆/压缩工具 |
| Dotfuscator | 微软官方 .NET 混淆器,Visual Studio 自带社区版 |
| PyInstaller | 将 Python 脚本打包为独立可执行文件;可用 pyinstxtractor 解包并反编译 .pyc 还原源码 |
| ARM Protector | ARM 平台保护工具 |
自修改 / 运行时解密(Self-modifying / runtime decrypt)
| 标签 | 简述 |
|---|---|
| Self-modifying / runtime decrypt | 代码段在运行时被自身改写:关键函数体以加密态存储,执行前由 Stub 解密到内存,执行后可能重新加密。静态反汇编只能看到密文,需动态跟踪或模拟执行还原。 |
四、加密 / 哈希算法(Crypto / hash algorithm)
与第二节的"数据加密"不同,此类指核心验证逻辑中实现的密码学算法------逆向者需识别算法并分析密钥派生流程,通常无法直接绕过。
摘要 / 校验类
| 标签 | 原理简述 |
|---|---|
| MD5 | 输出 128 位摘要。crackme 中常见用法:MD5(serial) == 常量。由于 MD5 已可碰撞,现代场景仅作校验 |
| SHA-1 | 160 位摘要,MD5 的替代演进版本 |
| SHA-256 | 256 位摘要,SHA-2 家族,目前主流安全摘要算法 |
| CRC32 | 循环冗余校验,非密码学安全,主要用于数据完整性校验与代码自检 |
| Other / custom hash | 作者自定义哈希:可能改造标准算法(改轮数/改初始向量/改模数),需逆向其常数表识别 |
加密 / 签名类
| 标签 | 原理简述 |
|---|---|
| RSA | 非对称加密。crackme 中常用于注册码签名验证:程序内置公钥 n, e,用户名/序列号需满足 RSA 数学校验。需分析大数运算实现 |
| AES (crypto) | 用于核心 keygen/验证逻辑,需提取密钥派生函数(KDF)与工作模式 |
| RC4 (crypto) | 核心逻辑使用 RC4 流密码,需跟踪 S-Box 初始化的密钥 |
| TEA / XTEA (crypto) | 轻量级分组密码参与验证,常见于序列号变换 |
| Blowfish | 对称分组加密(64 位分组,16 轮 Feistel),以其依赖的 4168 字节 S-Box 常数表易于识别 |
| DES / 3DES | 经典对称加密,64 位分组,16 轮 Feistel;常通过 IP 置换表 / S-Box 常数识别 |
| Base64 (crypto) | Base64 作为核心验证链路的一环(如序列号编码后再校验) |
五、代码虚拟化 / VM(Code virtualization / VM)
| 标签 | 原理简述 |
|---|---|
| Code virtualization / VM | 将原始 x86/x64 指令翻译为自定义虚拟机的私有字节码 ,由运行时解释器(VM dispatcher)逐条取指、译码、执行。原始逻辑在物理指令层面完全消失,逆向者必须: 1. 识别 VM dispatcher 与 handler 表 2. 还原每条私有指令的语义 3. 编写反虚拟化工具将字节码转回等价逻辑 代表工具:VMProtect、Themida、Code Virtualizer。是当前强度最高的静态防护手段。 |
六、控制流混淆(Control-flow obfuscation)
目的:不改变程序功能的前提下,破坏控制流图(CFG)的可读性,使逆向者难以还原程序逻辑结构。
| 标签 | 原理简述 |
|---|---|
| Spaghetti / junk-branch | 在真实路径间插入大量随机生成的不可达/条件永假的"面条分支",每个分支内含无意义计算。CFG 边数呈指数膨胀,人工阅读困难。 |
| Exception / interrupt-based | 用异常机制实现跳转:如故意触发除零/越界访问,在 SEH/VEH 处理器中修改 CONTEXT.Eip 跳到下一逻辑块。反汇编器无法静态推断跳转目标。 |
| Indirect / computed jumps & calls | 将直接跳转 jmp addr 改为间接形式:jmp [reg]、call [table+idx*4]、或经过寄存器中转的多步寻址。跳转目标需运行时动态计算,静态分析只能看到"指针"。 |
| State machine / dispatcher | 将函数重构为状态机:一个全局状态变量 + 主循环 + switch 分发器,每个基本块对应一个 state,执行完当前块后更新状态进入下一块。原始顺序/分支结构在汇编层面完全消失。 |
| Control-flow flattening (CFF) | 状态机混淆的工程化加强版(OLLVM 代表特征):所有基本块压平到一个 while(1) { switch(state) } 大循环中,块间通过状态变量跳转。CFG 从树状退化为星状,所有块都指向 dispatcher。 |
| Return-address / stack-based | 操作返回地址实现跳转:如 push target; ret(等同于 jmp 但反汇编器常误判),或在 call 后通过 [esp] 读取/篡改返回地址,使函数"返回"到非常规位置。栈视图与控制流视图被人为错位。 |
七、反反汇编(Anti-disassembly)
目的:欺骗静态反汇编器(IDA/Ghidra),使其生成错误的指令流。核心手段是利用反汇编器的线性扫描/递归下降解析缺陷。
| 标签 | 原理简述 |
|---|---|
| Junk / garbage bytes | 在指令间隙插入垃圾字节:这些字节永远不会被执行(被前面的 jmp 跳过),但线性扫描反汇编器会将其误解析为指令,导致后续真实指令错位显示为乱码。 |
| Malformed PE / bad bytes (UD2) | 畸形 PE 结构:如节表 PointerToRawData 与实际不符、伪造重叠 Section;或在代码中插入 UD2(未定义指令,CPU 遇到即抛异常)等非法指令欺骗反汇编器按字面解析。 |
| Opaque predicates | 不透明谓词:插入形如 if (x*x >= 0) 这样恒为真/恒为假但静态分析难以证明的判断,其假分支指向垃圾数据或错位指令。反汇编器会按"存在两个分支"错误建模 CFG。 |
| Overlapping / misaligned instructions | 重叠指令:一段字节序列从偏移 A 开始解析是一条合法指令,从偏移 A+1 开始解析是另一条指令------真实执行流跳到 A+1,而反汇编器默认从 A 开始解析。利用 x86 变长指令特性制造"指令重叠"。 |
| Jump-based desync | 跳转错位:jmp 指令跳转到"反汇编器认为的指令中间"而非边界,使反汇编器后续解析全部错位。通常构造 jmp +1 跳过当前指令的一个字节实现 desync。配合 nop 字节可将反汇编器完全带偏。 |
八、反篡改 / 完整性校验(Anti-tamper / integrity)
| 标签 | 原理简述 |
|---|---|
| Anti-tamper / integrity | 对自身代码段/资源/配置做哈希或 CRC 校验: • 启动时校验 :计算 .text 节 CRC32 与预存常量比对,不符则退出或休眠若干次后触发 • 运行时周期校验 :后台线程定时重算,防止打补丁后继续运行 • 多点分散校验 :校验逻辑分散在多个函数中,patch 单点无法完全绕过 逆向对策:hook 校验函数返回固定值,或用硬件断点+修改标志位。 |
九、导入表 / API 混淆(Import / API obfuscation)
| 标签 | 原理简述 |
|---|---|
| Import / API obfuscation | 隐藏 IAT(导入地址表)中的真实 API: • 动态解析 :不静态导入敏感 API,运行时通过 LoadLibrary + GetProcAddress 按名称哈希获取 • 名称哈希 :不存储 API 明文名,存储预计算的名称 hash,运行时遍历导出表逐个计算 hash 比对 • 代理 IAT :IAT 指向中转 stub,stub 再跳转真实函数 逆向对策:跑一遍后 dump 重建 IAT,或对 GetProcAddress 下断观察。 |
十、自定义 / 通用混淆(Custom / generic obfuscation)
| 标签 | 原理简述 |
|---|---|
| Custom / generic obfuscation | 归类为作者自创或未明确分类的混淆技术,可能是上述多种手段的组合或变种。 |
十一、反虚拟机 / 沙箱(Anti-VM / sandbox)
| 标签 | 原理简述 |
|---|---|
| Anti-VM / sandbox | 检测运行环境是否为虚拟机或沙箱,常见探测点: • CPUID 指令 :hypervisor 位 / ECX=0x4E564456 ("VMware") 魔数 • 虚拟硬件指纹 :MAC 地址前缀(00:05:69 / 00:0C:29)、注册表键、设备名(VMware SVGA、QEMU DVD) • 行为特征 :虚拟机常见进程(vmtoolsd.exe)、用户交互缺失(鼠标移动次数)、运行时间过短 • 指令时序 :VMEXIT 导致的特定指令延迟 检出后通常静默退出或执行伪装行为。 |
十二、弹窗 / 试用限制(Nag / trial)
| 标签 | 原理简述 |
|---|---|
| Nag / trial | 商业软件常见保护形态: • Nag 弹窗 :启动或退出时弹出"未注册"提醒,逆向目标是去掉 MessageBox 调用 • Trial 限制 :基于天数/次数/日期的限制逻辑,常见实现为注册表/文件记录 + 时间比对,破解目标是定位比较点并 NOP 或构造合法 key crackme 场景下通常演化为"写出 keygen"或"移除注册验证"任务。 |
附:技术强度与应对难度参考
| 防护强度 | 典型技术 | 逆向难度 |
|---|---|---|
| ★☆☆☆☆ | UPX、明文字符串、简单 XOR、IsDebuggerPresent | 脚本/工具一键处理 |
| ★★☆☆☆ | ASPack、简单反调试组合、Base64+MD5 | 需手动定位 OEP、分析验证逻辑 |
| ★★★☆☆ | CFF 控制流平坦化、多种反调试叠加、RC4/AES 验证 | 需理解混淆模式 + 手动脱壳 |
| ★★★★☆ | 反反汇编 desync、多层完整性校验、代理 IAT | 需动态调试 + 自定义脚本 |
| ★★★★★ | VMProtect/Themida 代码虚拟化 | 需还原 VM 结构,门槛极高 |