Для создания приложений на платформе .NET можно использовать различные языки программирования - C#, VB.NET, F#, другие языки, которые имеют неофициальную поддержку .NET (типа Deflfi .NET и т.д.). При компиляции кода на любом из этих языков создается сборка - это может быть выполняемое приложение, либо библиотека классов, которая используется в других приложениях. Однако вне зависимости от того, какой язык используется, скомпилированная сборка содержит код в некоторой промежуточной форме, независимой от исходного языка программирования, целевой машины и ее операционной системы. Благодаря этому проекты на одном .NET-языке могут использовать сборки, написанные на другом .NET-совместимом языке программирования. При запуске приложения и соотвественно запуске файла сборки общеязыковая среда выполнения CLR загружает ее и преобразует ее код в машинный код для последующего выполнения.
Промежуточное представление приложений .NET, предназначенное для общеязыковой среды выполнения CLR. среды, включает в себя два основных компонента: метаданные и управляемый код (managed code). Метаданные — это система дескрипторов всех компонентов приложения (классов, структур и их элементов и т.д.) и их отношения. Управляемый код представляет функциональность методов приложения, закодированных в специальной бинарной форме, которая называется MSIL или Microsoft intermediate language (другие названия - Common Intermediate Language (CIL) или просто Intermediate Language (IL). Затем уже при выполнении сборки JIT-компилятор компилирует методы, закодированные в MSIL, в бинарный/машинный код текущей платформы, который затем собственно и выполняется.
Для работы с кодом IL Microsoft предоставляет ряд утилит, в частности, ildasm и ilasm.
Утилита ildasm предназначена для декомпиляции сборки .NET и получения из нее кода IL. Для использования данной утилиты надо установить nuget-пакет Microsoft.NETCore.ILDAsm.
Стоит отметить, что пакет устанавливается глобально в кэш nuget, поэтому для других проектов не надо повторно его устанавливать. Например, на Windows для версии .NET 6.0.0 это каталог
C:\Users\[имя_пользователя]\.nuget\packages\microsoft.netcore.ildasm\6.0.0\runtimes\native
Например, в моем случае это каталог C:\Users\eugen\.nuget\packages\microsoft.netcore.ildasm\6.0.0\runtimes\native. И в этом каталоге можно найти саму утилиту ildasm.
Стоит отметить, что в качестве альтернативы мы также можем установить пакет nuget Ildasm для конкретной платформы. Нужный пакет называется по шаблону runtime.[платформа].Microsoft.NETCore.ILDAsm
. Например, некоторые пакеты:
runtime.win-x64.Microsoft.NETCore.ILDAsm
runtime.osx-x64.Microsoft.NETCore.ILDAsm
runtime.linux-x64.Microsoft.NETCore.ILDAsm
Рассмотрим, как использовать эту утилиту. Для начала скомпилируем какую-нибудь программу на C#. Пусть это будет следующая простейшая программа на C# 10:
Console.WriteLine("Hello, World!");
После построения проекта в папке проекта в каталоге bin\Debug\net6.0\
мы найдем скомпилированный файл сборки, который называется по имени проекта и имеет
расширение dll.
Например, в моем случае проект называется MSILApp, соотвественно файл сборки называется MSILApp.dll, а полный путь к файлу сборки будет C:\Users\eugen\source\repos\csharp\Console\MSILApp\MSILApp\bin\Debug\net6.0
Теперь декомпилируем полученную сборку. Для этого передадим утилите ildasm путь к файлу сборки в виде
ildasm путь_к_сборке
В этом случае код IL сборки будет выводиться на консоль. Но мы также можем вывести его в файл. Для этого утилите ildasm нужно передать параметр /out, который указывает на путь к генерируемому файлу.
ildasm путь_к_сборке /out=файл_с_кодом.il
Итак, откроем командную строку/терминал и сначала с помощью команды cd перейдем к каталогу, где располагается файл сборки. И затем передадим утилите ildasm файл сборки и укажем имя выходного файла:
C:\Users\eugen>cd C:\Users\eugen\source\repos\csharp\Console\MSILApp\MSILApp\bin\Debug\net6.0 C:\Users\eugen\source\repos\csharp\Console\MSILApp\MSILApp\bin\Debug\net6.0>C:\Users\eugen\.nuget\packages\microsoft.netcore.ildasm\6.0.0\runtimes\native\ildasm MSILApp.dll /out=msilapp.il
И после выполнения команды в папке, где располагается файл сборки, также появится файл со сгенерированным кодом IL:
В моем случае код IL будет выглядеть следующим образом:
// Microsoft (R) .NET IL Disassembler. Version 6.0.0 // Metadata version: v4.0.30319 .assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 6:0:0:0 } .assembly extern System.Console { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 6:0:0:0 } .assembly MSILApp { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 18 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V 65 72 73 69 6F 6E 3D 76 36 2E 30 01 00 54 0E 14 // ersion=v6.0..T.. 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79 // FrameworkDisplay 4E 61 6D 65 00 ) // Name. .custom instance void [System.Runtime]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 07 4D 53 49 4C 41 70 70 00 00 ) // ...MSILApp.. .custom instance void [System.Runtime]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 05 44 65 62 75 67 00 00 ) // ...Debug.. .custom instance void [System.Runtime]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0.. .custom instance void [System.Runtime]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 05 31 2E 30 2E 30 00 00 ) // ...1.0.0.. .custom instance void [System.Runtime]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 07 4D 53 49 4C 41 70 70 00 00 ) // ...MSILApp.. .custom instance void [System.Runtime]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 07 4D 53 49 4C 41 70 70 00 00 ) // ...MSILApp.. .hash algorithm 0x00008004 .ver 1:0:0:0 } .module MSILApp.dll // MVID: {7346FB26-485E-4860-8B19-1AC519288D2E} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x00000211BEA40000 // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit Program extends [System.Runtime]System.Object { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .method private hidebysig static void '<Main>$'(string[] args) cil managed { .entrypoint // Code size 12 (0xc) .maxstack 8 IL_0000: ldstr "Hello, World!" IL_0005: call void [System.Console]System.Console::WriteLine(string) IL_000a: nop IL_000b: ret } // end of method Program::'<Main>$' .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Program::.ctor } // end of class Program // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** // WARNING: Created Win32 resource file msilapp.res
То есть это тот код, в который компилируется простейшая программа на C#, выводящая на строку "Hello World"
Стоит отметить, что на Windows при установке Visual Studio и .NET 4.X также устанвливается графическая утилита ildasm, которая в отличие от консольной утилиты позволяет графически посмотреть код IL сборки. В системе она расположена по пути
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools
Правда, она нацелена прежде всего на сборки, скомпилированные с помощью .NET Framework 4.x, но также позволяет просматривать (по крайней мере некоторые) сборки .NET Core/.NET5+