Hi all,
I was attempting to hook some interesting function which, in 1.12, happens to be implemented with the __fastcall calling convention. After a few hours of searching and frustration, I recalled that I had already done the work a few months ago, extending Apoc's GreyMagic (LINK) to support detouring of fastcall functions.
The code isn't particularly clean, implementing a new DetourManager just for fastcall hooks and not using any sort of inheritance to avoid having to touch original anywhere not strictly necessary. To be honest I don't even remember completely how I did it, feel free to point out any potential bugs/leaks or anything else, for me it's working properly with a few functions from 1.12.
To use, you need to define more stuff than for a regular hook. In particular:
- 1. A delegate for the original function you are detouring. Make this delegate match the signature of the original function.
- 2. A delegate for the fastcall stub (more on this later) which should take the signature of the original function, with the addition of an extra argument at the beginning of the argument list, an IntPtr.
- 3. A delegate for the detour (which will point to the managed function serving as the detour function), with the same signature as item #1.
The fastcall stub is a function defined in some non-managed DLL that allows you to call an arbitrary fastcall function just by calling into the stub with the stdcall calling convention providing as an extra (first) argument the address of the fastcall function.
In particular the one I'm using was picked from the 1.12 thread (LINK)(by MartyT) which goes like this:
Code:
void __declspec(dllexport) __declspec(naked) InvokeFastcall()
{
__asm
{
pop edx
XCHG DWORD PTR SS:[ESP+8],EDX
pop eax
pop ecx
jmp eax
}
}
Just load the DLL and get the address for this function with the usual Windows API functions. Then, create the stub delegate instance like this:
Code:
dSomeFunctionStub = (D_SomeFunction_Stub)
Marshal.GetDelegateForFunctionPointer(Addr_InvokeFastcall, typeof(D_SomeFunction_Stub));
I think you can also use CreateFunction from GreyMagic which sort of wraps the above call if I remember right.
Overall, the process of installing these kind of hooks is more involved than regular (stcall/cdecl/thiscall) hooks, but since I've only used it so far in a couple of places I haven't bothered to provide a facade to make it easier.
A more comprehensive example, hooking the SpellStartFunction in 1.12 (I include the relevant pieces only without context):
Code:
// The managed detour function itself
private int SpellStartHandlerDetour(int arg1, int pkt_code, int arg3, IntPtr pDataStore)
{
// * Note how the arguments are passed to the original function *
object[] args = { ADDR_SPELLSTARTHANDLER, arg1, pkt_code, arg3, pDataStore};
// Do stuff...
return (int)gr.in_magic.FastcallDetours[SPELLSTARTHANDLER_DETOUR_ID].CallOriginal
(args);
}
private const string SPELLSTARTHANDLER_DETOUR_ID = "SpellStartHandlerDetour";
private static IntPtr ADDR_SPELLSTARTHANDLER = (IntPtr)0x6e7640;
// Getting the stub function
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
static extern int LoadLibrary(
[MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddress(int hModule,
[MarshalAs(UnmanagedType.LPStr)] string lpProcName);
int hModule = LoadLibrary(Cwd + @"\FastcallDll.dll");
Addr_InvokeFastcall = GetProcAddress(hModule, "InvokeFastcall");
// Signatures
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int D_SpellStartHandler(int arg1, int pkt_code, int arg3,
IntPtr pDataStore);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int D_SpellStartHandler_Stub(IntPtr lpfnAddr, int arg1,
int pkt_code, int arg3, IntPtr pDataStore);
// Initializing instances
dSpellStartHandlerOriginal = gr.in_magic.CreateFunction<D_SpellStartHandler>
(ADDR_SPELLSTARTHANDLER);
dSpellStartHandlerFastcallStub = (D_SpellStartHandler_Stub)
Marshal.GetDelegateForFunctionPointer(Addr_InvokeFastcall,
typeof(D_SpellStartHandler_Stub));
dSpellStartHandlerDetour = this.SpellStartHandlerDetour;
// Applying
gr.in_magic.FastcallDetours.CreateAndApply(dSpellStartHandlerOriginal,
dSpellStartHandlerFastcallStub, dSpellStartHandlerDetour,
SPELLSTARTHANDLER_DETOUR_ID);
It should be enough to replace DetourManager.cs, which I attach, along with the file InProcessMemoryReader.cs, to which you should add:
Code:
private FastcallDetourManager _fastcallDetourManager;
public virtual FastcallDetourManager FastcallDetours { get { return _fastcallDetourManager ?? (_fastcallDetourManager = new FastcallDetourManager(this)); } }