tl;dr
- At least one commercial post exploitation framework is using Vectored Exception Handling (VEH) on Microsoft Windows to hook functions in an attempt subvert hooking detection.
- e.g. We recently released a Copy on Write Detector which is capable of detecting patches to
EtwEventWritevia traditional memory patching methods without the need for byte comparison.
- e.g. We recently released a Copy on Write Detector which is capable of detecting patches to
- NCC Group researched a way to enumerate and identify anomalous and/or potentially malicious VEH handlers present on a host.
- We have documented our approach and shared example code which is capable of identifying:
- if a process is using VEH or not
- which handlers are present and which modules they point to
- highlight if they don’t point to a known module
Prior Work
This research was aided significantly by the prior work of others:
- Dimitri Fourny’s post Dumping the VEH in Windows 10 [32 bit] from June 2020
- this work is excellent but due to pointer encoding changes and structure changes we were required to revisit parts for x64.
- rinse and REpeat and their PEB Structure 64-bit
- Geoff Chappell’s documentation of the Cross-Process Flags in the PEB
Problem Statement
In short we want to be able to:
- Enumerate which processes are using VEH
- Identify how many VEH handlers are present
- Identify which loaded modules those VEH handlers are serviced by
Malicious VEH Usage
VEH use is a well known technique in the gaming community as a means of bypassing integrity checking code used by games publishers. As such some in the cyber research community have adopted it as a technique for similar reasons.
An example malicious VEH installation would look something akin to:
Our objective is to detect such use.
Solution
VEH using Process Enumeration
Using the Process Environment Block (PEB) there is the CrossProcessFlags bit mask which provides an indicator if a process is or is not using VEH.
To enumerate if a process is using VEH we:
- Open a handle to the process via
OpenProcess - use
NtQueryInformationProcessto get process basic information which includePebBaseAddress - use
ReadProcessMemoryto read the PEB and check theCrossProcessFlagsbit mask for0x4(VEH)
This provides a performant way to do initial process triage.
VEH Handler and Handling Module Enumeration
As documented by others previously the VEH handlers are stored in a doubly linked list. The address for the start of the doubly linked list is in an unexported variable called LdrpVectorHandlerList in ntdll.dll. The address LdrpVectorHandlerList is obtainable from symbols or using a heuristic on RtlpCallVectoredHandlers without.

Once we have the address of LdrpVectorHandlerList we walk the list and its associated structure. It was at this point sadly that Dimitri’s excellent prior work did not translate to Windows 10 x64.
The structure we ended up using was:
#pragma pack(2)
struct VEH_HANDLER_ENTRY
{
LIST_ENTRY Entry;
PVOID UnKwn1;
PVOID UnKwn2;
PVOID VectoredHandler3;
};
With this structure we are able to:
- use
ReadProcessMemoryusing the address ofLdrpVectorHandlerList - walk the doubly linked list extracting
VectoredHandler3from eachVEH_HANDLER_ENTRY - decode
VectoredHandler3– it is encoded viaEncodePointer– to get the handler’s virtual address- note: this was done using
NtQueryInformationProcessand the undocumentedProcessCookieclass (thank you Process Hacker team) as opposed toDecodeRemotePointer
- note: this was done using
- For each of the decoded pointers then identify via
EnumProcessModulesandGetModuleInformationwhich modules (i.e. executables or libraries) each of the handlers point to.
With this done we are able to enumerate both which processes are or are not using VEH.
But also how many handlers are present for a particular process and which modules are serving them.
Identifying Anomalous or Malicious Handlers
VEH use doesn’t seem overly common in the unscientific sample size of one host. But what does an anomalous or malicious handler look like in the output? To test the concept we used the following as a cartoon:
Using our approach we can clearly see the last ‘mal’ handler points to a region of memory which isn’t hosted by a DLL or EXE.

Further work can be done here to ensure that the exception handler pointers reside in the .text section and that the section is integral.
Summary and Conclusions
In this post we have documented the appeal of VEH use on Windows to avoid certain hooking detection techniques. We have also shown VEH use is easy to detect due to a low volume of usage coupled with high signal indicators when anomalously used – all in a performant manner.
We encourage EDR, telemetry and other observability vendors to integration these enumeration techniques into their Windows product lines to facilitate detection by cyber defense teams.
Code
The example code can be obtained from here.
However please note it is only example code , it is only known to work on Windows 10 x64
Footnotes
Some footnotes associated with the project.
x64dbg
The x64dbg team have support for dumping the VEH – however it produces incorrect pointers on x64 Windows 10 – we reported it and shared our code.
RtlpLogExceptionHandler
It was noted RtlpCallVectoredHandlers contains a call to RtlpLogExceptionHandler

It was hypothesized that this might provide a further source of telemetry / forensic artifacts about which handlers have recently been called even if the handlers were no longer present. However it was unclear how it would be retrieved. In speaking to Alex Ionescu it transpires it first needs to be enabled via gflags
After only which it can be obtained via !exrlog in WinDbg


