This repository contains the source code for an advanced implementation of the VEH² (Vectored Exception Handling²) technique. This tool functions as a sophisticated loader that can execute .NET assemblies directly from memory, while bypassing the Anti-Malware Scan Interface (AMSI) without patching the AmsiScanBuffer
function.
- Patchless AMSI Bypass: Utilizes a double Vectored Exception Handler (VEH) setup to intercept and neutralize AMSI scans at runtime.
- .NET CLR Hosting: Hosts the Common Language Runtime (CLR) from a native C++ process, allowing it to load and manage .NET code.
- In-Memory Assembly Execution: Reads a target .NET assembly from disk into a memory buffer and executes it from there, avoiding direct execution from the file that resides on disk.
- Stealthy Module Loading: Forces
amsi.dll
into the process's address space by loading a sacrificial, embedded dummy assembly. This allows the tool to find theAmsiScanBuffer
address without making a potentially monitoredLoadLibrary
call.
-
CLR and AMSI Initialization:
- The program first hosts the .NET CLR.
- It then loads a tiny, embedded sacrificial .NET assembly from a byte array. The sole purpose of this action is to trigger the CLR to load
amsi.dll
into the process. - With
amsi.dll
now in memory, the program dynamically resolves the address ofAmsiScanBuffer
usingGetModuleHandle
. - Two Vectored Exception Handlers (VEHs) are registered: VEH1 for
EXCEPTION_BREAKPOINT
and VEH2 forEXCEPTION_SINGLE_STEP
.
-
Arming the Bypass:
- The program intentionally triggers a breakpoint using
DebugBreak()
. - This invokes VEH1, which sets a hardware breakpoint on the
AmsiScanBuffer
function's address by modifying the debug registers (Dr0-Dr7).
- The program intentionally triggers a breakpoint using
-
Triggering the Bypass:
- The user-specified .NET assembly is read from disk into a memory buffer.
- The program instructs the CLR to load this assembly from the memory buffer.
- As the CLR prepares to execute the code, it first attempts to scan the memory buffer for malicious content by calling
AmsiScanBuffer
. - The instant the CPU tries to execute
AmsiScanBuffer
, the hardware breakpoint is triggered, raising anEXCEPTION_SINGLE_STEP
. - This exception invokes VEH2.
-
The Bypass Execution:
- Inside VEH2, the magic happens:
- The hardware breakpoint is cleared to prevent an infinite loop.
- The return value of
AmsiScanBuffer
(in theRAX
register) is set toS_OK
(0). - The
amsiResult
parameter on the stack is set toAMSI_RESULT_CLEAN
. - The instruction pointer (
RIP
) is manipulated to skip the entireAmsiScanBuffer
function, jumping directly to its return address.
- Execution continues as if
AmsiScanBuffer
had run and found nothing malicious. The user's assembly is then executed.
- Inside VEH2, the magic happens:
This method is considered "patchless" because it doesn't alter the code of AmsiScanBuffer
itself, making it potentially stealthier than traditional patching techniques.
This project is configured to be built with the Microsoft Visual C++ compiler (cl.exe
) and nmake
. You will need to have the Visual Studio Build Tools or a full Visual Studio installation with the C++ toolchain.
Open a developer command prompt (like the "x64 Native Tools Command Prompt for VS") and run:
nmake
This will create a build
directory and place the VEH2_Release.exe
executable inside it.
To remove all build artifacts, run:
nmake clean
Execute the compiled program, passing the path to the .NET assembly you wish to load as an argument.
.\build\VEH2_Release.exe C:\path\to\your\assembly.exe
The core bypass technique was described in detail by CrowdStrike: CrowdStrike Blog: Investigating the Threat of Patchless AMSI Bypass Attacks