UnhookMe is a Universal Windows API Resolver And Unhooker Addressing Problem Of Invoking Unmonitored System Calls From Within Of Your Red Teams Malware
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.
Showcase
Here’s how UnhookMe
example works:
MessageBoxW
that is not subject for hookingMessageBoxW
prologue ourselves to make it always return 0 without displaying it’s messageMessageBoxW
dynamically using the UnhookingImportResolver
resolver, which will detect applied prologue patches and restore original bytes, effectively unhooking MessageBoxW
functionality.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
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 UnhookingImportResolver
implementation and handy macrodefinitionsresolver.cpp
– source code with global options definedusings.h
– a one big and nasty header file containing tens of using
type definitions for commonly used WinAPIsPE.cpp
– custom PE parser source code filePE.h
– custom PE parser header fileYour program will require only two headers being included:
# include “usings.h”
# include “resolver.h”
Global options
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.cpp
file:
Resolver global options:
globalQuietOption
– set to true if you don’t want to have any sort of outputglobalVerboseOption
– set to true if you want to have detailed verbose outputglobalAntiSplicingOption
– 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 (
ParamType1 paramName1,
...,
ParamTypeN paramNameN,
);
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.
Having function pointer type defined like specified above, we will be able to use it in the following manner:
RESOLVE(libraryName, FunctionName);
ReturnType output = _FunctionName(param1, …, paramN);
The macro 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)
Resolver’s constructor:
template<typename Ret, typename ...Args>
ImportResolver<Ret WINAPI(Args...)>(
std::string dllName,
std::string funcName,
bool _verbose = false,
bool _unhook = false,
bool *_wasItHooked = nullptr
)
How Does It Work?
The underlaying resolver leverages custom PE headers parser, that processes every referenced DLL module to map their exports and verify that module’s PE headers integrity as well as integrity of referenced function’s stub bytes.
The idea is following:
LoadLibrary
to load referenced by the user library (the one specified as first parameter for RESOLVE
macro) if it could not be reached through GetModuleHandle
.std::map
) during subsequent hits.Among the problems such dynamically-unhooking resolver faced are the issues with traversing forwarded APIs (a DLL may contain Export thunk saying that this function is not implemented in this module, but it is in another one) – which although this implementation has support for, sometimes it brokes its traversal logic.
Kali Linux 2024.4, the final release of 2024, brings a wide range of updates and…
This Go program applies a lifetime patch to PowerShell to disable ETW (Event Tracing for…
GPOHunter is a comprehensive tool designed to analyze and identify security misconfigurations in Active Directory…
Across small-to-medium enterprises (SMEs) and managed service providers (MSPs), the top priority for cybersecurity leaders…
The free and open-source security platform SecHub, provides a central API to test software with…
Don't worry if there are any bugs in the tool, we will try to fix…