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
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
0 comments:
Post a Comment