MineSweeper
Windows user-land hooks manipulation tool.
Highlights
- Supports any x64/x86 Windows DLL (actually, any x64/x86 Windows PE for that matter)
- 4 main features
- Enumerates loaded modules in the target process (
-l
flag) - Finds user-land hooks in loaded modules (
-s
flag) - Unhooks specified modules (
-u
flag) - Re-hooks specified modules (
-r
flag)
- Enumerates loaded modules in the target process (
- Shows which function RVAs have been modified with byte-to-byte comparison (
-v
flag) - Cross-architecture support for the x64 variant.
- Cautious mode: can unhook itself first before manipulating remote processes (
-c
flag) - Can target either all loaded modules within the target process or only those containing a specified string in their path (
-m
flag) - Lightweight: x64 and x86 binaries are only 18KB and 17KB respectively.
- No Visual C++ Redistributable Packages (
vcruntime140.dll
) dependency. MineSweeper dynamically links to the following Windows core libraries present on every modern distribution:msvcrt.dll
andkernel32.dll
.
Command Line Reference
MineSweeper by @ars3n11
Usage: MineSweeper.exe [-c] [-l | -s | -u | -r] [-t targetPID] [-v]
[-m moduleNameStringMatch] [-d hookDonorPID]
Modes available:
-l List Mode - List loaded modules by the target process (-t).
Module name filter (-m) is available.
-s Sweep Mode - Sweep target PID (-t) for any user-land hooks.
Module name filter (-m) is available.
-u Unhook Mode - Sweep and unhook target PID (-t) from any user-land hooks.
Module name filter (-m) is available.
-r Re-hook Mode - Sweep hook donor PID (-d) for user-land hooks.
If any hooks found - copy them over to our target PID (-t).
Module name filter (-m) is available.
Safety modes:
-c Cautious Mode - Unhook the local process before proceeding with
one of the chosen main modes.
Options:
-t Target PID. Will target the local process if not provided.
-d Hook donor PID (i.e.: the process that will be used to copy hooks FROM).
Will set the local process as the hooks donor if not provided.
-m Filter string to be applied to the loaded module canonical path
(e.g: \Device\HarddiskVolume3\Windows\System32\ntdll.dll).
Will target all modules (same as "-m .dll") if not provided.
-v Verbose flag. Prints modified RVAs and their byte-to-byte comparison for each hooked function.
Examples:
MineSweeper.exe: -l List loaded modules in MineSweeper's own process.
MineSweeper.exe: -l -t 5476 List loaded modules in PID 5476.
MineSweeper.exe: -s Sweep MineSweeper's local process for user-land hooks.
MineSweeper.exe: -s -v Same as above but also print modified RVAs for each hooked function.
MineSweeper.exe: -s -t 5476 Sweep PID 5476 for user-land hooks.
MineSweeper.exe: -u -t 5476 Unhook PID 5476 from all user-land hooks.
MineSweeper.exe: -c -u -t 5476 Unhook PID 5476 from all user-land hooks. Run in Cautious mode (unhook
MineSweeper's own process before trying to unhook PID 5476).
MineSweeper.exe: -u -t 5476 -m ntdll.dll Unhook PID 5476 from any hooks found in the ntdll.dll module.
MineSweeper.exe: -r -t 5476 -d 8156 Sweep PID 8156 for user-land hooks and copy over any discovered
hooks into the matching modules in the PID 5476.
MineSweeper.exe: -c -r -t 5476 -d 8156 Same as above but run in Cautious mode (unhook MineSweeper's
own process before doing anything else).
Cross-architecture
x64 version of MineSweeper can enumerate and manipulate both x64 and x86 processes. This only applies to the x64 version since a call to EnumProcessModulesEx
function from an x86 process will return x86 module handles only.
Cross-architecture support:
x86 | x64 | |
---|---|---|
x86 | Yes | No |
x64 | Yes | Yes |
Dependencies
TLDR: nothing to worry about, you can clone the repo and go straight to compiling.
- Imports a total of 28 functions from
msvcrt.dll
andkernel32.dll
. - Links to
msvcrt.dll
to avoid Visual C++ Redistributable Packages (vcruntime140.dll
) dependency.
msvcrt.dll
Linking to I wanted to link MineSweeper to msvcrt.dll
in order to avoid C++ Redistributable Packages
dependency for C runtime. I first looked at Benjamin Delpi's approach used in Mimikatz. That looked too complex for the task at hand and after a few more nights of research I came across Mahmoud Al-Qudsi's elegant msvcrt.lib project which is what we are using here.
For convenience, I included the msvcrt.lib
files in this project already so you don't need to pull them twice. They are located under libs/msvcrt/
.
Compiling
Download compiled binaries here.
Since opening 3rd party Visual Studio project files is mauvais ton these days, I'm providing command line compilation instructions below. The VS project files are also included in the repo, so that's always an option too.
Compiling in CLI
Step 1: Compile (make sure to use the right cl.exe
for your target architecture!).
cl.exe /GS- /GL /W4 /O1 /nologo /Zl /Os /Oi /c /D "_UNICODE" /D "UNICODE" MineSweeper.c MineSweeperCore.c
Step 2: Link - x64:
link.exe /LTCG /ENTRY:"wmain_custom" /OPT:REF /SAFESEH:NO /SUBSYSTEM:CONSOLE /NODEFAULTLIB /MACHINE:X64 /OUT:"MineSweeper_cmd_compiled.exe" MineSweeper.obj MineSweeperCore.obj libs\msvcrt\x64\msvcrt.lib kernel32.lib
OR
Step 2: Link - x86:
link.exe /LTCG /ENTRY:"wmain_custom" /OPT:REF /SAFESEH:NO /SUBSYSTEM:CONSOLE /NODEFAULTLIB /MACHINE:x86 /OUT:"MineSweeper_cmd_compiled.exe" MineSweeper.obj MineSweeperCore.obj libs\msvcrt\x86\msvcrt.lib kernel32.lib
Exceptions
There are several use cases where MineSweeper will not be able to sweep a specified process module. Whenever it encounters the below listed conditions, MineSweeper will notify the user in stdout
and skip the module.
- A DLL without a
.text
section (e.g.:FileSync.Resources.dll
andFileSync.LocalizedResources.dll
loaded byOneDrive.exe
). - Non-consecutively committed PE sections. The only example I have observed during my testing was
kernel32.dll
in a x86 process running on a x64 system where. The committed memory regions had reserved memory regions in between them preventingReadProcessMemory
function from reading the target module all at once. - Modules with large chunks of
.text
section being overwritten. In my testing I have come across several instances where random parts of a module's.text
section were overwritten resulting in a false positive. I was not able to explain this behavior since it was sporadic and affected different modules each time. Normally, a hooked module would have less than 1% of its.text
section modified so that is what MineSweeper is checking for to avoid this condition.
How it Works
Below is a high-level overview of the unhooking (-u
flag) workflow which also invokes module enumeration (-l
flag) and sweeping for hooks (-s
flag) functions.
- Enumerate target process modules via
EnumProcessModulesEx
function. - Identify modules of interest by their file name / path.
- [If targeting a remote process] Copy over the remote process module to our local process using
ReadProcessMemory
. - Map view of file of the module of interest on disk using
CreateFileMapping
(PAGE_READONLY | SEC_IMAGE_NO_EXECUTE
) andMapViewOfFile
- Byte-to-byte comparison of the
.text
section of the dll saved on disk against the dll module loaded by the target process in memory. During this process, MineSweeper takes a note of the original values currently present in the dll on disk as well as the modified values identified in memory. - [For the
-l
flag only] Cross-reference.text
section differences to specific function ordinals in the PE EAT (portable executable export address table). - Modify remote process target module memory permissions to
PAGE_EXECUTE_READWRITE
usingVirtualProtectEx
in order to overwrite the hooks. - Overwrite the hooks in the target module using
WriteProcessMemory
for a remote process or simply dereferencing the memory pointer for a local process (in order to avoidWriteProcessMemory
call). - Modify the memory permissions back to the original ones using
VirtualProtectEx
.
OpSec Considerations
MineSweeper currently possesses very limited set of evasion techniques. The primary goal of the project was to do required heavy-lifting for hook detection across as many DLLs as possible on a modern Windows system.
Nonetheless, in my testing it was working fine (enumerating hooks and unhooking remote process modules) on systems with cutting-edge enterprise EDRs. This was true so long the MineSweeper's Cautious mode (-c
flag) was on.
Below is a short list of MineSweeper's opsec features:
- The Cautious mode (
-c
flag) will unhook MineSweeper's own process first before attempting to manipulate a remote process. - Creating on-disk file mapping is done with
PAGE_READONLY | SEC_IMAGE_NO_EXECUTE
permissions in order "to avoid triggering multiple image load events". Shout out to @slaeryan for the tip. - Overwriting local process hooks is done by memory address pointer dereference to avoid calling
WriteProcessMemory
function.
Use Cases
While this project was initially conceived as a learning opportunity for Windows API, C and PE format, there are a couple opportunities for a practical application of the project:
- For Blue Team to test their tooling and telemetry against various user-land unhooking techniques.
- For Red Team for situational awareness on a target system.
Troubleshooting
- If a target process crashes after unhooking or re-hooking, try enabling Cautious mode (
-c
flag).