Język pośredni (IL, Intermediate Language) to niskopoziomowy język programowania będący pośrednią wersją między kodem źródłowym a kodem maszynowym. Po skompilowaniu kodu napisanego w języku wysokiego poziomu .NET (np. C#, Visual Basic, F#) otrzymujemy plik binarny zawierający instrukcje IL oraz metadane. Kod IL jest niezależny od procesora i systemu operacyjnego, ale wymaga środowiska .NET (CLR, Common Language Runtime) do uruchomienia. Podczas wykonania CLR kompiluje IL do kodu natywnego, który następnie wykonuje procesor [1, 2].
Analiza kodu IL
Analiza kodu pośredniego może dać głębszy wgląd w działanie aplikacji .NET. Kod IL jest czytelny, a jego analiza nie jest skomplikowana. Dysponując samym plikiem wykonywalnym .NET można zajrzeć do jego środka i zobaczyć definicje klas, metod, pola oraz manifest assembly, nawet bez dostępu do pierwotnego kodu źródłowego [3]. Podstawowym narzędziem służącym do analizy i dostarczonym przez Microsoft podczas instalacji Visual Studio jest Ildasm.exe [4]. Aby uruchomić program w menu start należy wpisać Developer Command Prompt for VS 2022. A następnie w konsoli ildasm.

Za jego pomocą można zobaczyć jak przekształcony zostanie poniższy kod aplikacji konsolowej napisanej w C# na język pośredni.
namespace IldasmAnalysis
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}

Poniżej znajduje się dokładna analiza kodu IL na podstawie oficjalnej dokumentacji, do której należy zajrzeć podczas analizy bardziej skomplikowanych przypadków [5]:
.method private hidebysig static void Main(string[] args) cil managed
.method — zaczyna definicję metody.
private — metoda jest widoczna tylko w obrębie zestawu/typu (tu: wewnętrzna dla klasy).
hidebysig — przeciążenia są rozróżniane po sygnaturze (typach parametrów).
static — metoda nie wymaga instancji (wywoływana na typie).
void Main(string[] args) — podpis metody.
cil managed — ciało jest w IL, a kod wykonuje CLR (zarządzany).
{
.entrypoint
.entrypoint — to punkt startowy programu (metoda, od której zaczyna się wykonanie).
.custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 )
Atrybut kompilatora: NullableContextAttribute. Informuje narzędzia/kompilator o kontekście typów nullowalnych w metodzie.
// Code size 13 (0xd)
Rozmiar ciała metody: 13 bajtów (0x0d w hex).
.maxstack 8
Maksymalna głębokość stosu ewaluacyjnego potrzebna do wykonania metody.
IL_0000: nop
nop — wypełnia miejsce, jeśli kody operacyjne są poprawiane. Nie jest wykonywana żadna znacząca operacja, chociaż można użyć cyklu przetwarzania.
IL_0001: ldstr "Hello, World!"
ldstr — wypycha nowe odwołanie do obiektu do literału ciągu przechowywanego w metadanych.
Stos po tej instrukcji: top → „Hello, World!”.
IL_0006: call void [System.Console]System.Console::WriteLine(string)
call — wywołuje metodę wskazaną przez deskryptor metody przekazywanej.
Stos przed: top → „Hello, World!”
Stos po: pusty (WriteLine nic nie zwraca — void).
IL_000b: nop
nop — wypełnia miejsce, jeśli kody operacyjne są poprawiane. Nie jest wykonywana żadna znacząca operacja, chociaż można użyć cyklu przetwarzania.
IL_000c: ret
ret — zwraca wartość z bieżącej metody, wypychając wartość zwracaną (jeśli istnieje) ze stosu oceny wywoływanego do stosu oceny obiektu wywołującego.
} // end of method Program::Main
Koniec definicji metody.
Źródła:
[1] https://pvs-studio.com/en/blog/terms/7006/
[2] https://learn.microsoft.com/en-us/dotnet/standard/managed-code
[3] https://www.spyshelter.com/exe/net-ildasm-exe/
[4] https://learn.microsoft.com/en-us/dotnet/framework/tools/ildasm-exe-il-disassembler
[5] https://learn.microsoft.com/pl-pl/dotnet/api/system.reflection.emit.opcodes?view=net-8.0