ILSpyは.NETのアセンブリブラウザおよび逆コンパイラ。 C#やF#などの言語で書かれたアプリケーションのDLLを解析してIL (.NETの中間表現)やC#のコードに逆コンパイルすることができる。開発時のデバッグやリバースエンジニアリングなどに使われる。
- Arch Linux x64
インストールには、.NET Runtime 6.0および.NET SDKが必要。
sudo pacman -S dotnet-runtime-6.0 dotnet-sdk
dotnet tool install -g ilspycmd
$ ilspycmd --help
ilspycmd: 8.2.0.7535
ICSharpCode.Decompiler: 8.2.0.7535
dotnet tool for decompiling .NET assemblies and generating portable PDBs
Usage: ilspycmd [options] <Assembly file name(s)>
Arguments:
Assembly file name(s) The list of assemblies that is being
decompiled. This argument is mandatory.
Options:
-v|--version Show version of ICSharpCode.Decompiler used.
-h|--help Show help information.
-o|--outputdir <directory> The output directory, if omitted decompiler
output is written to standard out.
-p|--project Decompile assembly as compilable project.
This requires the output directory option.
-t|--type <type-name> The fully qualified name of the type to
decompile.
-il|--ilcode Show IL code.
--il-sequence-points Show IL with sequence points. Implies -il.
-genpdb|--generate-pdb Generate PDB.
-usepdb|--use-varnames-from-pdb Use variable names from PDB.
-l|--list <entity-type(s)> Lists all entities of the specified type(s).
Valid types: c(lass), i(nterface), s(truct),
d(elegate), e(num)
-lv|--languageversion <version> C# Language version: CSharp1, CSharp2,
CSharp3, CSharp4, CSharp5, CSharp6, CSharp7,
CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0,
CSharp9_0, CSharp10_0, Preview or Latest
Allowed values are: CSharp1, CSharp2,
CSharp3, CSharp4, CSharp5, CSharp6, CSharp7,
CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0,
CSharp9_0, CSharp10_0, CSharp11_0, Preview,
Latest.
Default value is: Latest.
-r|--referencepath <path> Path to a directory containing dependencies
of the assembly that is being decompiled.
--no-dead-code Remove dead code.
--no-dead-stores Remove dead stores.
-d|--dump-package Dump package assemblies into a folder. This
requires the output directory option.
--nested-directories Use nested directories for namespaces.
--disable-updatecheck If using ilspycmd in a tight loop or fully
automated scenario, you might want to disable
the automatic update check.
Remarks:
-o is valid with every option and required when using -p.
Examples:
Decompile assembly to console out.
ilspycmd sample.dll
Decompile assembly to destination directory (single C# file).
ilspycmd -o c:\decompiled sample.dll
Decompile assembly to destination directory, create a project file, one source file per type.
ilspycmd -p -o c:\decompiled sample.dll
Decompile assembly to destination directory, create a project file, one source file per type,
into nicely nested directories.
ilspycmd --nested-directories -p -o c:\decompiled sample.dll
試しにF#のHelloWorldを逆コンパイルしてみる。
$ cd "$(mktemp -d)"
$ dotent new condole -lang=F#
$ ls
bin/ obj/ Program.fs tmp.qVus8o47GS.fsproj
$ dotnet publish
MSBuild のバージョン 17.8.5+b5265ef37 (.NET)
Determining projects to restore...
All projects are up-to-date for restore.
tmp.qVus8o47GS -> /tmp/tmp.qVus8o47GS/bin/Release/net8.0/tmp.qVus8o47GS.dll
tmp.qVus8o47GS -> /tmp/tmp.qVus8o47GS/bin/Release/net8.0/publish/
基本的な使い方としてはDLLファイル(.NETアセンブリ)のパスを引数に指定して起動すればよい。
$ ilspycmd ./bin/Release/net8.0/tmp.qVus8o47GS.dll
実行するとこのようなC#のソースが出力される。
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using <StartupCode$tmp-qVus8o47GS>;
using Microsoft.FSharp.Core;
[assembly: FSharpInterfaceDataVersion(2, 0, 0)]
[assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
[assembly: AssemblyCompany("tmp.qVus8o47GS")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("tmp.qVus8o47GS")]
[assembly: AssemblyTitle("tmp.qVus8o47GS")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default)]
[assembly: AssemblyVersion("1.0.0.0")]
[CompilationMapping(SourceConstructFlags.Module)]
public static class Program
{
[CompilationMapping(SourceConstructFlags.Value)]
internal static PrintfFormat<Unit, TextWriter, Unit, Unit> format@1 => $Program.format@1;
}
namespace <StartupCode$tmp-qVus8o47GS>
{
internal static class $Program
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal static PrintfFormat<Unit, TextWriter, Unit, Unit> format@1;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal static int init@;
public static void main@()
{
format@1 = new PrintfFormat<Unit, TextWriter, Unit, Unit, Unit>("Hello from F#");
PrintfModule.PrintFormatLineToTextWriter(Console.Out, Program.format@1);
}
}
}
namespace <StartupCode$tmp-qVus8o47GS>.$Tmp.qVus8o47GS
{
internal static class AssemblyInfo
{
}
}
namespace <StartupCode$tmp-qVus8o47GS>.$.NETCoreApp,Version=v8.0
{
internal static class AssemblyAttributes
{
}
}
また、-il
オプションを付けるとC#ではなくILを出力させることができる。
$ ilspycmd -il ./bin/Release/net8.0/tmp.qVus8o47GS.dll
// IL code: tmp.qVus8o47GS
.class private auto ansi '<Module>'
extends [System.Runtime]System.Object
{
} // end of class <Module>
.class public auto ansi abstract sealed Program
extends [System.Runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = (
01 00 07 00 00 00 00 00
)
// Methods
.method assembly specialname static
class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> get_format@1 () cil managed
{
// Method begins at RVA 0x2050
// Header size: 1
// Code size: 6 (0x6)
.maxstack 8
IL_0000: ldsfld class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> '<StartupCode$tmp-qVus8o47GS>.$Program'::format@1
IL_0005: ret
} // end of method Program::get_format@1
// Properties
.property class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> format@1()
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = (
01 00 09 00 00 00 00 00
)
.get class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> Program::get_format@1()
}
} // end of class Program
.class private auto ansi abstract sealed '<StartupCode$tmp-qVus8o47GS>.$Program'
extends [System.Runtime]System.Object
{
// Fields
.field assembly static class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> format@1
.custom instance void [System.Runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggerBrowsableState) = (
01 00 00 00 00 00 00 00
)
.field assembly static int32 init@
.custom instance void [System.Runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggerBrowsableState) = (
01 00 00 00 00 00 00 00
)
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
.custom instance void [System.Runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public static
void main@ () cil managed
{
// Method begins at RVA 0x2058
// Header size: 1
// Code size: 32 (0x20)
.maxstack 8
.entrypoint
IL_0000: ldstr "Hello from F#"
IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit>::.ctor(string)
IL_000a: stsfld class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> '<StartupCode$tmp-qVus8o47GS>.$Program'::format@1
IL_000f: call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()
IL_0014: call class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit> Program::get_format@1()
IL_0019: call !!0 [FSharp.Core]Microsoft.FSharp.Core.PrintfModule::PrintFormatLineToTextWriter<class [FSharp.Core]Microsoft.FSharp.Core.Unit>(class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0, class [System.Runtime]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
IL_001e: pop
IL_001f: ret
} // end of method $Program::main@
} // end of class <StartupCode$tmp-qVus8o47GS>.$Program
.class private auto ansi abstract sealed '<StartupCode$tmp-qVus8o47GS>.$Tmp.qVus8o47GS.AssemblyInfo'
extends [System.Runtime]System.Object
{
} // end of class <StartupCode$tmp-qVus8o47GS>.$Tmp.qVus8o47GS.AssemblyInfo
.class private auto ansi abstract sealed '<StartupCode$tmp-qVus8o47GS>.$.NETCoreApp,Version=v8.0.AssemblyAttributes'
extends [System.Runtime]System.Object
{
} // end of class <StartupCode$tmp-qVus8o47GS>.$.NETCoreApp,Version=v8.0.AssemblyAttributes
便利!
Avalonia UIというクロスプラットフォームのGUIツールキットを使用したAvaloniaILSpyというGUIフロントエンドがある。
インストールするには、リリースページからアセットをダウンロードし、ILSpy
を起動する。
curl -sLO 'https://github.com/icsharpcode/AvaloniaILSpy/releases/download/v7.2-rc/Linux.x64.Release.zip'
7z x Linux.x64.Release.zip
7z x ILSpy-linux-x64-Release.zip
chmod a+x ./artifacts/linux-x64/ILSpy
./artifacts/linux-x64/ILSpy
System.InvalidOperationException: Default font family name can't be null or empty. Trhown when executing
というエラーが出て起動できないことがある。
DejaVuフォントをインストールしてLANGを英語に設定してからILSpyの実行ファイルを叩いたら起動できた。
sudo pacman -S ttf-dejavu
LANG=en_US.UTF8 ./artifacts/linux-x64/ILSpy