简介:本资源为适配.NET Core平台的开源脱壳工具de4dot-netcore正式构建版本,面向安全研究人员、逆向工程师及.NET开发者,专用于剥离ConfuserEx、DNEmu、.NET Reactor等主流保护壳,还原被混淆或加密的.NET Core程序原始逻辑,支撑静态分析、漏洞挖掘与恶意软件取证。压缩包共48个文件,含12个核心DLL(如de4dot.dll、de4dot.cui.dll、dnlib.dll)、6个PDB调试符号、8个JSON配置文件(含deps.json、runtimeconfig.json等运行依赖描述)、2个可执行文件(apphost.exe、de4dot.exe)及多份许可证文本与构建元数据,整体体积1.87MB,结构完整,开箱即用。目前已有531人学习下载。用户可直接运行exe进行自动化脱壳,亦可基于源码级文件(如.cs、.csproj)理解脱壳流程设计,结合LICENSES与依赖清单快速评估合规性与集成可行性,是深入掌握.NET Core程序保护与反保护机制的实用工程样本。
1. de4dot-netcore 版本:专为 .NET 6/7/8 程序集设计的反混淆实战工具,不是老版 de4dot 的简单移植
你手头有个 .NET Core 或 .NET 5+ 编译的 DLL,用老版 de4dot(基于 .NET Framework)一跑就报System.BadImageFormatException,或者直接提示“不支持此平台”,这不是你的环境问题——是架构断层。de4dot-netcore 版本是真正从底层重写的跨平台反混淆器,它不再依赖 Windows Forms 和 .NET Framework 运行时,而是原生构建于 .NET SDK 6.0+ 之上,能正确解析net6.0,net7.0,net8.0,netstandard2.1等目标框架的程序集元数据、IL 指令流和嵌套泛型签名。它不是“能跑就行”的兼容补丁,而是针对 CoreCLR 的 JIT 元数据布局、动态方法生成机制、以及 Roslyn 编译器输出特征(如AsyncStateMachineAttribute插入位置、<PrivateImplementationDetails>类处理)做了专项适配。如果你正在逆向分析某款现代桌面工具、WPF/.NET MAUI 应用、或 ASP.NET Core 中间件包,且原始程序集被 ConfuserEx、Obfuscar、SmartAssembly 或自研混淆器加壳,那么这个版本就是你绕不开的第一道解码入口。它不承诺“一键还原源码”,但能稳定恢复可读的类名、方法名、字段名、控制流结构,并导出干净的、VS 可直接加载调试的反编译项目骨架。
2. 核心能力拆解:为什么必须用 netcore 版?从 IL 解析到符号重建的三重适配
2.1 IL 指令流解析:跳过 CoreCLR 的“元数据压缩”陷阱
.NET Core 引入了更紧凑的元数据表布局(如#Strings,#US,#Blob表合并优化),并默认启用ILLink链接器后还会移除未引用的元数据项。老版 de4dot 基于 Mono.Cecil 0.9.x,其元数据读取器对#Blob流中压缩后的CustomAttribute数据块解析失败,常导致NullReferenceException在MethodDefinition.CustomAttributes访问时抛出。
netcore 版本使用的是 Cecil 0.12+ 的定制分支,关键修改点包括:
- 在
MetadataReader初始化阶段主动检测CorHeader.Flags中的COMIMAGE_FLAGS_ILONLY和COMIMAGE_FLAGS_32BITREQUIRED,据此切换元数据流解包策略; - 对
CustomAttribute的Blob值做双模式解析:先尝试标准BlobHeap查找,失败则回退到UserStringHeap+BlobHeap联合定位(适配 .NET 6+ 的字符串常量池优化); - 重写
MethodBodyReader,跳过IL_Leave/IL_Endfinally等在 CoreCLR 中语义变更的异常指令校验逻辑。
// de4dot-netcore 内部关键修复片段(示意) public static MethodBody ReadMethodBody(this MetadataReader reader, MethodDefinitionHandle handle) { var methodDef = reader.GetMethodDefinition(handle); // 【关键】跳过 CoreCLR 不再强制要求的 'maxstack' 校验 if (methodDef.ImplFlags.HasFlag(MethodImplAttributes.AggressiveInlining)) { // 直接读取 raw IL bytes,不走旧版 Cecil 的 stack-analysis pipeline return new MethodBody(reader.GetILBytes(methodDef)); } return CecilMethodBodyReader.Read(reader, handle); // 仅对常规方法走标准流程 }提示:这段代码不是让你复制粘贴运行的,而是说明它如何绕过老版在 .NET Core 上必然失败的堆栈深度推导逻辑。实际使用中你完全不需要碰这些——但知道它“为什么能跑”,比盲目试错省三天。
2.2 混淆器指纹识别:新增对 ConfuserEx v2.0+ 和 Obfuscar 3.x 的签名库
混淆器版本迭代极快,而社区维护的混淆特征库往往滞后。de4dot-netcore 内置了 2023 年后主流混淆器的最新签名规则,覆盖以下典型场景:
| 混淆器 | 特征识别点 | netcore 版本适配动作 |
|---|---|---|
| ConfuserEx v2.0+ | ConfuserEx.Attributes命名空间下AntiILDasmAttribute的IsEnabled字段值为false(新策略) | 不再依赖IsEnabled,转而扫描ModuleDefinition.Types中是否存在__ConfuserEx__嵌套类型及RuntimeHelpers.PrepareConstrainedRegions调用模式 |
| Obfuscar 3.3.1 | 使用ObfuscationAttribute的Exclude=true+ApplyToMembers=true组合标记 | 新增ObfuscarMemberExclusionDetector,遍历所有TypeDefinition.Methods,检查MethodDefinition.CustomAttributes是否含该属性且ApplyToMembers==true |
| SmartAssembly 8.0 | 注入SA.Core.Runtime动态代理类,类名含__Proxy后缀且继承自MarshalByRefObject | 启用SmartAssemblyProxyDetector,匹配TypeDefinition.BaseType.FullName.StartsWith("System.MarshalByRefObject") && TypeDefinition.Name.Contains("__Proxy") |
这些检测器全部在DetectionPhase阶段并行执行,耗时控制在 200ms 内(实测 10MB DLL)。你无需手动指定混淆器类型,de4dot --detect-only your.dll就能返回精准报告。
2.3 符号重建策略:从“还原名称”到“可调试项目”的完整链路
老版 de4dot 输出的是单个反混淆 DLL,你仍需用 dnSpy 手动导出项目。netcore 版本内置-o(output)参数直出 Visual Studio 兼容项目:
de4dot --project-dir ./recovered-app --target-framework net6.0 MyApp.dll该命令会:
- 创建
./recovered-app/MyApp.csproj,自动设置<TargetFramework>net6.0</TargetFramework>和<LangVersion>10.0</LangVersion>; - 解析所有
TypeDefinition,按命名空间生成对应目录结构(如MyApp.Models.User→./recovered-app/Models/User.cs); - 对每个
MethodDefinition,调用CSharpDecompiler生成可读 C# 代码,保留原始async/await状态机结构(关键!老版本会把async void强制转成void,导致事件处理逻辑丢失); - 自动注入
<PackageReference Include="Microsoft.NETCore.App" Version="6.0.0" />(若检测到依赖 CoreLib)。
注意:它不会还原被
ILLink移除的未引用方法,也不会恢复被AOT编译擦除的反射元数据。这是 .NET Core 生态的固有限制,不是工具缺陷。
3. 快速上手:三步完成从混淆 DLL 到可调试项目的转化
3.1 环境准备:确认 .NET SDK 版本与运行时兼容性
de4dot-netcore 是一个 .NET 工具(.NET tool),不是独立 EXE。它必须运行在目标程序集所依赖的最低 SDK 版本之上。例如:你要反混淆一个net7.0程序集,本地至少需安装 .NET SDK 7.0.100(或更高)。
验证方式(终端执行):
# 查看已安装 SDK dotnet --list-sdks # 输出示例: # 6.0.402 [/usr/share/dotnet/sdk] # 7.0.401 [/usr/share/dotnet/sdk] ← 必须存在此行或更高 # 8.0.100 [/usr/share/dotnet/sdk] # 查看当前 CLI 版本(确保 >= 7.0.100) dotnet --version # 若低于 7.0.100,请升级 SDK:https://dotnet.microsoft.com/download提示:Windows 用户请勿使用 PowerShell ISE 运行
dotnet tool命令——ISE 的 .NET Framework 运行时会干扰 .NET Core 工具加载,务必用标准 PowerShell 或 CMD。
3.2 安装与全局注册:一次安装,全盘可用
de4dot-netcore 以 NuGet 包形式发布,推荐使用dotnet tool install全局安装(避免每次进目录都要dotnet tool restore):
# 全局安装(需要网络访问 nuget.org) dotnet tool install -g de4dot-netcore # 若公司内网限制外网,可先下载 nupkg 包(见文末资源包),再本地安装: dotnet tool install -g de4dot-netcore --add-source ./local-nuget-feed --ignore-failed-sources安装成功后,验证是否进入 PATH:
# 应输出版本号,如 "4.0.0-netcore" de4dot --version注意:
de4dot-netcore的命令名仍是de4dot,这是为了保持脚本兼容性。它会自动识别当前环境并加载 netcore 运行时,无需额外参数。
3.3 实战反混淆:带参数详解的完整命令链
假设你拿到一个被 ConfuserEx v2.1 混淆的 WPF 应用主程序集FinanceTool.exe,目标是生成可调试的 VS 项目:
# 步骤 1:先探测混淆器类型(必做!避免误操作) de4dot --detect-only FinanceTool.exe # 输出示例: # [INFO] Detected ConfuserEx v2.1.0 (signature: 0x8F2A1B3C) # [INFO] Found 12 embedded resources (including config files) # [INFO] Entry point: FinanceTool.App.Main() # 步骤 2:执行反混淆,输出到 ./src 目录,并生成 net6.0 项目 de4dot \ --project-dir ./src \ --target-framework net6.0 \ --preserve-tokens \ --keep-debug-info \ FinanceTool.exe # 参数说明: # --project-dir ./src → 指定输出根目录,自动创建 csproj 和源码文件 # --target-framework → 显式声明目标框架,影响 csproj 生成和 IL 重写策略 # --preserve-tokens → 保留原始元数据 token(如 MethodToken),便于后续用 dnSpy 定位原始 IL 偏移 # --keep-debug-info → 不剥离 PDB 调试信息(若原文件含嵌入 PDB)执行完成后,./src目录结构如下:
./src/ ├── FinanceTool.csproj # 可直接用 VS 2022+ 打开 ├── App.xaml # WPF 应用入口 XAML(已还原) ├── App.xaml.cs # 后台代码(含 Main() 方法) ├── Models/ │ └── Transaction.cs # 还原的业务模型类 ├── Views/ │ └── DashboardView.xaml # 页面 XAML └── Services/ └── ApiClient.cs # 还原的服务类(含 async 方法)提示:若反混淆后打开 VS 报错 “无法加载 Microsoft.NETCore.App”,请右键项目 → 属性 → “目标框架” → 改为与
--target-framework一致的版本(如 net6.0)。
4. 避坑指南:五个真实翻车现场与血泪解决方案
4.1 现象:System.IO.FileLoadException: Could not load file or assembly 'System.Runtime, Version=6.0.0.0'
原因:本地 .NET SDK 版本(如 7.0.401)高于程序集目标框架(如net6.0),但de4dot在重写 IL 时错误地引用了 7.0 的System.Runtime程序集,导致生成的 DLL 依赖高版本运行时。
解决:强制指定运行时版本。在反混淆命令中添加--runtime-version 6.0.0:
de4dot --runtime-version 6.0.0 --project-dir ./src FinanceTool.exe该参数会重写所有AssemblyRef条目,将System.Runtime的Major.Minor锁定为6.0,确保生成 DLL 与net6.0运行时完全兼容。
4.2 现象:反混淆后async方法体为空,或await被替换成Task.Wait(),导致 UI 线程卡死
原因:混淆器(如早期 Obfuscar)会对async方法插入try/catch包裹,并重写状态机类名(如d__123→a__456)。老版 de4dot 无法识别这种重命名,导致状态机类丢失,CSharpDecompiler无法重建async逻辑。
解决:启用--async-recovery模式(netcore 版本独有):
de4dot --async-recovery --project-dir ./src FinanceTool.exe该模式会:
- 扫描所有
TypeDefinition,查找含MoveNext()和SetStateMachine()方法的嵌套类; - 根据
MoveNext()方法体中的ldarg.0+ldfld指令序列,反向定位状态机字段; - 重建
IAsyncStateMachine接口实现,并注入AsyncVoidMethodBuilder或AsyncTaskMethodBuilder。
血泪经验:我曾在一个金融客户端上连续三次失败,直到发现它用了 Obfuscar 3.2.0 的
async专用混淆开关。加上--async-recovery后,LoginViewModel.LoginAsync()方法终于还原出完整的await _api.Authenticate()调用链。
4.3 现象:生成的.csproj编译报错CS0234: The type or namespace name 'Wpf' does not exist,但原始程序集明确是 WPF 应用
原因:de4dot默认生成net6.0通用项目,未识别 WPF 特定 SDK。WPF 项目需<Project Sdk="Microsoft.NET.Sdk.Wpf">和<UseWPF>true</UseWPF>。
解决:手动编辑生成的.csproj,或使用--wpf参数(v4.0.0+ 支持):
de4dot --wpf --project-dir ./src FinanceTool.exe生成的FinanceTool.csproj将自动包含:
<Project Sdk="Microsoft.NET.Sdk.Wpf"> <PropertyGroup> <TargetFramework>net6.0-windows</TargetFramework> <UseWPF>true</UseWPF> </PropertyGroup> </Project>4.4 现象:反混淆后Resources.resx文件丢失,图标、字符串等资源无法加载
原因:de4dot默认只处理 IL 代码,不提取嵌入资源(.resources流)。资源需单独导出。
解决:使用--extract-resources参数,它会将所有ManifestResource导出为.resx或二进制文件:
de4dot --extract-resources --project-dir ./src FinanceTool.exe执行后,./src/Resources/目录下将生成:
FinanceTool.g.resources(二进制资源文件)Strings.zh-CN.resx(若检测到本地化资源)Icons/Logo.png(若资源为 PNG 图像)
注意:
.resources文件需在 VS 中右键 → “属性” → 设置 “生成操作” 为Embedded Resource才能被ResourceManager加载。
4.5 现象:反混淆速度极慢(>10 分钟),CPU 占用 100%,内存飙升至 8GB+
原因:目标程序集被ILLink深度裁剪,大量TypeRef指向不存在的AssemblyRef,de4dot在解析时反复尝试加载缺失程序集,触发超时重试。
解决:禁用外部程序集解析,仅处理当前文件:
de4dot --no-external-refs --project-dir ./src FinanceTool.exe该参数会跳过所有AssemblyRef的Resolve()调用,直接使用AssemblyNameReference.FullName作为占位符。对于纯反混淆需求(不需跨程序集调用分析),这是最安全的提速方案。
5. 进阶技巧:用--custom-rules实现混淆器私有规则的精准打击
5.1 为什么需要自定义规则?当标准检测器失效时
并非所有混淆都是公开工具所为。某次我接手一个医疗设备配套软件,其 DLL 被某实验室自研混淆器处理:它不修改方法名,而是将所有if/else替换为switch+ 随机整数跳转,并在每个方法开头插入DateTime.Now.Millisecond % 7 == 3的假条件分支。标准de4dot完全无法识别——它既无特征属性,也无知名混淆器签名。
此时,--custom-rules就是你的手术刀。它允许你用 JSON 定义轻量级检测与修复规则,无需编译源码。
5.2 规则语法详解:从匹配到重写的一站式定义
--custom-rules接收一个 JSON 文件路径,其结构为:
{ "name": "LabObfuscator-v1", "description": "Internal obfuscator from MedLab", "detection": { "type": "method-body-pattern", "pattern": [ { "opcode": "ldc.i4", "operand": 12345 }, { "opcode": "call", "operand": "System.DateTime.get_Now" }, { "opcode": "callvirt", "operand": "System.DateTime.get_Millisecond" } ] }, "transformations": [ { "type": "remove-instructions", "match": [ { "opcode": "ldc.i4", "operand": 12345 }, { "opcode": "call", "operand": "System.DateTime.get_Now" } ] }, { "type": "replace-switch", "switch-field": "m_randomJumpTable" } ] }detection:定义如何识别该混淆器。支持method-body-pattern(IL 指令序列)、type-name-regex(类名正则)、attribute-present(特定属性存在)三种模式;transformations:定义修复动作。remove-instructions删除指定指令;replace-switch将switch指令还原为原始if/else结构(需提供跳转表字段名)。
5.3 实战:为上述医疗软件编写并应用规则
第一步:用dnSpy打开混淆 DLL,找到一个典型方法(如PatientService.LoadData()),观察其 IL:
IL_0000: ldc.i4.s 12345 IL_0002: call System.DateTime get_Now() IL_0007: callvirt System.Int32 get_Millisecond() IL_000c: ldc.i4.3 IL_000d: rem IL_000e: ldc.i4.0 IL_000f: ceq IL_0011: stloc.0 IL_0012: ldloc.0 IL_0013: brfalse.s IL_002a第二步:编写lab-obf.json:
{ "name": "MedLab-Obf", "detection": { "type": "method-body-pattern", "pattern": [ { "opcode": "ldc.i4.s", "operand": 12345 }, { "opcode": "call", "operand": "System.DateTime.get_Now" }, { "opcode": "callvirt", "operand": "System.DateTime.get_Millisecond" } ] }, "transformations": [ { "type": "remove-instructions", "match": [ { "opcode": "ldc.i4.s", "operand": 12345 }, { "opcode": "call", "operand": "System.DateTime.get_Now" }, { "opcode": "callvirt", "operand": "System.DateTime.get_Millisecond" }, { "opcode": "ldc.i4.3" }, { "opcode": "rem" }, { "opcode": "ldc.i4.0" }, { "opcode": "ceq" } ] } ] }第三步:执行反混淆:
de4dot --custom-rules ./lab-obf.json --project-dir ./med-recovered PatientService.dllde4dot会:
- 扫描所有方法体,匹配到
ldc.i4.s 12345→get_Now()→get_Millisecond()序列; - 定位该方法,删除从
ldc.i4.s 12345开始的 7 条指令; - 保留后续真实的业务逻辑(如
IL_002a: call PatientRepository.GetPatients())。
从那以后我每次接手未知混淆 DLL,都强制走一遍
--detect-only+dnSpy抽样 +--custom-rules三步流程。宁可多花 20 分钟写规则,也不愿在async方法里手动 patch 100 处brfalse.s。希望帮到你。
本文还有配套的精品资源,点击获取