I’m sure many have ran across the same issue I did recently, and that is that when doing API hooking of low-level functions (e.g. the NT API) code that is inserted into the hook function (e.g. logging code) will end up calling the same function and cause a stack overflow.
Greyman had a good idea which I’ve used, and put together an example for. Credits to Greyman for the idea.
Basically, you use TLS to hold a flag so that the offending code (the code causing the infinite recursion) is only executed once per call. The following code should work under all major Windows compilers, but is using a nonstandard compiler extension so may not work cross-platform (which is not a concern for a low level project anyway). Using LdrGetProcedureAddress as an example because using any of the standard output functions will cause the recursive call (printf, std::cout, etc):
NTSTATUS NTAPI APIHook::LdrGetProcedureAddress_Hook( IN HMODULE ModuleHandle,
IN PANSI_STRING FunctionName OPTIONAL, IN WORD Ordinal OPTIONAL,
OUT PVOID *FunctionAddress )
{
// Use TLS to prevent recursive function calls when logging
static __declspec(thread) bool Logging = false;
// Get the true address of the function
NTSTATUS RetVal =
((tLdrGetProcedureAddress)(PROC)(sm_LdrGetProcedureAddress))
(ModuleHandle,FunctionName,Ordinal,FunctionAddress);
// Don’t call function recursively when logging
if (Logging)
return RetVal;
// Start logging wrapper
Logging = true;
// Function pointer
PVOID pFunc = (FunctionAddress ? *FunctionAddress : 0);
// Log API call
if (Config::Get()->ShouldLogFunctions())
{
try
{
// Guard against structured exceptions
SehGuard Guard;
// Create string from c-style string
// Note: Will thrown an access violation if c-style string isn’t a
// valid pointer
std::string FunctionNameTemp(FunctionName->Buffer);
// If we get to this point the string is valid so log it.
DBGPRNT(boost::str(boost::format(”LdrGetProcedureAddress: Module”
” = %p, Name = %s. Return = %p.\n”) %ModuleHandle
%FunctionNameTemp %pFunc).c_str());
}
catch (const SehException& /*e*/)
{
// If we get here the string is invalid so we assume it’s an
// ordinal and log it as such
DBGPRNT(boost::str(boost::format(”LdrGetProcedureAddress: Module”
” = %p, Ordinal = %u. Return = %p.\n”) %ModuleHandle %Ordinal
%pFunc).c_str());
}
}
// End logging wrapper
Logging = false;
// Check we got a valid pointer back
if (!FunctionAddress)
return RetVal;
// Dereference pointer and get actual function pointer
FARPROC pfn = (FARPROC)(*FunctionAddress);
// Is it one of the functions that we want hooked?
APIHook* p = sm_pHead;
for (; (pfn != NULL) && (p != NULL); p = p->m_pNext)
{
if (pfn == p->m_pOrig)
{
// The address to return matches an address we want to hook
// Return the hook function address instead
pfn = p->m_pHook;
break;
}
}
return RetVal;
}
In the above code DBGPRNT is just a macro used to call ‘printf’ if a certain compiler flag is enabled, or omit the code entirely if the flag is disabled. Implemented that way for performance reasons. Irrelevant details aside though, its easy to see how the implementation and how simple it is to use. Obviously thread-safe due to the variable being thread-local so it shouldn’t interfere with anything, I’ve yet to have any problems with it. Tested on both IA-32 and AMD64.