MSIL (MicroSoft Intermediate Language) is the bytecode language of Microsoft's .NET environment. Language code from any .NET language is converted to MSIL, stored in executable files and later compiled to native machine code by the platform's JIT compiler at runtime. MSIL has a high level assembly language style syntax, allowing code and the contents of assemblies to be human-readable, and even human-writable (in exceptional circumstances).

Understanding MSIL

Without a shadow of a doubt, it would be foolish to write applications entirely in MSIL. After all, if you're the sort of person who insists on writing everything from tight loops to full office applications in assembler, you're probably not going to be using .NET (which is so far from the guts of the system you'd most likely feel physically sick at the prospect). However, working MSIL knowledge is a valuable tool for the .NET developer. For example, you can use the tool ildasm to browse program assembly contents displayed in MSIL, or perhaps even write a compiler for your favourite language so that it outputs managed code (BrainFuck.Net has already been done - really! Go check). Finally (and somewhat surprisingly) even the flagship .NET language C# doesn't support all of the features of the platform. In very unusual circumstances it may be necessary to interface with MSIL in order to facilitate any strange functionality.

Reading MSIL

To grasp the basics of MSIL, a good starting point is to write a console application which prints a string, and then browse the generated MSIL using the ildasm tool supplied with the framework. It should look something like this (unnecessary, complex syntax has been stripped for clarity):

.method public static void  Main(string[] args)
{
  .entrypoint
  // Code size       11 (0xb)
  .maxstack  1
  ldstr      "Hello!"
  call       void [mscorlib]System.Console::WriteLine(string)
  ret
}

(This is compile-able code - using ilasm you could turn this into an assembly for use in a .NET project using any language.)

The first thing you might notice is that Main doesn't seem to be a member of a class. MSIL is a little like C++ in that it has OOP capabilities, but also allows items to be independent of classes. This is mostly irrelevant, as the compiler knows what to do depending on which language you use.

Reassembly

.method is the first directive in the MSIL code, and as you might expect it declares a callable public static method which takes a string array parameter and returns void. The guts of the method are marked by curly braces, as in C-like languages.

.entrypoint is a way of marking the initial execution point for a run-able assembly. The entry point of a program doesn't have to be Main, even in C# itself.

.maxstack declares the amount of stack space the method will be needing. Most managed .NET instructions require use of the stack, so it's optimal to declare a set stack space before execution.

ldstr loads a string object into the stack which we have already declared the size of.

call causes the execution to jump to a method in the core library namespace System.Console with the name WriteLine. The string parameter is needed so that the runtime knows which overloaded version of WriteLine to use.

ret, as you might imagine, returns from the method.

MSIL is fairly intuitive for what is the lowest level language of the platform. In most cases it can be understood from browsing compiled code, supplemented with the SDK reference documents to look up any confusing syntax.

References:
    http://www.codeguru.com/Csharp/.NET/net_general/il/article.php/c4635/
    http://weblogs.asp.net/kennykerr/category/7140.aspx