Skip to content

Instantly share code, notes, and snippets.

@afish
Created December 28, 2017 03:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save afish/8fd6cf8f8c196901b5e1a5ee1000ee68 to your computer and use it in GitHub Desktop.
Save afish/8fd6cf8f8c196901b5e1a5ee1000ee68 to your computer and use it in GitHub Desktop.
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