Function Hooking
The Vezel.Ruptura.Memory package provides an easy-to-use FunctionHook
class that takes care of some common requirements such as placing the trampoline near the target function, atomically toggling an installed hook, associating state with the hook function, and avoiding certain cases of deadlock or stack overflow.
Usage looks like this:
This example will print foobar
to the console. There is a lot going on here, so let us go over each part:
The
NativeLibrary
API is used to retrieve a function pointer to theSetThreadDescription
function.A
PageCodeManager
instance is created to manage code allocations. This is used byFunctionHook
to allocate its internal trampoline near the target function.FunctionHook.Create()
is called to hookSetThreadDescription
with theSetThreadDescriptionHook
method. The string object"bar"
is passed as the hook's associated state so that it can be accessed in the hook method.A created hook is fully functional with all code emission and patching done, but calls will not actually be intercepted until the
IsActive
property is set totrue
, so that is then done.The function pointer for the target function is invoked with some dummy arguments. Since the hook is active, the
SetThreadDescriptionHook
method will be called.SetThreadDescriptionHook
accesses theFunctionHook.Current
property and retrieves itsState
property (i.e. the"bar"
string passed earlier). It concatenates it with the string passed in aslpThreadDescription
and prints the result ("foobar"
) to the console.
This example is fairly contrived, but you can hook almost any function, whether it comes from the operating system, a normal library, or an executable. Of course, you can also associate more useful state objects with the hook so that you can access your application's state.
It is important that the hook method matches the target function exactly in calling convention (cdecl
, stdcall
, etc), return type, and parameter types. Mismatches can result in unpredictable bugs and crashes. Also, managed exceptions thrown from within a hook method must be handled; native call frames cannot be correctly unwound by CoreCLR.
Hook Gate
Internally, FunctionHook
uses a so-called hook gate as part of its trampoline to guard calls to the hook method. The most user-visible effects of this are:
The
FunctionHook
instance for the most recent hook method in the call stack can be accessed through theCurrent
property.Hook recursion does not invoke the hook method a second time, preventing common issues like deadlocks and stack overflows when calling arbitrary code in a hook.
Hooks are not invoked while the Windows loader lock is held since managed code is virtually impossible to run reliably when that is the case.
Last updated