This is a mirror of official site: http://jasper-net.blogspot.com/

Dynamically Writing and Executing Native Assembly in C#

| Monday, April 12, 2010
Generally, executing native assembly in C# is bad because you lose all the benefits of the managed world. In the cases where it is impossible to perform something in C#, it is better to make a C++ DLL. That said, the new method Marshal.GetDelegateForFunctionPointer() in the .NET Framework v2.0 opens up the possibility for dynamically writing and executing assembly code. Consider the following basic C function:


   int __declspec(noinline) __stdcall MyAdd(int x, int y)
   {
       return x + y;
   }

The C compiler will compile the code into assembly. Though the code which gets compiled may vary from compiler to platform, for x86 it will usually have this basic form:


   Instruction                 Code Bytes   Comment
   ------------------------------------------------------------------------------
   mov eax,dword ptr [esp+8] ' 8B 44 24 08  Load the x into eax
   mov ecx,dword ptr [esp+4] ' 8B 4C 24 04  Load the y into ecx
   add eax,ecx               ' 03 C1        Add eax and ecx, result goes into eax
   ret 8                     ' C2 08 00     Pop x and y off the stack, return eax

The instructions listed are disassembly copied from the VC++ debugger. The code bytes are what actually executes. The idea is to take those code bytes, write them into a native buffer, acquire a delegate for that buffer, and finally execute the delegate. Here is sample code to run these code bytes from C#:


   using System;
   using System.Runtime.InteropServices;


   class Program
   {
       private delegate Int32 MyAdd(Int32 x, Int32 y);

       private static void Main()
       {
           // A simple Add function
           Byte[] myAddNativeCodeBytes = new Byte[]
           {
               0x8B, 0x44, 0x24, 0x08, // mov eax,dword ptr [esp+8]
               0x8B, 0x4C, 0x24, 0x04, // mov ecx,dword ptr [esp+4]
               0x03, 0xC1,             // add eax,ecx
               0xC2, 0x08, 0x00        // ret 8
           };

           // We need to push the code bytes into a native buffer
           IntPtr myAddNativeCodeBytesPtr = IntPtr.Zero;

           try
           {
               // Allocate the native buffer
               myAddNativeCodeBytesPtr =
                   Marshal.AllocCoTaskMem(myAddNativeCodeBytes.Length);

               // Push the code bytes over
               Marshal.Copy(myAddNativeCodeBytes, 0,
                   myAddNativeCodeBytesPtr, myAddNativeCodeBytes.Length);

               // Get a function pointer for the native code bytes
               MyAdd myAdd = (MyAdd)Marshal.GetDelegateForFunctionPointer(
                   myAddNativeCodeBytesPtr, typeof(MyAdd));

               // Call the native code bytes
               Int32 result = myAdd(4, 5);

               // Did it work?
               Console.WriteLine("Result: {0}", result);
           }

           finally
           {
               // Free the native buffer
               if (myAddNativeCodeBytesPtr != IntPtr.Zero)
               {
                   Marshal.FreeCoTaskMem(myAddNativeCodeBytesPtr);
                   myAddNativeCodeBytesPtr = IntPtr.Zero;
               }
           }
       }
   }

In this sample I just used Marshal.AllocCoTaskMem(), but one should actually P/Invoke VirtualAllocEx and VirtualProtectEx to ensure the page where the code exists has PAGE_EXECUTE access. Additionally the above sample is targetted to my personal x86 machine, and will not run on x64 or any other platform for that matter.

Read more: Devin Jenson's WebLog

Posted via email from jasper22's posterous

0 comments: