x86 assembly in C# revisited
As I mentioned, the original code I wrote didn't run on Windows with .NET, but it ran on Windows using Mono. However, after digging a little deeper (with the help of the nice folks at mono-list) I found that the problem wasn't the runtime stopping me from executing code, but rather my assembly code being invalid (who would have thought ;-) ?). Or more precisely, the code didn't follow certain conventions that must be followed.
The assembly code works if compiled into a single program, but when interacting with (here, being called by) other methods written in other languages, there are certain rules that must be followed so that the stack and registers aren't messed up while executing. There are certain registers that must be the same after the method returns, and the stack pointer must either be untouched or properly changed (depending on which convention is used).
After getting that last one right I thought that my code was OK, and that was when I posted the C# code. However, what I didn't get right was that the ebx register (and a few others) must not be changed (mov ebx 9 is essentially assignment; ebx = 9).
So this time I let GCC do the work for me by compiling a single C function and then disassemble it. After that it's just inserting the opcodes in the C# array just like the last time:
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; class X86AssemblyExec { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int TheDelegate(); public static void Main(string[] args) { // 0: 55 push ebp // 1: 89 e5 mov ebp,esp // 3: 83 ec 10 sub esp, 16 // 6: c7 45 fc 06 00 00 00 mov [ebp-4], 6 // d: c7 45 f8 0c 00 00 00 mov [ebp-8], 12 // 14: 8b 45 f8 mov eax,[ebp-8] // 17: 01 45 fc add [ebp-4],eax // 1a: 8b 45 fc mov eax,[ebp-4] // 1d: c9 leave // 1e: c3 ret // opcode List<byte> codeList = new List<byte>(); codeList.Add(0x55); codeList.Add(0x89); codeList.Add(0xe5); codeList.Add(0x83); codeList.Add(0xec); codeList.Add(0x10); codeList.Add(0xc7); codeList.Add(0x45); codeList.Add(0xfc); codeList.Add(0x06); codeList.Add(0x00); codeList.Add(0x00); codeList.Add(0x00); codeList.Add(0xc7); codeList.Add(0x45); codeList.Add(0xf8); codeList.Add(0x0c); codeList.Add(0x00); codeList.Add(0x00); codeList.Add(0x00); codeList.Add(0x8b); codeList.Add(0x45); codeList.Add(0xf8); codeList.Add(0x01); codeList.Add(0x45); codeList.Add(0xfc); codeList.Add(0x8b); codeList.Add(0x45); codeList.Add(0xfc); codeList.Add(0xc9); codeList.Add(0xc3); byte [] code = codeList.ToArray(); unsafe { fixed (void *ptr = code) { // create the delegate TheDelegate del = (TheDelegate) Marshal.GetDelegateForFunctionPointer( new IntPtr(ptr), typeof(TheDelegate)); // call the function int x = del(); // outputs 18 Console.WriteLine(x); } } } }
The difference is that now it runs on MS .NET too!
Comments
- Blackdanhil http://www.homepage148.domain849.com 2009-01-27 13:57:52
- Hack again?!
© 2008 Markus Johnsson