Zypherion-Technologies/UnConfuserEx
GitHub: Zypherion-Technologies/UnConfuserEx
一款针对 ConfuserEx2 混淆保护的 .NET 去混淆工具,支持反篡改、压缩壳、控制流和资源恢复,专为恶意软件分析与逆向工程场景设计。
Stars: 1 | Forks: 0
# UnConfuserEx
[](https://dotnet.microsoft.com/)
[](https://github.com/mkaring/ConfuserEx)
[](https://www.zypherion.tech)
[](https://discord.gg/JXx32jKJXY)
[](https://t.me/zypherion_technologies)
[](https://x.com/Zypherion_Tech)
https://github.com/user-attachments/assets/de2c7fd9-6736-4f39-83c0-3c25aa9c1f24
如果你曾经分析过恶意软件样本,就会发现其中一些是用 ConfuserEx 混淆的。我尝试用一些公开的去混淆工具来处理最新版本,但都没起作用。所以我决定 fork 一个能够对付最新版本的去混淆工具,并根据自己的需求进行了修改。
这个仓库是 [MadMin3r/UnconfuserEx](https://github.com/MadMin3r/UnconfuserEx) 的 fork。功劳也属于他。原项目完成了最困难的部分:制作了一个专门针对 ConfuserEx2 的去混淆工具,能够真正移除实际的保护机制。
这就是本项目的作用。它按固定顺序运行一系列移除器,尽可能重写方法体/资源/metadata,然后将新的 assembly 输出。最重要的部分在于顺序。Compressor 和 anti tamper 必须尽早执行,因为模块的其余部分此时甚至可能还不是真正的 IL。
上游版本已经很有用了,但仍有一些情况不断出现。
其中一个是 LZMA 路径。有些样本提供的字节看起来像是常量/资源 payload,但 LZMA 属性却是无意义的。如果你直接将其传入解码器,就会得到荒谬的字典大小,并最终引发类似“数组维度超出支持范围”的异常。因此,这个版本会检查属性,限制字典大小,限制解压大小,并在解码器分配出荒谬的空间之前中止操作。
```
LZMA properties => CE FD 62 5F 9F
Invalid LZMA properties byte 0xCE or unreasonable dictionary size
```
常量还有一个愚蠢但真实存在的问题。许多 resolver 代码希望位于 getter 调用之前的 ID 是一个 `ldc.i4`。但有时它不再是一条指令,而是一个小型的算术表达式。
```
ldc.i4 0x1234
ldc.i4 0x55
xor
call string (int32)
```
原来的 fork 程序看到 `xor` 后,会调用 `GetLdcI4Value()`,然后就会崩溃,因为 `xor` 显然不是整数加载指令。这个版本会回溯小型算术序列,模拟栈操作,将其合并回一个 `ldc.i4`,然后让普通的 resolver 继续执行。
因此,不需要将其视为一种完全不同的常量保护,它会变成这样:
```
ldc.i4 0x1261
call string (int32)
```
然后现有的常规/x86 常量 resolver 路径就能完成它的工作了。
控制流方面的表现有些平庸
Switch 移除器可以处理常规的 ConfuserEx switch dispatcher 结构。它会遍历代码块,恢复下一个目标,删除无效代码块,并输出一个正常的方法体。但在某些样本中,只有部分方法能被理解。如果你修改了方法的一半,然后发现它仍然是被混淆的,那么输出的结果比无用的还要糟糕,因为现在你得到了损坏的 IL,而且无法清晰地推断出发生了什么。
所以这个版本在修改方法体之前会先对其进行快照:
```
instructions
exception handlers
```
如果去混淆过程抛出异常,或者方法之后看起来仍被混淆,就会恢复原始的方法体。日志依然可以提示“该方法未被解决”,但程序集不会仅仅因为某一个方法具有异常的 dispatcher 而被悄悄损坏。
Jump/trampoline 控制流现在也有了自己的处理阶段。有些方法不仅是 switch dispatcher。它们是由小型分支 trampoline 链接在一起,直到到达真正的代码块。现在这些结构会被检测并折叠,而不是被仅有 switch 的路径忽略。
Compressor 移除器是必须先于其他所有步骤运行的部分。
ConfuserEx compressor stubs 通常会保持真实的程序集处于压缩状态,启动一个微型 loader,解压 payload,并在运行时加载它。
移除器会找到 loader 的结构,提取嵌入的 payload,解压它,并将模块切换为真实的程序集。常规和紧凑型 compressor 布局都可以处理。
```
[+] Compressor detected
[+] Extracted compressed module payload
[+] Decompressed real module
[+] Continuing pipeline on unpacked assembly
```
Anti-tamper 现在有两个路径。
常规/动态 anti tamper 会从受保护的节中解密方法体,并将恢复的方法体写回模块。JIT anti tamper 更麻烦,因为方法体设计为在 runtime 请求它们时才进行实体化。
其大致结构为:
```
find init
extract keys
find encrypted JIT body section
derive per method key
read body
write CilBody back
```
这仍然是基于模式的。显然,如果 stub 改动过大,它就会失效。
Resources 的处理方式与常量类似:找到加密的 resource blob,恢复密钥/解密器结构,解密,必要时进行解压,然后将 resource 放回常规 .NET 工具所期望的位置。
此外还有一个可选的嵌入 PE 重建路径。一些受保护的样本会在 resource 中携带一个托管 PE。启用重建后,移除器也会尝试解析并重写该 payload,而不是留下一个外层已去混淆但内层未被触碰的程序集。
```
UnConfuserEx.exe sample.exe sample.clean.exe --rebuild-embedded-pe
```
当你确定样本在 resources 中隐藏了另一个托管程序集时,就可以使用这个功能。如果 payload 不是托管的 PE,重建路径应该会保持原样不动。
# 使用说明
构建:
```
dotnet build .\UnConfuserEx.sln -c Release
```
运行:
```
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe
```
或者给它指定一个明确的输出路径:
```
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe
```
如果你不提供输出路径,它会在输入文件旁生成一个文件,并在名称中加上 `-deobfuscated`。
针对内嵌的托管 payload:
```
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe --rebuild-embedded-pe
```
# 保护机制
这是当前支持的功能列表。这并不意味着所有可能的 ConfuserEx fork 都能工作。它仅表示 pipeline 知道如何寻找这些结构。
- [x] Anti-debug
- [x] Safe
- [x] Win32
- [x] Antinet
- [x] Anti-dump
- [x] Anti-tamper
- [x] Normal
- [x] Dynamic
- [x] JIT
- [x] JIT dynamic
- [x] Compressor
- [x] Normal
- [x] Compact
- [x] Constants
- [x] Normal
- [x] Dynamic expression
- [x] x86
- [x] 在 getter 调用前折叠小型算术 ID
- [x] Control flow
- [x] Switch dispatcher
- [x] Jump/trampoline blocks
- [x] 当方法无法被安全解决时进行快照/恢复
- [x] Reference proxy
- [x] Normal
- [x] Dynamic expression
- [x] x86
- [x] Renamer
- [x] 将非 ASCII 名称替换为可读的通用名称
- [x] Resources
- [x] Normal
- [x] Dynamic
- [x] 可选的嵌入式托管 PE 重建
- [x] 静态清理
- [x] Obfuscator 属性
- [x] 在安全的情况下移除无效的全局 helper/fields
- [x] 在安全的情况下移除无法访问的垃圾类型
代码中可能还隐藏着一些我忘了列出来的功能 xD。
# 日志
有用的日志是那些能告诉你哪个阶段失败的日志,而不仅仅是提示输出无法运行。
一个被修复的常量路径示例:
```
Constants detected, attempting to remove
Found 3 constant getter(s)
Detected constant decryption type is Dynamic
Decompressed constants blob to 18492 byte(s)
Resolving getter ::???????? as String with 41 call site method(s)
Removed all instances of getter ::????????
```
一个有意保留原样的控制流方法示例:
```
Removing obfuscation from method System.Void Example::Run()
Method System.Void Example::Run() still appears obfuscated after deobfuscation -- left original body intact
Removed obfuscation from 42 methods. Failed to remove from 0 methods. 1 methods left untouched
```
第二种日志虽然不完美,但至少是诚实的
# 此工具无法神奇解决的问题
更改了所有 helper 结构的自定义 ConfuserEx fork。
通过完整的控制流一团糟而不是小型栈表达式来计算其 ID 的常量 getter。
dispatcher 依赖于静态模拟器未知的 runtime 值的控制流图。
需要实际 runtime 执行而非 IL 模拟的 Native helper。
在混淆之前就已经损坏的 assembly。
具有不同加密方法体布局的 JIT anti-tamper 变体。
# 报告问题
如果你希望提交的问题有用,请包含足够的数据以便于复现。
在上传样本之前,请先去掉文件的扩展名。
将所有内容打包在一起,并包含以下内容:
```
Command:
UnConfuserEx.exe
Failure stage:
- compressor
- anti tamper
- constants
- control flow
- resources
- writing output
- runtime after deobfuscation
Expected result:
Actual result:
Console output:
Archive link:
Notes / investigation:
```
如果你只发送一句“无法运行”,回复可能只会是“是的”。
# 去混淆后的效果如何?
### 之前:
### 之后:
# 注意
本项目最初是作为一个实用的逆向工程工具创建的,而非一次软件工程练习。其核心重点始终是可靠地恢复受保护的程序集,而不是维持完美的代码质量。
# 免责声明
此工具旨在用于授权的恶意软件分析、逆向工程、软件恢复、互操作性和教育研究。
用户有责任遵守适用法律,并在分析、访问或处理软件或系统之前获得任何所需的授权。
Zypherion Technologies 不授权对此工具的非法使用,并且对第三方的滥用免责。
本仓库或 `www.zypherion.tech` 上的任何内容均不构成法律建议。
### 之后:
# 注意
本项目最初是作为一个实用的逆向工程工具创建的,而非一次软件工程练习。其核心重点始终是可靠地恢复受保护的程序集,而不是维持完美的代码质量。
# 免责声明
此工具旨在用于授权的恶意软件分析、逆向工程、软件恢复、互操作性和教育研究。
用户有责任遵守适用法律,并在分析、访问或处理软件或系统之前获得任何所需的授权。
Zypherion Technologies 不授权对此工具的非法使用,并且对第三方的滥用免责。
本仓库或 `www.zypherion.tech` 上的任何内容均不构成法律建议。标签:DAST, DNS 反向解析, 云资产清单, 去混淆, 反病毒, 恶意软件分析, 逆向工程