ILSpy PowerShell 模块实战指南:用 Get-Decompiler 系列 Cmdlet 在脚本中反编译 .NET 程序集
2026/9/21 19:22:47 网站建设 项目流程

ILSpy PowerShell 模块实战指南:用 Get-Decompiler 系列 Cmdlet 在脚本中反编译 .NET 程序集

【免费下载链接】ILSpy.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!项目地址: https://gitcode.com/gh_mirrors/il/ILSpy

导读

ILSpy 不仅是图形化反编译工具,其核心引擎ICSharpCode.Decompiler也被封装成了 PowerShell 二进制模块,供脚本与自动化流水线直接调用。本文以 ICSharpCode.Decompiler.PowerShell/README.md 为骨架,结合模块源码逐一向你介绍 6 个核心 Cmdlet(Get-DecompilerGet-DecompiledSourceGet-DecompiledTypesGet-DecompiledProjectGet-DecompilerVersionGet-TargetFramework)的参数、行为与底层实现,并给出可复制的端到端示例。读完本文,你将能够在 Windows PowerShell 5.1 与 PowerShell 7+(Windows/macOS)环境中完成"加载程序集 → 枚举类型 → 输出源码 → 生成完整工程"的完整反编译工作流。

模块概览:PowerShell 前端的定位与适用场景

ICSharpCode.Decompiler.PowerShell是 ILSpy 生态中的 PowerShell 前端模块,README 明确其构建参考了 PowerShell 官方仓库的 command-line-simple-example 指南,测试覆盖环境为Windows 上的 PowerShell 5.1,以及 Windows 和 Mac 上的 PowerShell 7+。它没有 GUI,也不依赖 ILSpy 桌面程序,而是直接复用ICSharpCode.Decompiler(C# 反编译引擎)与ICSharpCode.ILSpyX(PDB 解析等辅助设施)的类库能力,让反编译能力可以被 PowerShell 脚本"管道化"地消费。

从 ICSharpCode.Decompiler.PowerShell.csproj 可以看到其工程形态:

  • 目标框架为netstandard2.0,因此模块可被 Windows PowerShell 5.1(.NET Framework)与 PowerShell 7+(.NET Core/.NET 5+)加载;
  • 通过PowerShellStandard.Library包编写二进制 Cmdlet,并显式引入Mono.CecilSystem.Memory统一依赖版本;
  • 直接ProjectReference引用..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj,并以内联Compile方式把ICSharpCode.ILSpyX/PdbProvider下的MonoCecilDebugInfoProvider.csPortableDebugInfoProvider.csDebugInfoUtils.cs三个文件编入本程序集(这正是Get-Decompiler支持自动定位 PDB 的来源);
  • 构建后的 PostBuild 步骤会(Windows 用powershell、非 Windows 用pwsh)把 manifest.psd1 复制到输出目录并重命名为与 DLL 同名,从而形成一个标准 PowerShell 模块目录。

换句话说:模块 = 二进制 Cmdlet(DLL)+ 模块清单(psd1),编译完成后即可被Import-Module直接加载。

模块清单解析:manifest.psd1 导出的命令面

manifest.psd1 是模块的"身份证",关键字段如下:

字段说明
RootModuleICSharpCode.Decompiler.PowerShell.dll二进制模块入口程序集
ModuleVersion8.0.0.0模块版本号(与仓库当前版本线对应)
GUID198b4312-cbe7-417e-81a7-1aaff467ef06模块唯一标识
Author/CompanyNameILSpy Contributors/ic#code作者与厂商信息
DescriptionPowerShell front-end for ILSpy模块用途描述
CmdletsToExport6 个 Get-* Cmdlet(见下表)显式导出的命令清单
FunctionsToExport/AliasesToExport空数组不导出函数与别名

清单显式导出的 Cmdlet 与对应源码文件:

Cmdlet源码文件输出类型
Get-DecompilerGetDecompilerCmdlet.csCSharpDecompiler
Get-DecompiledSourceGetDecompiledSourceCmdlet.csstring
Get-DecompiledTypesGetDecompiledTypesCmdlet.csITypeDefinition[]
Get-DecompiledProjectGetDecompiledProjectCmdlet.csstring
Get-DecompilerVersionGetDecompilerVersion.csstring
Get-TargetFrameworkGetTargetFramework.csstring

核心 Cmdlet 逐一拆解:参数、行为与底层实现

1. Get-Decompiler:创建反编译器实例(一切工作的起点)

源码 GetDecompilerCmdlet.cs 定义的参数:

参数位置必填默认值说明
LiteralPath0待反编译程序集路径,支持PSPath别名,ValidateNotNullOrEmpty校验非空
LanguageVersionLatest反编译器使用的 C# 语言版本
RemoveDeadStores$false是否移除死存储(dead stores)
RemoveDeadCode$false是否移除死代码(dead code)
PDBFilePath$null指定 PDB 文件路径;不传时自动探测

其内部实现值得注意的调用链:

  1. PEFile打开目标程序集(PEStreamOptions.Default,只读打开);
  2. 调用DebugInfoUtils.FromFile(module, PDBFilePath)尝试从程序集旁/指定位置解析调试符号(该工具类来自ICSharpCode.ILSpyX/PdbProvider,同时支持 Mono 与 Portable PDB 两种格式);
  3. 构造CSharpDecompiler,并把DecompilerSettings的关键开关写死为:ThrowOnAssemblyResolveErrors = false(程序集解析失败不抛异常,便于批处理容错)、UseDebugSymbolsShowDebugInfo跟随 PDB 是否存在而联动开启;
  4. 将解析出的debugInfo赋给decompiler.DebugInfoProvider

因此,只要把程序集路径传给Get-Decompiler,返回的CSharpDecompiler对象就同时携带了"源码反编译能力"与(若能找到的)"调试符号信息",可直接作为后续三个 Cmdlet 的管道输入。

2. Get-DecompiledTypes:按类型种类筛选枚举程序集内的类型

源码 GetDecompiledTypesCmdlet.cs 要求两个参数:

  • Decompiler(位置 0,必填):Get-Decompiler产出的CSharpDecompiler
  • Types(必填,字符串数组):要筛选的类型种类。

Types参数由 TypesParser.cs 解析,支持两种写法:

  • 完整关键字classstructinterfaceenumdelegate(不区分大小写,且允许前缀缩写,如cla可匹配class);
  • 字母缩写组合:当只传入一个不以任何关键字开头的值时,按字母解释——c=class、i=interface、s=struct、d=delegate、e=enum,例如-Types cise等价于同时选择 class、interface、struct、enum。

实现上,Cmdlet 遍历Decompiler.TypeSystem.MainModule.TypeDefinitions,只保留TypeKind命中筛选集合的类型,并以ITypeDefinition[]数组写出,方便后续用ForEach-ObjectSelect-Object继续处理(例如读取FullNameFullTypeName等元数据)。

3. Get-DecompiledSource:输出 C# 源码文本

源码 GetDecompiledSourceCmdlet.cs:

参数位置必填默认值说明
Decompiler0CSharpDecompiler实例
TypeNamestring.Empty要反编译的类型的完整名称

行为分支:

  • 不传TypeName:调用Decompiler.DecompileWholeModuleAsString(),把整个程序集的所有类型反编译为一段 C# 源码字符串;
  • TypeName:用FullTypeName解析该名称,调用Decompiler.DecompileTypeAsString(name),仅反编译指定类型。

两种情况下,源码都会以string通过WriteObject输出到管道,可直接重定向到文件:Get-DecompiledSource $d | Set-Content out.cs。反编译过程中产生的成员级异常会被 DecompilationErrorReporting.cs 以"每个成员一条非终止错误"的方式上报(错误 ID 为DecompilationFailed,即ErrorIds.DecompilationFailed = "2"),同时源码照常输出——这样脚本不会把"已知反编译失败"误当作"干净成功"。

4. Get-DecompiledProject:一键生成完整 .NET 工程

源码 GetDecompiledProjectCmdlet.cs 是全模块中"最重"的一个 Cmdlet:

参数位置必填说明
Decompiler0CSharpDecompiler实例
LiteralPath1输出目录,支持PSPathOutputPath两个别名

关键行为与实现细节:

  1. 输出目录必须预先存在:源码先校验Directory.Exists(path),否则直接输出提示字符串Destination directory must exist prior to decompilation并返回——这是使用上最容易踩的坑,务必先New-Item -ItemType Directory
  2. 底层调用WholeProjectDecompiler:从Decompiler.TypeSystem.MainModule.MetadataFile取出元数据,构造UniversalAssemblyResolverfalse表示不启用多目标框架解析,目标框架 ID 通过DetectTargetFrameworkId()探测),再执行decompiler.DecompileProject(module, path),生成.csproj.sln与全部源码文件;
  3. 进度报告:Cmdlet 实现了IProgress<DecompilationProgress>接口,通过后台Task执行反编译、主线程轮询进度,并调用WriteProgress输出ProgressRecord(包含已完成单元数、总单元数与百分比),反编译结束时写一条Completed记录;
  4. 结束后通过decompiler.Errors收集DecompilerException列表,逐个以非终止错误上报。

5. Get-DecompilerVersion:输出引擎版本

源码 GetDecompilerVersion.cs 实现极简:读取FullTypeName类型所在程序集(即ICSharpCode.Decompiler.dll)的版本号并以字符串输出。适合在脚本开头做版本断言或写入日志。

6. Get-TargetFramework:探测目标框架标识

源码 GetTargetFramework.cs 接受一个必填的Decompiler参数,从Decompiler.TypeSystem.MainModule.MetadataFile调用module.Metadata.DetectTargetFrameworkId(),返回该程序集的 Target Framework 标识(TFM,形如.NETCoreApp,Version=v6.0)。结合Get-DecompiledProject使用,可在生成工程前判断目标程序集的框架形态。

完整工作流示例:从 DLL 到源码与工程

README 给出了官方示例脚本 Demo.ps1,它展示了完整的端到端用法。核心流程整理如下(命令已按可复制的形式给出):

# 1. 加载模块(假设已按下文"构建与加载"一节编译) $modulePath = "$PSScriptRoot\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.PowerShell.dll" Import-Module $modulePath # 2. 输出引擎版本 $version = Get-DecompilerVersion Write-Output $version # 3. 创建反编译器实例 $asm = "$PSScriptRoot\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.PowerShell.dll" $decompiler = Get-Decompiler $asm # 4. 枚举程序集中的 class 类型并打印全名 $classes = Get-DecompiledTypes $decompiler -Types class $classes.Count foreach ($c in $classes) { Write-Output $c.FullName } # 5. 反编译单个类型的源码 Get-DecompiledSource $decompiler -TypeName ICSharpCode.Decompiler.PowerShell.GetDecompilerCmdlet # 6. 反编译整个工程(输出目录需先创建) Get-DecompiledProject $decompiler -OutputPath .\decomptest

要点解读:

  • 第 3~4 步构成"反编译器 → 类型筛选"管道,Types参数可换成class,struct,interface,enum,delegate任意组合,或缩写串cise
  • 第 5 步-TypeName必须使用类型的完整名称(含命名空间,如ICSharpCode.Decompiler.PowerShell.GetDecompilerCmdlet);省略该参数则输出整个模块源码;
  • 第 6 步的.\decomptest目录必须先存在,否则 Cmdlet 只输出提示字符串而不执行反编译。

若要反编译全部类型而非仅 class,可将第 4 步替换为:

Get-DecompiledTypes $decompiler -Types class,struct,interface,enum,delegate | ForEach-Object { $_.FullName }

构建与加载:三步跑通模块

模块没有发布到 PowerShell Gallery(README 中明确列为待办事项),因此目前的标准用法是从源码构建后本地加载:

  1. 编译模块:在仓库根目录用dotnet build ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj(或dotnet build ILSpy.sln)构建。构建产物位于bin/Debug/netstandard2.0/,PostBuild 步骤会自动把manifest.psd1复制到该目录;
  2. 确认模块目录结构:输出目录应同时包含ICSharpCode.Decompiler.PowerShell.dllICSharpCode.Decompiler.PowerShell.psd1,以及依赖的ICSharpCode.Decompiler.dll等(csproj 设置了CopyLocalLockFileAssemblies=true);
  3. 加载与验证Import-Module <输出目录>\ICSharpCode.Decompiler.PowerShell.dll后执行Get-Command -Module ICSharpCode.Decompiler.PowerShell,应能看到 6 个Get-*命令。

注意两点前置条件:模块目标为netstandard2.0,Windows 上需 PowerShell 5.1 或 7+,macOS 上需 PowerShell 7+(与 README 的测试声明一致);另需本机具备 .NET SDK 才能完成编译。

错误处理与容错设计

模块在两个环节有显式的容错设计,值得在脚本中配合使用:

  • 程序集加载失败Get-Decompiler):写入ErrorRecord,错误 ID 为ErrorIds.AssemblyLoadFailed(即"1"),类别OperationStopped,并WriteVerbose输出完整异常——可在脚本中通过$Error[0].FullyQualifiedErrorId判断;
  • 反编译过程中的成员级失败:以非终止错误逐个上报(错误 IDDecompilationFailed,即"2"),源码仍照常产出,脚本可用-ErrorAction SilentlyContinue选择性忽略后继续收集输出;
  • Get-DecompiledProject若输出目录不存在,返回的是普通字符串而非异常,脚本应显式判断返回值(或先自行创建目录)。

已知限制与后续方向

README 中明确标注的唯一未完成事项是发布到 PowerShell Gallery(当前仓库版本尚未提供Publish-Module产物),因此使用者目前只能走"源码构建 + 本地Import-Module"的路径;同时 README 附带的开发参考(官方简单 Cmdlet 编写指南、Cmdlet 动词白名单、二进制模块测试方法等)可供扩展模块时对照遵循。若需要为模块补充新的 Cmdlet,可参考 Demo.ps1 中的双 netstandard 程序集测试方式,并在 manifest.psd1 的CmdletsToExport中登记新命令名。

【免费下载链接】ILSpy.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!项目地址: https://gitcode.com/gh_mirrors/il/ILSpy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询