.NET 程序集本身是一个 PE 文件,和任何 Windows 的 .exe、.dll 用的是同一种容器格式,但它携带的结构信息比原生二进制丰富得多。里面有一个元数据数据库:每个类型、方法、字段都有名字;依赖了哪些其他程序集、哪些原生库,都有记录;每个方法体以 CIL(通用中间语言)指令的形式保存,而这些指令引用的仍然是那些名字。所以托管程序集的大部分内容,不用运行、也不用反编译器,就能看明白。
这个工具在你的浏览器里读取这些结构。选择一个 .dll 或 .exe,你会看到程序集身份、引用列表、可以逐层展开的类型树、P/Invoke 声明和资源;点击某个方法,页面就反汇编它的 IL。文件自始至终不会上传。
能做什么
输入。 .NET 程序集(.dll 或 .exe),最大 256 MiB,.NET Framework、.NET Core、.NET 5 及以后版本、.NET Standard 都可以。每次请求(生成报告,或者反汇编一个方法的 IL)超时时间 60 秒。
报告。 页面会展示:
- 程序集身份:名称、版本、culture 和公钥标记(public key token,按运行时的算法从公钥计算得出)。目标框架取自程序集级别的
TargetFrameworkAttribute,例如.NETCoreApp,Version=v8.0。模块名和 MVID 也会列出。 - 引用:每个被引用的程序集及其版本、culture、公钥标记,以及
ModuleRef表里点名的原生模块。 - 类型树:先按命名空间,再按类型,嵌套类型挂在外层类型下面。每个类型显示种类(class、interface、struct、enum、delegate 或 static class)、可见性、是否
abstract、sealed,以及基类。每个字段显示类型、可见性,以及是否 static、const、readonly。每个方法显示 C# 风格的签名,例如int Parse(string text, ref int position),还有可见性、是否 static、virtual、abstract、P/Invoke,以及元数据 token 和 RVA。 - P/Invoke:每个
DllImport声明,包括托管方法、原生 DLL、入口点名称、字符集、调用约定和SetLastError。这是托管代码和原生代码的边界,往往是整份报告里最值得看的一张表。 - 资源:清单资源(manifest resource)及其可见性。嵌入式资源显示大小和文件偏移,以
.resources魔数0xBEEFCACE开头的会单独标出;链接式资源显示存放它的文件或程序集。 - PE 与 CLI 头:机器类型(x86、x64、ARM64 等,也能识别 ReadyToRun 映像在 Linux、macOS 上使用的带操作系统标记的值)、PE32 还是 PE32+、DLL 还是可执行文件、节表、CLI 运行时版本、CLI 标志(
ILONLY、32BITREQUIRED、STRONGNAMESIGNED等)和入口点 token。 - 元数据布局:元数据版本字符串(例如
v4.0.30319)、各个流(stream),以及每张元数据表的行数。 - 映像标记:映像里在 IL 之外还带有预编译的原生代码时,标为 ReadyToRun;
ILONLY标志未设置时(例如含原生代码的 C++/CLI 程序集),标为混合模式。 - 警告:某一行或某个签名解不出来时,记为一条警告(最多 200 条),而不是让整个分析失败。
按需查看 IL。 点击一个方法就反汇编它的方法体。你会看到方法头格式(tiny 或 fat)、maxStack、局部变量是否零初始化、代码长度、局部变量的类型,以及带 IL_xxxx 偏移的指令清单。操作数会被解析成可读的形式:调用和字段访问显示成员名,ldstr 显示字符串字面量,分支和 switch 显示跳转目标标签。异常处理子句(带捕获类型的 catch、filter、finally、fault)连同 try 区间和 handler 区间一起列出。没有 IL 的方法会说明原因:abstract、P/Invoke、由运行时实现,或者是原生代码。
原理
解析器遵循定义 .NET 元数据的 ECMA-335 标准。它读取 PE 头,在第 14 个数据目录里找到 CLI 头,通过节表把 RVA 映射到文件偏移,然后打开元数据根(BSJB 签名)。接着读取表流(#~,或者未压缩的 #-)和各个堆:#Strings 存名字,#US 存字符串字面量,#GUID 和 #Blob 存签名和特性参数。类型名和签名渲染成类似 C# 的文本,泛型参数使用声明时的名字。
PE 读取、元数据布局、签名语法、操作码表和方法体解析,移植自 REA 6.4.0(morluto 的开源项目,MIT 许可),并且用 REA 6.4.0 在同一批样例程序集上的输出做了对照测试。
IL 部分:页面读取方法 RVA 处的方法头,用完整的 CIL 操作码表逐条解码指令,把每个元数据 token 解析成可读的名字,再解析 fat 方法体后面的异常处理段。只有你点击的那个方法才会被解码,所以大程序集不需要一开始就整体反汇编。
如果文件没有 CLI 头,页面会检查 NativeAOT 的特征(名为 .managed 或 hydrated 的节,或者文件中任意位置出现的 DotNetRuntimeDebugHeader 字符串),然后告诉你这是 NativeAOT 编译的程序,还是普通的原生 PE。
隐私是怎么保证的
程序集不会被上传:
- 分析和每一次 IL 请求都在你设备上的 Web Worker 里进行。Worker 通过浏览器的文件 API 读取文件,只把报告或指令清单发回页面。
- 统计事件只有粗粒度信息:成功或错误码、类型数量区间(例如
11-100)、文件大小区间、耗时区间,绝不包含文件名、类型名、方法名或任何其他内容。完整清单见隐私政策。 - 工具页不展示广告。
如何自己验证
- 在选择文件之前,打开浏览器开发者工具,切到 Network(网络) 面板。分析一个程序集,再打开几个方法。出现的请求只有页面资源和 worker 脚本
/_astro/dotnet-worker-*.js,没有任何一条带着你的文件。 - 或者先打开本页,然后断开网络(关闭 Wi-Fi,或在 Network 面板勾选 Offline),再分析文件,照样能完成。
- Worker 运行在
default-src 'none'; script-src 'self'; connect-src 'none'这条内容安全策略之下,浏览器根本不允许它发出网络请求。
局限
- 不反编译成 C#。 工具展示的是元数据和 IL,不会还原 C# 或任何其他源码。签名写成 C# 的样子,只是为了好读。
- NativeAOT 和原生 PE 没有 IL。 NativeAOT 把所有代码编译成机器码,不保留 ECMA-335 元数据,所以这里没有东西可看。请改用 Ghidra 这类原生反汇编或反编译工具。
- 不展示 ReadyToRun 的原生代码。 对 ReadyToRun 映像,页面展示 IL 和元数据,不展示旁边那份预编译的机器码。混合模式程序集同样只展示托管部分。
- 单文件发布要先解包。 单文件发布的应用最外层是一个原生宿主程序,没有 CLI 头。请分析解包出来的程序集。
- 混淆过的名字仍然是混淆的。 名字按原样显示,超过 1024 个字符会被截断。
- 不验证签名。
STRONGNAMESIGNED只是头部的一个标志,页面不验证强名称签名,也不验证 Authenticode 签名。 - 单个方法的上限。 每个方法最多解码 1 MiB 的 IL、20 万条指令。遇到未知操作码或操作数越界时停止解码,并给出说明。
- 只做静态分析。 不执行任何代码,所以反射、动态加载、运行时生成的代码都看不到。
常见问题
这算在线版 ILDasm 吗?
覆盖的范围相近:元数据表、类型和成员列表、带 token 解析的 IL 清单。但输出格式是本页自己的,不是可以重新汇编的 .il 文件。
页面提示 “NativeAOT”,怎么办?
这个文件里只有机器码,请用原生分析工具打开。REA + Ghidra 免费分析原生二进制这篇教程讲了完整流程。
为什么某个方法显示没有 IL?
它是 abstract 方法、P/Invoke 声明、由运行时实现的方法(例如委托的 Invoke),或者是混合模式程序集里的原生代码。
能看到字符串常量吗?
能,在 IL 清单里:ldstr 指令会显示字面量,特殊字符经过转义,超过 4096 个字符会被截断。
为什么公钥标记是空的?
这个程序集或这条引用没有使用强名称。
合规说明
请只分析你自己构建的程序集、你获得授权检查的程序集,或者开源程序集。请遵守所分析软件的许可协议(包括其中限制逆向工程的条款)以及适用于你的法律法规。本工具不提供任何绕过授权或其他保护措施的建议。另见免责声明。
相关教程
- 查看 .NET DLL 的目标框架、版本号和依赖:读出目标框架、程序集版本、文件版本和引用,包括没有目标框架属性的 DLL。
- 用 REA 静态分析 .NET 程序集:元数据、CIL 与成员对比:在命令行做同样的元数据和 CIL 检查,并对比不同版本之间的成员变化。
- 用 REA 逆向分析 Electron 应用:ASAR、preload 与 IPC 全流程:桌面应用如果是 Electron 而不是 .NET 写的,看这篇;也可以用 ASAR 与 Electron IPC 分析器。