简介:de4dot-netcore 是经典 .NET 脱壳工具 de4dot 面向 .NET Core 环境适配的版本,主要服务于安全研究人员、逆向工程师及恶意样本分析人员,用于剥离 ConfuserEx、.NET Reactor、Themida 等常见保护壳,还原可分析的原始程序集。压缩包共 48 个文件,约 1.87MB,以 dll 动态库、pdb 调试符号、json 运行时配置、txt 许可说明及 exe 可执行文件为主,另含少量 cs 源码与缓存文件,结构上保留了 de4dot 主程序、code、blocks、mdecrypt 等模块及 dnlib 依赖,便于直接运行或二次定制。已有 532 人学习下载,说明其在 .NET Core 逆向场景中具备一定参考价值。读者可获得一套可落地的脱壳工具链,用于静态与动态分析前的样本预处理,并借助开源代码理解壳识别与保护逻辑还原思路,为漏洞挖掘和取证分析提供支撑。
1. 装 de4dot 之前,先把 .NET 运行时这关过了
上周帮一个做逆向分析的朋友收拾一台旧机器,他要在上面跑 de4dot 做程序集反混淆,结果dotnet de4dot.dll一敲下去直接报You must install .NET to run this application,折腾了一下午没搞定。这场景太常见了:de4dot 原版是 .NET Framework 时代的产物,后来有人把它迁到 .NET Core / .NET 上重新编译,于是就有了 de4dot-netcore 这个分支。名字里带 netcore,很多人以为装个 SDK 就完事,实际上运行时版本、目标框架、依赖库三者对不上,程序连启动都启动不了。这篇笔记记录的是我在 Windows 和 Linux 上把 de4dot-netcore 从零装到能跑通的全过程,包括版本怎么选、依赖怎么补、报错怎么救。适合两类人:一是第一次接触 de4dot 想拿它处理混淆程序集的开发者,二是已经装过原版但被 netcore 分支的运行时问题卡住的老手。装不好会怎样?轻则命令找不到入口,重则加载程序集时抛FileNotFoundException,你还以为是目标文件坏了。
2. 环境准备与版本选择:netcore 分支到底要装什么
2.1 先搞清楚 de4dot-netcore 和原版的区别
de4dot 本身是一个开源的 .NET 反混淆工具,原版基于 .NET Framework 编译,在 Windows 上双击 exe 就能跑。de4dot-netcore 是把同一套反混淆逻辑迁移到 .NET Core / .NET 5+ 运行时上的分支,好处是能跨平台,Linux 和 macOS 上也能用命令行跑。代价是它不再自带运行时,你必须自己装对应版本的 .NET Runtime,而且它对目标框架的声明比较严格——编译时用的是哪个大版本,运行时就得有对应大版本。
我一般会先确认手头这份 de4dot-netcore 是用哪个目标框架编的。方法很简单,看发布包里有没有de4dot.runtimeconfig.json,打开看framework字段:
{ "runtimeOptions": { "tfm": "net6.0", "framework": { "name": "Microsoft.NETCore.App", "version": "6.0.0" } } }这段配置的意思是:这个程序需要Microsoft.NETCore.App6.0.0 及以上。注意tfm写的是net6.0,那你就不能只装 .NET 8 运行时指望它向下兼容——.NET 的主版本之间默认不互相回退,装错了就是启动失败。常见做法是照着tfm装同大版本的 Runtime,比如net6.0就装 6.0.x 的最新补丁版。
2.2 运行时版本对照表
不同发布包的目标框架不一样,我整理了一张对照表,装之前先对号入座:
| runtimeconfig 里的 tfm | 需要安装的运行时 | 下载渠道关键词 |
|---|---|---|
| net6.0 | .NET Runtime 6.0.x | dotnet-runtime-6.0 |
| net7.0 | .NET Runtime 7.0.x | dotnet-runtime-7.0 |
| net8.0 | .NET Runtime 8.0.x | dotnet-runtime-8.0 |
| netcoreapp3.1 | .NET Core Runtime 3.1.x | dotnet-runtime-3.1 |
如果你拿到的包没有 runtimeconfig,而是自包含发布(self-contained),那目录里会有一堆原生依赖库,这种反而不需要额外装运行时,直接运行可执行文件即可。判断方法:看目录里有没有hostfxr.dll(Windows)或libhostfxr.so(Linux),有就是自包含。
提示:不要图省事装 SDK 代替 Runtime。SDK 里确实包含 Runtime,但版本管理更复杂,多个 SDK 并存时
dotnet命令解析到的运行时不一定是你以为的那个。只跑工具的话,装对应版本的 Runtime 最干净。
2.3 Windows 上的运行时安装
Windows 上我习惯用 winget 装,省得去翻下载页:
winget install Microsoft.DotNet.Runtime.6这条命令装的是 .NET 6 运行时。装完用下面这条确认:
dotnet --list-runtimes输出里应该能看到Microsoft.NETCore.App 6.0.x。如果列表里同时有 6、7、8 好几个版本,不用慌,de4dot 启动时会按 runtimeconfig 里的版本去匹配,匹配不到才报错。真正要小心的是只装了 8 没装 6 的情况,这时候dotnet de4dot.dll会提示找不到框架。
Linux 上各发行版包名不同,Debian/Ubuntu 系常见做法是先加软件源再装:
sudo apt-get update sudo apt-get install -y dotnet-runtime-6.0装完同样用dotnet --list-runtimes验证。如果系统里根本没有dotnet命令,说明连 host 都没装,需要先装dotnet-host或者直接用官方脚本安装运行时。
2.4 目录结构与依赖检查
把 de4dot-netcore 的发布包解压到一个固定目录,比如D:\tools\de4dot-netcore或/opt/de4dot-netcore。解压后先看目录里有什么:
ls -la /opt/de4dot-netcore正常情况下应该能看到de4dot.dll、de4dot.runtimeconfig.json、de4dot.deps.json,以及若干依赖 dll。deps.json记录了所有依赖程序集的版本,如果这个文件缺失或者和实际 dll 对不上,运行时会抛Could not load file or assembly。我遇到过有人手动删了“看起来没用”的 dll,结果 de4dot 加载目标程序集时直接崩,所以解压后别乱动目录里的文件。
依赖检查还有一个土办法:直接跑一次dotnet de4dot.dll --help,能打印帮助就说明运行时和依赖都齐了;如果报缺某个 dll,按报错里的程序集名去补对应 NuGet 包解压出来的 dll,放到同目录即可。
3. 安装与首次运行:从命令行到能处理第一个程序集
3.1 用 dotnet 命令启动的正确姿势
de4dot-netcore 不是双击就能跑的 exe(除非是自包含发布),标准启动方式是:
dotnet /opt/de4dot-netcore/de4dot.dll --help这里dotnet是运行时宿主,后面跟 dll 路径。参数说明:--help只是打印用法,不处理文件。第一次跑建议就用--help,确认宿主能找到 dll、运行时版本匹配、依赖加载正常。如果这一步就报错,后面处理文件肯定也跑不通,先把启动问题解决掉。
Windows 上路径带空格时要加引号:
dotnet "D:\tools\de4dot-netcore\de4dot.dll" --help常见的一个坑是当前工作目录不对。de4dot 加载依赖时默认从 dll 所在目录找,但如果你用相对路径启动,某些情况下会去当前目录找依赖,导致找不到。我一般会先cd到工具目录再执行,或者直接用绝对路径。
3.2 处理第一个程序集:参数怎么给
确认能打印帮助后,拿一个测试程序集跑一遍。最基本的用法:
dotnet /opt/de4dot-netcore/de4dot.dll -f "input.exe" -o "output.exe"参数含义:-f指定输入文件,-o指定输出文件。de4dot 会读取输入程序集,识别混淆器类型,做反混淆处理后写到输出文件。注意它默认不会覆盖原文件,-o必须给一个不同的路径,否则会报错拒绝写入。
如果想先看看它识别出什么混淆器,可以加-d只检测不处理:
dotnet /opt/de4dot-netcore/de4dot.dll -d "input.exe"输出里会打印检测到的混淆器名称和版本。这一步很有用:如果检测结果为空或者报“unknown obfuscator”,说明这个程序集可能没混淆,或者混淆器版本太新 de4dot 不认识,这时候硬跑反混淆意义不大。
3.3 常用参数组合与取舍
实际使用中我常用的参数组合是这样的:
dotnet /opt/de4dot-netcore/de4dot.dll \ -f "input.exe" \ -o "output.exe" \ --dont-rename \ --keep-types--dont-rename表示不重命名符号,适合你还要对照原始符号调试的场景;--keep-types保留类型定义不被清理。这两个参数是取舍点:默认情况下 de4dot 会尽量清理和重命名,让输出更“干净”,但如果你后续还要用原始类型名做匹配,就得关掉重命名。反过来,如果你只是想让程序集能正常反编译阅读,默认行为就够用。
还有一个-p参数指定混淆器类型,当你明确知道目标用了哪种混淆器时可以跳过自动检测,加快速度。但自动检测失败时手动指定往往也救不回来,因为检测失败通常意味着特征不匹配,手动指定只是跳过检测直接按该类型处理,可能产出错误结果。我一般先-d看检测结果,检测不出来就不硬上。
3.4 输出验证:怎么确认处理成功
处理完不能只看命令退出码。de4dot 有时会“成功”输出一个文件,但里面方法体是空的或者类型引用断了。验证方法是用一个反编译工具(比如 ILSpy 的命令行版或者 monodis)打开输出文件,看类型和方法是否完整:
monodis --typedef output.exe | head -20能正常列出类型定义,说明程序集结构没被破坏。再抽查几个方法体,确认不是空的。如果输出文件比输入小了一大截,大概率是清理过度,把有用的东西删了,这时候要回退参数,去掉--keep-types之外的激进选项重跑。
4. 避坑与排查:de4dot-netcore 装完跑不起来的五种情况
4.1 报 “You must install .NET to run this application”
现象:执行dotnet de4dot.dll直接提示需要安装 .NET,但明明已经装了运行时。
原因:最常见的是装了大版本不匹配的运行时,比如程序要 6.0 你只装了 8.0;其次是装了 SDK 但 PATH 里的dotnet指向了另一个没有目标运行时的安装位置。
解决:先dotnet --list-runtimes看实际有哪些版本,对照 runtimeconfig 里的tfm补装同大版本。如果列表里明明有 6.0 还是报错,检查是不是有多个 dotnet 安装(比如系统级和用户级各一份),用which dotnet或where dotnet确认当前用的是哪个。
4.2 报 “Could not load file or assembly” 缺依赖
现象:启动时报某个 dll 找不到,程序集名通常是ICSharpCode.Decompiler或dnlib之类。
原因:发布包解压不完整,或者有人手动删了目录里的依赖 dll;也可能是从压缩包里直接运行没解压,导致依赖路径解析失败。
解决:重新完整解压到一个本地目录,不要在压缩包预览里直接跑。确认deps.json里列出的依赖在目录里都存在。如果确实缺某个包,按报错里的程序集名去对应 NuGet 包下载,解压出 dll 放到同目录。
4.3 处理时卡住或内存暴涨
现象:跑大程序集时进程内存一路涨到几个 G,最后被系统杀掉。
原因:de4dot 反混淆需要把整个程序集加载进内存做分析,大文件(几十 MB 以上)加上复杂混淆会非常吃内存。32 位进程还有 2G 地址空间限制。
解决:确认用的是 64 位 dotnet 宿主;给系统留足内存;对超大程序集可以先用-d只检测,确认值得处理再跑完整流程。如果反复 OOM,考虑拆分程序集或者换用更高配置的机器。
4.4 输出文件无法被反编译工具打开
现象:de4dot 跑完没报错,但输出文件用反编译工具打开报元数据损坏。
原因:参数过于激进导致清理了必要的元数据,或者输入程序集本身有强名称签名,处理后签名失效但引用还在。
解决:去掉--dont-rename之外的清理类参数重跑;对强名称程序集,处理前先确认是否需要保留签名,必要时用sn -Vr跳过验证或者处理后重新签名。我一般会保留一份原始输入文件,输出异常时能立刻对比。
4.5 Linux 上权限或路径问题
现象:dotnet de4dot.dll报权限拒绝,或者找不到文件。
原因:解压后的 dll 没有读权限,或者路径里有中文/空格导致解析异常。
解决:chmod +r给 dll 读权限;路径尽量用纯英文无空格;用绝对路径启动避免工作目录问题。Linux 上还要注意大小写敏感,De4dot.dll和de4dot.dll是两个文件。
5. 装完怎么验证跑通:一套可复现的自检流程
装完 de4dot-netcore 之后,我习惯走一遍固定自检,确认这套环境真的能用,而不是“看起来装上了”。这套流程分四步,每步都有明确的通过标准。
第一步,验证运行时匹配。执行dotnet --list-runtimes,输出里必须包含 runtimeconfig 里tfm对应的大版本。比如tfm是net6.0,列表里就得有Microsoft.NETCore.App 6.0.x。这一步不通过,后面全白搭。
第二步,验证工具能启动。执行dotnet /path/to/de4dot.dll --help,能打印出参数列表就算过。如果这步报缺依赖,回到第 4 章对应条目处理。
第三步,用一个小程序集做端到端测试。找一个自己编译的、没有混淆的测试 exe,跑:
dotnet /path/to/de4dot.dll -d test.exe通过标准是能正常输出检测结果(哪怕结果是“未检测到混淆器”),且进程退出码为 0。这一步验证的是工具能读取程序集、能走完检测流程。
第四步,做一次实际处理并验证输出。对同一个测试 exe 跑-f test.exe -o test_out.exe,然后用反编译工具打开test_out.exe,确认类型和方法数量与输入一致。我一般会对比输入输出的类型数量:
monodis --typedef test.exe | wc -l monodis --typedef test_out.exe | wc -l两个数字应该相同或接近。如果输出明显少了很多,说明清理过度,需要调整参数重跑。
这套自检我踩过一次坑才固定下来:有回装完直接拿生产程序集跑,结果输出文件损坏,回头排查发现是运行时版本差了一个补丁号,--help能过但实际处理时某个 API 行为不一致。从那以后我每次装完 de4dot-netcore,都强制走一遍这四步,尤其是第三步和第四步,绝不用“能打印帮助”当作装好的标准。希望这套流程帮到你,少在运行时版本上翻车。
本文还有配套的精品资源,点击获取