Введение в MSIL

Что такое MSIL/IL. Ildasm и декомпиляция кода IL

Последнее обновление: 23.05.2022

Что такое MSIL/IL

Для создания приложений на платформе .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, в бинарный/машинный код текущей платформы, который затем собственно и выполняется.

Компиляция программы на C# в код MSIL в .NET

Для работы с кодом IL Microsoft предоставляет ряд утилит, в частности, ildasm и ilasm.

ildasm. Декомпиляция кода IL

Утилита ildasm предназначена для декомпиляции сборки .NET и получения из нее кода IL. Для использования данной утилиты надо установить nuget-пакет Microsoft.NETCore.ILDAsm.

Компиляция программы на C# в код MSIL в .NET и утилита 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.

утилита Ildasm и декомпиляция кода IL

Стоит отметить, что в качестве альтернативы мы также можем установить пакет 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.

построение кода C# в сборку

Например, в моем случае проект называется 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:

декомпиляция программы на C# в код Intermediate Language

В моем случае код 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
графический ildasm на Windows

Правда, она нацелена прежде всего на сборки, скомпилированные с помощью .NET Framework 4.x, но также позволяет просматривать (по крайней мере некоторые) сборки .NET Core/.NET5+

Просмотр сборки .NET в ildasm
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850