Yes, see my detailed answer here
The main part is: (Without any P/Invoke or external reference)
public static unsafe int? InjectAndRunX86ASM(this Func<int> del, byte[] asm) { if (del != null) fixed (byte* ptr = &asm[0]) { FieldInfo _methodPtr = typeof(Delegate).GetField("_methodPtr", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo _methodPtrAux = typeof(Delegate).GetField("_methodPtrAux", BindingFlags.NonPublic | BindingFlags.Instance); _methodPtr.SetValue(del, ptr); _methodPtrAux.SetValue(del, ptr); return del(); } else return null; }
Which can be used as follows:
Func<int> del = () => 0; byte[] asm_bytes = new byte[] { 0xb8, 0x15, 0x03, 0x00, 0x00, 0xbb, 0x42, 0x00, 0x00, 0x00, 0x03, 0xc3 }; // mov eax, 315h // mov ebx, 42h // add eax, ebx // ret int? res = del.InjectAndRunX86ASM(asm_bytes); // should be 789 + 66 = 855
EDIT: The newest version of C# (C#9) introduces the concept of 'delegate pointers', which allows you to do the following:
byte[] asm = { 0x8D, 0x04, 0x11, // lea eax, [rcx+rdx] 0xC3 // ret }; void* buffer = VirtualAlloc(null, asm.Length, 0x1000, 4); var func = (delegate*<int, int, int>)buffer; int dummy; Marshal.Copy(asm, 0, (nint)buffer, asm.Length); VirtualProtect(buffer, asm.Length, 0x20, &dummy); Console.WriteLine(func(42, 378)); // call 'func' with (42, 378), which computes '420' VirtualFree(buffer, 0, 0x8000);
You can find a complete example here: https://gist.github.com/Unknown6656/a42a810d4283208c3c21c632fb16c3f9