C#从其内存地址调用函数

时间:2018-08-19 02:04:10

标签: c# callback

我在C#中挂了一个函数,之后需要用它的原始指针来调用它(我在调用它之前先恢复原始字节)

我知道在C语言中可以执行以下操作:

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

但是,我似乎找不到在C#中执行此操作的正确方法。 我尝试了以下方法:

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();
}

我还尝试玩以下游戏:

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

但是我似乎无法弄清楚如何像在C语言中那样为它提供一个指针。原始函数指针存储在: hookedFunction.OriginalMethod.MethodHandle.GetFunctionPointer()

2 个答案:

答案 0 :(得分:1)

Delegate充当函数的指针,因此您可以调用委托所指向的许多函数:

  

委托是一种可以安全封装方法的类型,类似于   C和C ++中的函数指针。”

Microsoft Docs about delegate

答案 1 :(得分:0)

我首先要提到的是不支持CallingConvention.FastCall,请看一下here

这是钩子类:

// 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();
    }
}

此技术不是线程安全的(more info on it),建议您使用EasyHook