In the era of intrusive AVs and EDRs that introduce hot-patches to the running processes for their enhanced optics requirements, modern adversaries must have a robust tool to slide through these watchguards. The propsed implementation of dynamic imports resolver that would be capable of unhooking used functions in-the-fly is yet another step towards strengthening adversary resilience efforts.
The solution I’m proposing here is to switch from using linker-resolved WinAPI imports, staying visibile in compiled executable’s PE headers (Import Address Table specifically) to favor fully-dynamic approach insisting on resolving imports only in a dynamic fashion. Such dynamical resolver can be equipped with unhooking logic happening in the background, without any sort of guidance from the operator’s side.
UnhookMe example works:
- It presents us with the first
MessageBoxWthat is not subject for hooking
- Then we hook
MessageBoxWprologue ourselves to make it always return 0 without displaying it’s message
- Finally, we resolve
MessageBoxWdynamically using the
UnhookingImportResolverresolver, which will detect applied prologue patches and restore original bytes, effectively unhooking
In the meantime of popping message boxes, these are the loglines printed to console’s stdout:
[~] Resolved symbol kernel32.dll!CreateFileA
[~] Resolved symbol kernel32.dll!ReadProcessMemory
[~] Resolved symbol kernel32.dll!MapViewOfFile
[~] Resolved symbol kernel32.dll!VirtualProtectEx
[#] Found trampoline hook in symbol: MessageBoxW . Restored original bytes from file.
[~] Resolved symbol user32.dll!MessageBoxW
How to use it?
There are in total 5 C++ source code/header files that your solution need to include. However your main program file needs to include only two required headers, as detailed below.
resolver.h– header containing most of the
UnhookingImportResolverimplementation and handy macrodefinitions
resolver.cpp– source code with global options defined
usings.h– a one big and nasty header file containing tens of
usingtype definitions for commonly used WinAPIs
PE.cpp– custom PE parser source code file
PE.h– custom PE parser header file
Your program will require only two headers being included:
There are couple of global options that can be changed affecting the way in which Resolver works or reports it’s activity. These are defined in the very beginning of
Resolver global options:
globalQuietOption– set to true if you don’t want to have any sort of output
globalVerboseOption– set to true if you want to have detailed verbose output
globalAntiSplicingOption– unhook resolved functions if they’re hooked.
globalLogFilePath– where to redirect output log lines. If empty, pick stdout.
bool globalQuietOption = false;
bool globalVerboseOption = true;
bool globalAntiSplicingOption = true;
wchar_t globalLogFilePath[MAX_PATH] = L"";
Custom API type specification
In order to use Resolver a function pointer type must be first declared with
using statement of strict form:
using fn_FunctionName = ReturnType WINAPI (
This repository comes with
usings.h header file containing predefined using types for tens of popular Windows APIs.
The FunctionName will correspond to the WinAPI that we want to have ImportResolver resolve and that function pointer must be marked as having WINAPI call convention (
__stdcall on x86 and
__fastcall on x64). The ReturnType must precede
WINAPI type modifier.
Function resolution and usage
Having function pointer type defined like specified above, we will be able to use it in the following manner:
ReturnType output = _FunctionName(param1, ..., paramN);
RESOLVE takes care of instantiating
ImportResolver templated object and adjust specifed library’s name.
Resolver introduces several more Macrodefinitions offering easy to use in various circumstances constructor invocation:
#define RESOLVE(mod, func) RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, ::globalAntiSplicingOption)
#define RESOLVE_NO_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, false)
#define RESOLVE_VERBOSE_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, true, true)
#define RESOLVE_VERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, true, false)
#define RESOLVE_NOVERBOSE_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, true)
#define RESOLVE_NOVERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, false)
<div class="highlight highlight-source-c++ position-relative" data-snippet-clipboard-copy-content=" template ImportResolver( std::string dllName, std::string funcName, bool _verbose = false, bool _unhook = false, bool *_wasItHooked = nullptr ) “>
template<typename Ret, typename ...Args>
bool _verbose = false,
bool _unhook = false,
bool *_wasItHooked = nullptr