Library for creating Delegate to any machine code provided as a byte array
// Created by Adam Furmanek | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
namespace ByteToFunc | |
{ | |
// Flags for VirtualProtect method | |
public enum Protection | |
{ | |
PAGE_NOACCESS = 0x01, | |
PAGE_READONLY = 0x02, | |
PAGE_READWRITE = 0x04, | |
PAGE_WRITECOPY = 0x08, | |
PAGE_EXECUTE = 0x10, | |
PAGE_EXECUTE_READ = 0x20, | |
PAGE_EXECUTE_READWRITE = 0x40, | |
PAGE_EXECUTE_WRITECOPY = 0x80, | |
PAGE_GUARD = 0x100, | |
PAGE_NOCACHE = 0x200, | |
PAGE_WRITECOMBINE = 0x400 | |
} | |
// Base type for stubbing to have runtime type checking | |
public class BaseStub | |
{ | |
public int Target; | |
} | |
public class FuncGenerator | |
{ | |
// Method to unlock page for executing | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); | |
// Unlocks page for executing | |
private static void UnlockPage(int address) | |
{ | |
uint old; | |
VirtualProtect((IntPtr)address, 6, (uint)Protection.PAGE_EXECUTE_READWRITE, out old); | |
} | |
// Some internal storage for pinning | |
private static IList<object> memory = new List<object>(); | |
private static IList<GCHandle> handles = new List<GCHandle>(); | |
// Pins array with code and returns address to the beginning of the array | |
private static IntPtr Pin(object data) | |
{ | |
memory.Add(data); | |
var handle = GCHandle.Alloc(data); | |
handles.Add(handle); | |
return Marshal.ReadIntPtr(GCHandle.ToIntPtr(handle)); | |
} | |
// Returns delegate of type T using class U for stubbing | |
public static T Generate<T, U>(byte[] data) where U : BaseStub, new() | |
{ | |
// Address of machine code in array | |
// We omit first 8 bytes (array type and size) | |
var arrayCodeAddress = ((int)Pin(data)) + 8; | |
Console.WriteLine("Machine code in array address: " + arrayCodeAddress.ToString("X")); | |
// Unlock page so we can execute code from it | |
UnlockPage(arrayCodeAddress); | |
// Fixing code of stub method Stub | |
// Target points to the actual place with the code | |
var oldTarget = new U { | |
Target = arrayCodeAddress | |
}; | |
var emitDelegate = Delegate.CreateDelegate(typeof(T), oldTarget, "Stub"); | |
var stubMethodHandle = typeof(U).GetMethod("Stub").MethodHandle; | |
RuntimeHelpers.PrepareMethod(stubMethodHandle); | |
var stubCodeAddress = stubMethodHandle.GetFunctionPointer(); | |
Console.WriteLine("Stub address: " + stubCodeAddress.ToString("X")); | |
// Modifies stub method to do the absolute jump | |
Marshal.WriteInt32(stubCodeAddress, unchecked((int)0xc30471ff)); // push dword ptr [ecx+4] ; retn | |
// Returns delegate of correct type so we have runtime type checking | |
return (T)(object)emitDelegate; | |
} | |
} | |
// Stub for Action<int> | |
// Each stub must have a non-static method called Stub with correct signature and at least 4 bytes of body | |
public class ActionInt : BaseStub | |
{ | |
public void Stub(int x) | |
{ | |
Console.WriteLine("Original action stub"); | |
} | |
} | |
// Stub for Func<int, int> | |
public class FuncInt : BaseStub | |
{ | |
public int Stub(int x) | |
{ | |
Console.WriteLine("Original func stub"); | |
return 0; | |
} | |
} | |
public class Program | |
{ | |
// Helper method just to show jumping | |
public void MyWriteLine(int text) | |
{ | |
// Type is ByteToFunc.ActionInt because we are jumping around in the code! | |
Console.WriteLine("I'm in the MyWriteLine! And I am:\t\t" + GetType()); | |
Console.WriteLine(text); | |
Console.WriteLine(); | |
} | |
static void ActionTest() | |
{ | |
var methodHandle = typeof(Program).GetMethod(nameof(Program.MyWriteLine), BindingFlags.Public | BindingFlags.Instance).MethodHandle; | |
RuntimeHelpers.PrepareMethod(methodHandle); | |
var methodAddress = methodHandle.GetFunctionPointer(); | |
Console.WriteLine("MyWriteLine address: " + methodAddress.ToString("X")); | |
var address = BitConverter.GetBytes((int)methodAddress).ToArray(); | |
// Should jump to MyWriteLine and do some job | |
Action<int> function = FuncGenerator.Generate<Action<int>, ActionInt>( | |
new byte[]{ | |
0x68, // push | |
} | |
.Concat(address) | |
.Concat(new byte[] | |
{ | |
0xC3 //retn | |
}).ToArray() | |
); | |
function(5); | |
} | |
static void FuncTest() | |
{ | |
Func<int, int> function = FuncGenerator.Generate<Func<int, int>, FuncInt>( | |
new byte[]{ | |
0x89, 0xD0, // mov eax, edx | |
0x83, 0xC0, 0x04, // add eax, 4 | |
0xc3 // retn | |
} | |
); | |
// Should print 23 + 4 == 27 | |
Console.WriteLine(function(23)); | |
} | |
static void Main(string[] args) | |
{ | |
ActionTest(); | |
FuncTest(); | |
// To figure out machine code you can use this: https://defuse.ca/online-x86-assembler.htm | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment