Thursday, June 3, 2010

Exploring Main() in C#

In a typical Win32 .NET application you could find that the entry point always point to the Main method, be it Console or Windows application. Out of curiosity I thought of digging into the internals, to see whether is it possible to map the entry point to a method name different from "Main". So to start off with, lets take the infamous HelloWorld sample for demo purpose.

Listing - 1

   1:  using System;
   2:   
   3:  namespace HelloWorldNamespace
   4:  {
   5:      public class Program
   6:      {
   7:          public static void Main()
   8:          {
   9:              Console.WriteLine("Hello World...");
  10:          }
  11:      }
  12:  }

Here in listing 1, you could see the Main method being the entry point as usual; before proceeding have you ever tried renaming the "Main()" to something of your choice?, If so, definitely the compiler would have stopped from compiling the application saying like "The Entrypoint Main() not found". So c'mon lets alter this code to get the entry point name mapped to something different. Before starting off, I strongly recommend using Notepad++ for working with the code listing discussed in the article, as we are going to deal with multi-file assemblies.


Listing - 2

   1:  using System;
   2:  using System.Reflection;
   3:   
   4:  namespace HelloWorldNamespace
   5:  {
   6:      public class Program
   7:      {
   8:          public static void MyEntryPoint()
   9:          {
  10:              MethodBase method = MethodBase.GetCurrentMethod();
  11:              Console.WriteLine(method.Name + ": Hello World...");
  12:          }
  13:      }
  14:  }


In listing 2, you could find the same code as in Listing 1 being retrofitted with some new additions like, the entry point had been re-named to MyEntryPoint, while a new type MethodBase is being implemented for printing out, the method name during application execution, this for ensuring that the MyEntryPoint method name is not being altered or overwritten by the compiler nor by the runtime behind the scenes. 


So lets compile the above code snippet from "Visual Studio command prompt", before this save the code in listing 2, to a file named "HelloWorld.cs". Key-in the commands in listing 3, to the "VS.NET Command Prompt" and make sure the location in console window is set to where the source code is located.


Listing - 3











csc /target:module HelloWorld.cs
al HelloWorld.netmodule /target:exe /Main:HelloWorldNamespace.Program.MyEntryPoint /out:Helloworld.exe  


(If you find working with the VS.NET command prompt cumbersome; like opening up the program from deep inside the start menu item and thereafter navigating to the source code location, I recommend reading this article.)
If you are familiar the commands and parameters in listing 3, you can straight away proceed to "Executing Application" section. In case if you find the commands in listing 3 unfamiliar? Let me give you a sneak preview. The commands CSC and AL are provided by .NET framework SDK, where "CSC" is the "CSharp Compiler", while the "AL" is the "Assembly Linker". Lets analyze the Listing 3, line by line. 


Listing - 4




csc /target:module HelloWorld.cs



In listing 4, you can find the first line from taken from listing 3. Here the csharp compiler(CSC) is passed with two parameters. The first parameter "/target:" instructs the compiler to generate a ".netmodule". While the second parameter "HelloWorld.cs" specifies the file name of source code as input. On execution the CSC generates  a file named "HelloWorld.netmodule" in the same directory as of "HelloWorld.cs".


Listing - 5
al HelloWorld.netmodule /target:exe /Main:HelloWorldNamespace.Program.MyEntryPoint /out:Helloworld.exe

In listing 5, you can find the second line from taken from listing 3. Here we are calling the "Assembly Linker" to generate an executable by referring the previously generated "HelloWorld.netmodule". From the the above listing you could find four parameters being passed to "AL.exe". The first parameter instructs to reference the HelloWorld.netmodule which was generated in the preceding step (Listing - 4). While the second parameter is a '/target:' flag which instructs to generate an console application. The Third parameter '/Main:' is where the entry point mapping happens, here we are specifying the method to be called in replacement of Main method. You can find fully qualified path to the method being passed in the following format {Namespace.TypeName.MethodName}. Finally in last parameter, the "/out:" flag where the name of the executable is mentioned as "HelloWorld.exe".



Executing Application
Voila, we are done with. Now you can execute the application and see the output. The output should be similar to listing 5, if you have used the same name for your entry-point.


Listing - 6




MyEntryPoint: Hello World...



Inside Story
So now we have an assembly with the entry point mapped to a different method name than that of the default Main(). Lets peek inside the HelloWorld.exe Assembly using "Red-Gate Reflector" and see, how this entry point re-mapping works.


Peeking inside the Code
Here is a screenshot from Reflector, of the HelloWorld.exe.




In the above screen shot from "Red-Gate Reflector", you can see two different modules namely HelloWorld.exe and HelloWorld.netmodule. You can find a method named "_EntryPoint():Void" the one highlighted in the screenshot, which does the job of pointing to the  entry-point of our choice. In the Disassembler window; you can see the C# code that does this mapping.


Main Program Variants
Its common to see the Main() method container type being used inside a Reference-Type most of the time. But did you know that, its possible to use "Struct"in place of "Class" container? Here in the below code listing you could find "struct" being used in place of "class", which is perfectly legal.




Listing - 7
   1:  using System;
   2:   
   3:  namespace HelloWorldNamespace
   4:  {
   5:      public struct Program
   6:      {
   7:          public static void Main()
   8:          {
   9:              Console.WriteLine("Hello World...");
  10:          }
  11:      }
  12:  }

So thats it. Truth to be told, If you were you ask me whats the benefit of this remapping Main() with a custom method name? I am clueless on this, if you happen to know any benefits on aspects relating to security or obfuscation, please let me know.


No comments: