1

I'm hooking a function in C# and I need to call it by it's original pointer afterwards (I'm restoring the original bytes before calling it)

I know that in C the following would work:

typedef void(__fastcall *tExecuteFunction)(int a1, __int64 a2);
tExecuteFunction oExecuteFunction = (tExecuteFunction)(0xABCDEF);
oExecuteFunction(0, 0);

However, I can't seem to find a proper way to do this in C#. I've tried the following:

void HookedFunction(int a1, IntPtr a2){
    //Do stuff
    hookedFunction.Unhook();
    //MethodInfo of the hooked function
    hookedFunction.OriginalMethod.Invoke(this, new object[] {a1, a2});
    hookedFunction.Hook();
}

I also tried to play around with the following:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void tExecuteFunction(int a1, __int64 a2);

but I can't seem to figure out how to give it a pointer like we can do in C. The original function pointer is stored in: hookedFunction.OriginalMethod.MethodHandle.GetFunctionPointer()

David
  • 71
  • 10
  • 1
    if you are trying to dynamically assign a function to a variable to pass it and invoke it in other functions, consider the C# Func, Action and Delegates. They serve that purpose. – mymo Aug 19 '18 at 02:12

2 Answers2

3

I have to mention it first that CallingConvention.FastCall is not supported, please take a look here.

Here's the hook class:

// Author: Moien007
public unsafe class Hook
{
    const string KERNEL32 = "kernel32.dll";

    [DllImport(KERNEL32)]
    static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, VirtualProtectionType flNewProtect, out VirtualProtectionType lpflOldProtect);

    private enum VirtualProtectionType : uint
    {
        Execute = 0x10,
        ExecuteRead = 0x20,
        ExecuteReadWrite = 0x40,
        ExecuteWriteCopy = 0x80,
        NoAccess = 0x01,
        Readonly = 0x02,
        ReadWrite = 0x04,
        WriteCopy = 0x08,
        GuardModifierflag = 0x100,
        NoCacheModifierflag = 0x200,
        WriteCombineModifierflag = 0x400
    }

    private byte[] m_OriginalBytes;

    public IntPtr TargetAddress { get; }
    public IntPtr HookAddress { get; }

    public Hook(IntPtr target, IntPtr hook)
    {            
        if (Environment.Is64BitProcess)
            throw new NotSupportedException("X64 not supported, TODO");

        TargetAddress = target;
        HookAddress = hook;

        m_OriginalBytes = new byte[5];
        fixed (byte* p = m_OriginalBytes)
        {
            ProtectionSafeMemoryCopy(new IntPtr(p), target, m_OriginalBytes.Length);
        }
    }

    public void Install()
    {
        var jmp = CreateJMP(TargetAddress, HookAddress);
        fixed (byte* p = jmp)
        {
            ProtectionSafeMemoryCopy(TargetAddress, new IntPtr(p), jmp.Length);
        }
    }

    public void Unistall()
    {
        fixed (byte* p = m_OriginalBytes)
        {
            ProtectionSafeMemoryCopy(TargetAddress, new IntPtr(p), m_OriginalBytes.Length);
        }
    }

    static void ProtectionSafeMemoryCopy(IntPtr dest, IntPtr source, int count)
    {
        // UIntPtr = size_t
        var bufferSize = new UIntPtr((uint)count);
        VirtualProtectionType oldProtection, temp;

        // unprotect memory to copy buffer
        if (!VirtualProtect(dest, bufferSize, VirtualProtectionType.ExecuteReadWrite, out oldProtection))
            throw new Exception("Failed to unprotect memory.");

        byte* pDest = (byte*)dest;
        byte* pSrc = (byte*)source;

        // copy buffer to address
        for (int i = 0; i < count; i++)
        {
            *(pDest + i) = *(pSrc + i);
        }

        // protect back
        if (!VirtualProtect(dest, bufferSize, oldProtection, out temp))
            throw new Exception("Failed to protect memory.");
    }

    static byte[] CreateJMP(IntPtr from, IntPtr to)
    {
        return CreateJMP(new IntPtr(to.ToInt32() - from.ToInt32() - 5));
    }

    static byte[] CreateJMP(IntPtr relAddr)
    {
        var list = new List<byte>();
        // get bytes of function address
        var funcAddr32 = BitConverter.GetBytes(relAddr.ToInt32());

        // jmp [relative addr] (http://ref.x86asm.net/coder32.html#xE9)
        list.Add(0xE9); // jmp
        list.AddRange(funcAddr32); // func addr

        return list.ToArray();
    }
}
  • Find target function address (for example using GetProcAddress)
  • Define a delegate for that function
  • Use Marshal.GetDelegateForFunctionPointer and get delegate for target function so you call it inside your hook function
  • Define your hook function (I mean the function that will called instead of target)
  • Use Marshal.GetFunctionPointerForDelegate and get function pointer for your hook function
    (Note: assign the delegate to a static field or use GCHandle.Alloc to prevent GC from collecting it which leads to crash)
  • Now use Hook class

This technique isn't thread safe (more info on it), I recommend you to use EasyHook.

moien
  • 999
  • 11
  • 26
1

Delegate works as pointer to a function, so you can call many functions whose are pointed by the delegate:

A delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++."

Microsoft Docs about delegate