增强版 UnconfuserEx 分支,改进了对现代 ConfuserEx2 变体的支持,包括反篡改恢复、压缩器移除、控制流还原和资源重建。
https://github.com/user-attachments/assets/de2c7fd9-6736-4f39-83c0-3c25aa9c1f24
如果你玩过恶意软件样本,其中一些是用 ConfuserEx 混淆的,我试过一些公开的去混淆器来对付最新的版本,但就是不行,所以我决定 fork 一个能对付那个最新版本的公开项目,然后按我的需求修改它。
这个仓库是 MadMin3r/UnconfuserEx 的 fork。功劳也归他。原项目完成了最困难的部分:制作了一个专注的 ConfuserEx2 去混淆器,能够真正移除实际的保护。
这就是这个项目的作用。它按固定顺序运行一系列移除器,在可以的情况下重写方法体/资源/元数据,然后写回一个新的程序集。重要的是顺序。压缩器和反篡改必须提前处理,因为模块的其余部分甚至可能还不是真正的 IL。
上游版本已经很有用了,但总有一些情况反复出现。
一个是 LZMA 路径。有些样本给你的字节看起来像是常量/资源负载,但 LZMA 属性是乱写的。如果你直接把它传给解码器,你会得到荒谬的字典大小,最终出现诸如数组维度超出支持范围的异常。所以这个版本会检查属性,限制字典大小,限制未压缩大小,并在解码器分配出离谱的东西之前退出。
LZMA properties => CE FD 62 5F 9F
Invalid LZMA properties byte 0xCE or unreasonable dictionary size
常量还有另一个愚蠢但真实的问题。很多解析器代码希望 getter 调用前面的 ID 是一个 ldc.i4。但有时它不再是一条指令了。它是一个微小的算术表达式。
ldc.i4 0x1234
ldc.i4 0x55
xor
call string <const getter>(int32)
原来的 fork 看到 xor,调用 GetLdcI4Value(),然后因为 xor 显然不是整数加载而挂掉。这个版本会回退遍历小的算术序列,模拟栈,把它折叠回一个 ldc.i4,然后让正常的解析器继续。
所以,不是把它当作一种完全不同的常量保护,而是变成这样:
ldc.i4 0x1261
call string <const getter>(int32)
然后现有的标准/x86 常量解析器路径就能完成它的工作。
控制流是它表现一般的地方
Switch 移除器可以处理正常的 ConfuserEx switch 分发器形状。它遍历块,恢复下一个目标,删除死块,并生成一个合理的方法体。但有些样本只有部分方法能被理解。如果你修改了半个方法,然后发现它仍然被混淆,输出结果比没用还糟,因为现在你有了损坏的 IL,而且没有干净的方法来推断发生了什么。
所以这个版本在触碰方法体之前先对它做快照:
instructions
exception handlers
如果去混淆抛出异常,或者方法在之后看起来仍然被混淆,原始方法体会被恢复。日志仍然可以说“这个方法没有被解决”,但程序集不会仅仅因为一个方法有奇怪的分发器就被静默损坏。
跳转/蹦床控制流也有了自己的一轮处理。有些方法不仅仅是 switch 分发器。它们是小的分支蹦床链接在一起,直到到达真正的块。这些现在会被检测并折叠,而不是被仅 switch 的路径忽略。
压缩器移除器是必须在其他所有东西之前运行的部分。
ConfuserEx 压缩器存根通常保持真正的程序集被压缩,启动一个小的加载器,解压负载,并在运行时加载它。
移除器找到加载器形状,提取内嵌的负载,解压它,并将模块切换为真正的程序集。标准和紧凑压缩器布局都会被处理。
[+] Compressor detected
[+] Extracted compressed module payload
[+] Decompressed real module
[+] Continuing pipeline on unpacked assembly
反篡改现在有两条路径。
标准/动态反篡改从受保护的部分解密方法体,并将恢复的方法体写回模块。JIT 反篡改更烦人,因为方法体本应在运行时请求时才被物化。
大致形状是:
find init
extract keys
find encrypted JIT body section
derive per method key
read body
write CilBody back
这仍然是基于模式的。如果存根变化足够大,它就会漏掉,显然。
资源处理方式与常量类似:找到加密的资源 blob,恢复密钥/解密器形状,解密,如果需要则解压,然后把资源放回正常的 .NET 工具所期望的位置。
还有一个可选的嵌入式 PE 重建路径。一些受保护的样本在资源内部携带一个托管 PE。启用重建后,移除器会尝试解析并重写该负载,而不是留下一个已去混淆的外层程序集和一个未处理的内层程序集。
UnConfuserEx.exe sample.exe sample.clean.exe --rebuild-embedded-pe
当你确定样本在资源中隐藏了另一个托管程序集时使用这个选项。如果负载不是托管 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。
对于嵌入式托管负载:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe --rebuild-embedded-pe
这是当前的支持列表。这并不意味着每个可能的 ConfuserEx fork 都能工作。这意味着这些是流水线知道要查找的形状。
代码里可能还有更多我忘了列出的东西 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 <Module>::???????? as String with 41 call site method(s)
Removed all instances of getter <Module>::????????
一个被有意保留的控制流方法示例:
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
第二个日志并不完美,但至少是诚实的
一个改变了每个辅助程序形状的自定义 ConfuserEx fork。
一个通过完整的控制流混淆而不是小型栈表达式来计算其 ID 的常量 getter。
一个分发器依赖静态模拟器不知道的运行时值的控制流图。
需要实际运行时执行而不是 IL 模拟的原生辅助程序。
在被混淆之前就已经损坏的程序集。
具有不同加密方法体布局的 JIT 反篡改变体。
如果你想让问题报告有用,请包含足够的数据来复现它。
上传样本前请移除文件扩展名。
把所有东西一起打包,并包含以下内容:
Command:
UnConfuserEx.exe <target> <optional output>
Failure stage:
- compressor
- anti tamper
- constants
- control flow
- resources
- writing output
- runtime after deobfuscation
Expected result:
Actual result:
Console output:
Archive link:
Notes / investigation:
如果你只发送“不能用”,答案可能会是“是啊”。
小而专注的修复比大规模重写好。
如果你为新的保护形状添加支持,请将其隔离在拥有它的移除器中。如果你添加回退行为,请确保失败不会损坏输出程序集。如果你碰控制流,请假设你修复的那个奇怪样本不是唯一存在的奇怪样本 :DDD。
就是不要让这个工具更难调试。
这个项目基于 MadMin3r/UnconfuserEx。
原项目提供了基础和最核心的去混淆流水线。这个 fork 专注于提高可靠性、增加对额外保护变体的支持,以及处理在真实样本中观察到的边缘情况。
这个项目始于一个实用的逆向工程工具,而不是软件工程练习。重点一直是可靠地恢复受保护的程序集,而不是保持完美的代码质量。
这个工具旨在用于授权的恶意软件分析、逆向工程、软件恢复、互操作性和教育研究。
用户有责任遵守适用法律,并在分析、访问或处理软件或系统之前获得任何所需的授权。
Zypherion Technologies 不授权非法使用此工具,并且不承担第三方滥用的责任。
本仓库或 www.zypherion.tech 上的任何内容都不是法律建议。