Why is difficult to disassemble native Win32, but easy to disassemble .NET app?
Due to the implementation of .NET allowing for interoperability between languages such as C#,VB, and even C/C++ through the CLI and CLR this means extra metadata has to be put into the object files to correctly transmit Class and object properties. This makes it easier to disassemble since the binary objects still contain that information whereas C/C++ can throw that information away since it is not necessary (at least for the execution of the code, the information is still required at compile time of course).
This information is typically limited to class related fields and objects. Variables allocated on the stack will probably not have annotations in a release build since their information is not needed for interoperability.
One more reason - optimizations that most C++ compilers perform when producing final binaries are not performed on IL level for managed code.
As result something like iteration over container would look like couple inc
/jnc
assembly instructions for native code compared with function calls with meaningful names in IL. Resulting executed code may be the same (or at least close) as JIT compiler will inline some calls similar to native compiler, but the code one can look at is much more readable in CLR land.
A .net assembly is built into Common Intermediate Language. It is not compiled until it is about to be executed, when the CLR compiles it to run on the appropriate system. The CIL has a lot of metadata so that it can be compiled onto different processor architectures and different operating systems (on Linux, using Mono). The classes and methods remain largely intact.
.net also allows for reflection, which requires metadata to be stored in the binaries.
C and C++ code is compiled to the selected processor architecture and system when it is compiled. An executable compiled for Windows will not work on Linux and vice versa. The output of the C or C++ compiler is assembly instructions. The functions in the source code might not exist as functions in the binary, but be optimized in some way. Compilers can also have quite agressive optimizers that will take logically structured code and make it look very different. The code will be more efficient (in time or space), but can make it more difficult to reverse.