It depends on what version of Windows you have. But the basic mechanism is that every .NET assembly contains five bytes of unmanaged code. For an EXE, it is a JMP instruction to _CorExeMain() in c:\windows\system32\mscoree.dll. Which then does the heavy lifting of initializing the CLR and getting the Main() method of your program started.
Later versions of Windows have a loader that knows about the format of a .NET assembly and integrates directly with mscoree.dll. Required to support the very unusual feature that a 32-bit executable can start a 64-bit process. More about that in this answer.
This is the 10,000 feet view. The essential difference between .NET and Java is that a .NET assembly can contain both unmanaged code (like that JMP) and data (the assembly manifest and IL). A Java .class or .jar file is pure data.