rea-reverse

ILSpy、dnSpyEx、dotPeek 怎么选:2026 年 .NET 反编译器对比

基于 REA 6.4.0更新于 约 8 分钟读完
本页目录

结论先行(以 2026-10-14 为准): 想要免费、开源、能在 Windows、macOS、Linux 上原生运行,还带一个可脚本化命令行版本(ilspycmd)的反编译器,选 ILSpy。需要在 Windows 上对没有源码的 .NET 代码单步调试,选 dnSpyEx。已经在用 JetBrains 全家桶、平台是 Windows,想要一个还能充当符号服务器的免费反编译器,选 dotPeek。原版 dnSpy 仓库从 2020 年起就已归档,dnSpyEx 是仍在维护的延续版本。

本文面向需要选一款反编译器的 .NET 开发者:可能是想看懂自己的构建产物,可能是某个依赖没附源码,也可能是自己维护的代码崩在了一个说不清的地方。内容包括当前版本、平台和许可证,用我自己写的一个 DLL 做的反编译实测,自动化方式,以及 REA(morluto 的开源项目)如何记录反编译结果。

范围与公平性说明。 下文所有事实都来自各项目自己的发布页或官网,核对日期 2026-10-14。ILSpy 的 ilspycmd 11.1 我亲自运行过。dnSpyEx 和 dotPeek 是 Windows 程序,而本文是在 macOS 上写的,所以这两款我没有运行,相关描述均来自官方文档,并在文中注明出处。dnSpyEx 还能编辑并保存程序集,本文不涉及这部分:请只用于你自己的代码,或你有权检查的代码。

三款工具一览

ILSpy dnSpyEx dotPeek
最新版本(2026-10-14 核对) 11.1,2026-09-23 6.6.0,2026-06-20 2026.2.3.1,2026-10-01
许可证 MIT(开源) GPL-3.0(开源) 免费,闭源
平台 Windows、macOS、Linux Windows(WPF) Windows(提供 x64、x86、ARM64 下载)
反编译为 C# 支持 支持 支持
调试器 无 有 无(可为调试器提供符号)
命令行 ilspycmd(dotnet tool) dnSpy.Console 导出 产品页未提及
单文件包 支持 我参考的资料里未提及 我参考的资料里未提及

出处:ILSpy 11.1 与 11.0 发布说明、dnSpyEx 6.6.0 发布说明及其 README、JetBrains 的 dotPeek 页面。dotPeek 的版本号和平台来自 JetBrains 公开的发布信息接口,其中只有 Windows 下载。

2026 年各自的现状

ILSpy:已经跨平台

ILSpy 11.0(2026 年 8 月)把界面从 WPF 移植到了 Avalonia。项目原话是:移植之后,ILSpy 除了 Windows 也能在 Linux 和 Mac 上使用,WPF 前端被整个移除。11.1 的发布页提供 Windows x64 和 ARM64 安装包、Linux 的 zip 以及 .deb、.rpm 包,还有 macOS ARM64 的 zip。长期以来“Mac/Linux 上有没有 dnSpy 替代品”这个问题,在浏览和反编译层面终于有了答案;但它没有调试器。

11.0 还专门为 agent 使用场景改进了 ilspycmd:新增单成员反编译的 --member 和导出元数据表的 --dump-table。发布说明提到这个 dotnet tool 的下载量已超过每周 7 万次,并把增长归功于 Claude 等 agent。

Visual Studio 调试器自带的反编译功能就是基于 ILSpy 的。微软的 调试时反编译文档 写明,源码生成基于开源的 ILSpy 项目,从 Visual Studio 2019 16.5 起可用。

dnSpy 与 dnSpyEx

GitHub 上原版 dnSpy/dnSpy 仓库已归档,最后一次推送是 2020-12-20。dnSpyEx 自称是 dnSpy 的非官方延续,一个调试器兼 .NET 程序集编辑器。6.6.0 是它两年来第一个稳定版:升级到 .NET 10,新增依赖系统已装 .NET 10 运行时的 dnSpy-net 发行包,新增 String References 窗口,修复了 runtime-async 方法导致的反编译崩溃。

发布页上有两点会影响你的选择:

  • 它是 Windows 上的 WPF 程序。6.6.0 能检测自己是否运行在 Linux 的 Wine 下,并关闭 WPF 硬件加速以提高兼容性。这只是兼容性调整,不是 Linux 或 macOS 版本。
  • 维护者明确表示,不会有官方的 dnSpy AI 集成或 MCP 服务器。

大多数人选它是为了调试器。README 列出的能力包括:无需源码即可调试 .NET Framework、.NET 和 Unity 程序集,可以在任意程序集里下断点,有局部变量、监视、调用栈等窗口。

dotPeek

JetBrains 对 dotPeek 的描述是:一款基于 ReSharper 内置反编译器的免费独立工具。它能把程序集反编译为 C# 或 IL,能把反编译结果保存为 Visual Studio 项目,还能充当符号服务器,让 Visual Studio 调试器单步进入反编译出来的库代码。当前帮助文档标注为 dotPeek 2026.2。JetBrains Rider 有自己的集成反编译器,也能调试反编译出的外部代码;按 Rider 2026.2 的 外部代码调试 帮助,在 ARM64 的 macOS 上暂不支持单步进入外部方法。Rider 是单独授权的 IDE,我没有测试。

同一个 DLL 的反编译结果

为了看输出质量,我写了一个小库,里面有 record、LINQ 链、async 方法和 switch 表达式,用 .NET 10 SDK 以 Release 配置编译(Demo.dll,10,240 字节):

public record Order(int Id, string Customer, decimal Total, bool Paid);

public static class OrderReport
{
    public static IEnumerable<string> TopCustomers(IEnumerable<Order> orders, int count) =>
        orders.Where(o => o.Paid)
              .GroupBy(o => o.Customer)
              .OrderByDescending(g => g.Sum(o => o.Total))
              .Take(count)
              .Select(g => $"{g.Key}: {g.Sum(o => o.Total):0.00}");

    public static async Task<int> CountLinesAsync(string path, CancellationToken ct = default)
    {
        using var reader = new StreamReader(path);
        var lines = 0;
        while (await reader.ReadLineAsync(ct) is not null)
            lines++;
        return lines;
    }

    public static string Describe(Order o) => o switch
    {
        { Paid: false } => "unpaid",
        { Total: > 1000m } => "large",
        _ => "normal",
    };
}

ilspycmd -t Demo.Order Demo.dll 还原出的 record 只有一行,和源码完全一样:

public record Order(int Id, string Customer, decimal Total, bool Paid);

ilspycmd -t Demo.OrderReport Demo.dll 的输出如下(省略了 using 行):

public static IEnumerable<string> TopCustomers(IEnumerable<Order> orders, int count)
{
	return from g in (from o in orders
			where o.Paid
			group o by o.Customer into g
			orderby g.Sum((Order o) => o.Total) descending
			select g).Take(count)
		select $"{g.Key}: {g.Sum((Order o) => o.Total):0.00}";
}

public static async Task<int> CountLinesAsync(string path, CancellationToken ct = default(CancellationToken))
{
	using StreamReader reader = new StreamReader(path);
	int lines = 0;
	while (await reader.ReadLineAsync(ct) != null)
	{
		lines++;
	}
	return lines;
}

public static string Describe(Order o)
{
	if ((object)o != null)
	{
		if (!o.Paid)
		{
			return "unpaid";
		}
		if (o.Total > 1000m)
		{
			return "large";
		}
	}
	return "normal";
}

可以看出:

  • record 和 async 方法基本按原样还原,编译器生成的状态机被隐藏了。
  • LINQ 链逻辑正确,但被改写成了查询语法,lambda 参数还标上了显式类型。
  • switch 表达式 变成了嵌套的 if,外加一个 (object)o != null 判断。逻辑一样,写法不一样。不同反编译器、不同版本之间,模式匹配往往是差别最大的地方。

在 Apple M5 Pro 上,反编译这一个类型耗时 0.98 秒,峰值内存 136 MB。dnSpyEx 和 dotPeek 我没有运行,所以无法给出它们对同一个 DLL 的输出。如果你要自己对比,用同一份构建,先看这四种语法结构。

没有源码时调试自己的代码

最常见的正当场景:崩溃发生在你依赖的某个库里,或者发生在自己应用的某个旧版本里,而那个版本的 PDB 已经找不到了。

  1. 先找源码。 如果包启用了 Source Link,或者 PDB 还在,普通调试器就能显示真实源码。微软的反编译文档也把启用了 Source Link 的库和 NuGet 包指向这条路。
  2. Visual Studio 2022 17.7 及更高版本 在关闭“仅我的代码”后单步进入外部 .NET 代码时,或者在调用栈窗口里双击某一帧时,会自动反编译,并且可以在生成的代码里下断点。微软列出的限制包括:优化过的构建可能把断点绑到错误的行,局部变量名可能丢失,async 代码可能反编译不完整。
  3. dnSpyEx 以独立 Windows 工具的形式提供同样的“无源码调试”能力,覆盖 .NET Framework、.NET 和 Unity。
  4. dotPeek 可以充当符号服务器,让 Visual Studio 调试器拿到没有 PDB 的程序集的源码。

ILSpy 自己不能调试。ilspycmd -genpdb 可以从反编译代码生成可移植 PDB,供调试器加载;这条路我在本文中没有测试。

自动化:ilspycmd 与 REA

ilspycmd 以 .NET tool 形式安装:

dotnet tool install ilspycmd --version 11.1.0.9782 --tool-path ./tools
./tools/ilspycmd --version
# ilspycmd: 11.1.0.9782
# ICSharpCode.Decompiler: 11.1.0.9782

我用到的选项:-t <type> 反编译单个类型,-m <token 或 doc id> 反编译单个成员,-l c 列出类,-p -o <dir> 导出可编译项目,--bundle-entry 和 -d 处理单文件包(见 这个 EXE 是不是 .NET 写的),脚本里加 --disable-updatecheck。遇到 NativeAOT 程序或原生 apphost,它会报 PE file does not contain any managed metadata.

REA 6.4.0 自己不运行反编译器。它的 .NET 命令直接读取元数据和 IL,参见 用 REA 做 .NET 程序集静态分析。它和 ILSpy 只有两个接触点:

  • 设置了 REA_ILSPY_CMD_PATH 时,rea doctor 会检查它(必须是绝对路径)。在我的环境里,它报告 ilspycmd ... (ilspycmd: 11.1.0.9782) 状态正常。
  • import-managed-reconstruction(MCP 工具名 import_managed_reconstruction)导入反编译结果,并把它绑定到产出它的那个方法上。decompiler 的 family 字段可选 ilspy、dnspy、dnlib、mono-cecil 或 other。

我在 Demo.dll 上完整跑了一遍:

rea inspect-managed-members /abs/path/Demo.dll --json > members.json
ilspycmd --disable-updatecheck -m 0x06000017 Demo.dll > describe.cs
# import.json = { static_members: <members.json>, decompiler: {name, version, family, options},
#                 methods: [{ token, signature_sha256, normalized_il_sha256, reconstruction: {kind, language, text} }] }
rea import-managed-reconstruction import.json --json

0x06000017 就是 Describe。签名和 IL 哈希取自成员证据里的 signature.raw_sha256 与 body.normalized_il_sha256。REA 接受了这次导入,校验块如下:

{ "matched_static_member": true, "exact_build_required": true,
  "canonical_observation": false, "confidence_floor": "inference" }

附带的限制说明是 “Decompiler output is imported as reconstruction evidence and analyst inference; metadata and IL observations remain canonical.” 我把 IL 哈希改成全零之后,它拒绝导入:Reconstruction method lock 0x06000017 does not match a static managed member observation。这正是它的价值所在:反编译出的 C# 始终被标为推断,也不会被误挂到另一个构建上。

什么时候根本不需要反编译器

很多问题只看元数据就能回答:目标框架、引用、版本号、P/Invoke 导入,或者文件到底是不是 .NET。本站的 .NET 元数据与 IL 查看器 在浏览器里显示这些信息以及每个方法的 IL,文件不上传。它不生成 C#。框架和版本相关的问题见 查看 .NET DLL 的目标框架。

到底选哪个

  • 在 macOS、Linux 上,或者用于 CI 和 agent 工作流: ILSpy 加 ilspycmd。
  • 在 Windows 上,必须单步调试没有源码的代码: 有 Visual Studio 就先用它内置的反编译,否则用 dnSpyEx。
  • 已经在用 ReSharper 或 Rider,平台是 Windows: dotPeek 很顺手,它的符号服务器也能帮到 Visual Studio 调试器。
  • 文件是 NativeAOT: 三个都不行,里面没有 IL,请改用原生工具。

常见问题

dnSpy 还在维护吗? 原版仓库已归档。dnSpyEx 仍在维护,2026 年 6 月发布了 6.6.0。

Mac 上有 dnSpy 吗? dnSpyEx 项目没有。ILSpy 11 可以在 macOS 和 Linux 上原生运行用于反编译,但没有调试器。

这几款工具免费吗? ILSpy(MIT)和 dnSpyEx(GPL-3.0)是开源的;dotPeek 免费但闭源。重新分发任何内容之前,请先看清各自的许可证。

混淆过的程序集能看懂吗? 反编译器只能展示 IL 里有的东西,被改名的符号依然是乱名。REA 的导入限制说明也写明,混淆名称和重构过的控制流不能当作证据。请只分析你拥有或有权检查的代码,并参阅本站 免责声明。

下一步