
逆向VEH源码完整指南VehDecryptHeapAsm如何用单步调试实现15字节指令的精准解密【免费下载链接】VoidgateA technique that can be used to bypass AV/EDR memory scanners. This can be used to hide well-known and detected shellcodes (such as msfvenom) by performing on-the-fly decryption of individual encrypted assembly instructions, thus rendering memory scanners useless for that specific memory page.项目地址: https://gitcode.com/gh_mirrors/vo/VoidgateVoidgate 是一个用于绕过 AV/EDR 内存扫描的开源技术项目。它的核心思路是让内存中始终只保留一条明文 x64 汇编指令——通过逆向其 VEH向量异常处理程序源码VehDecryptHeapAsm你会看到它如何借助硬件断点与单步调试机制对每条 15 字节以内的加密指令实现执行前解密、执行后重加密的精准控制。本文带你完整读懂这套机制。一句话理解让内存扫描器永远看不到全貌传统做法是把整段 shellcode 一次性解密后驻留内存AV/EDR 的内存扫描器只需扫描一次内存页就能命中已知特征如 msfvenom 生成的 payload。Voidgate 的思路完全不同内存中存放的是整段 XOR 加密的汇编指令程序安装一个 VEH它像一个隐形调试器在每条指令执行前解密、执行后立刻重新加密对扫描器而言任意时刻抓到这块内存页明文的只是当前 RIP 指向的 15 字节窗口特征匹配彻底失效。 这正是on-the-fly 逐条解密的威力payload 的绝大部分生命周期都处于加密状态。VEH 单步调试机制三步读懂工作流程第 1 步加密 shellcode 落入可执行内存埋下硬件断点程序启动后见voidgate-master/Voidgate/main.cpp第 17–36 行完成三件事用VirtualAlloc申请一段PAGE_EXECUTE_READWRITE内存前后各加 0x20 字节的 padding 作为缓冲垫把 XOR 加密后的 shellcode 拷入其中通过SetHardwareBreakpoint将入口地址写入调试寄存器Dr0见voidgate-master/Voidgate/Voidgate.cpp第 81 行相当于给 CPU 设了一个入口哨兵。当 CPU 首次执行到入口地址时硬件断点立即触发异常控制权进入 VEH。第 2 步VEH 接管异常进入单步执行循环VehDecryptHeapAsmVoidgate.cpp第 10 行是整个技术的心脏。它收到EXCEPTION_SINGLE_STEP异常后做三件事清掉 Dr0——入口哨兵完成使命后续不再依赖硬件断点置位 EFlags 的 TRAP 标志第 21 行EFlags | (1 8)——这是单步调试的关键TRAP 位置 1 后CPU 每执行完 1 条指令就抛一次单步异常控制权回到 VEH边界检查第 24 行——判断 RIP 是否落在 payload 上下界内。若指令实际在 kernel32.dll 等外部模块里执行比如 shellcode 调用了系统 API则直接放行不做加解密。第 3 步解密当前指令、重加密上一条——滚动式局部明文这是最精巧的部分第 32–62 行先用全局变量last_decrypted_asm记住上一轮解密的地址把上条指令重新加密恢复其密文形态再用GetXorKeyIndexForAsm算出对齐的密钥起点解密当前 RIP 指向的指令返回EXCEPTION_CONTINUE_EXECUTION带着 TRAP 标志继续执行下一条。如此循环往复形成一条不断向前滚动的解密窗口。为什么是 15 字节解密窗口的设计细节voidgate-master/Voidgate/Voidgate.h第 7 行定义了MAX_X64_ASM_OPCODE_LEN 15——这是 x64 架构下一条汇编指令的最大长度。每次 VEH 都以 RIP 为起点解密固定 15 字节因此即使遇到长指令如带长位移的movabs也能被完整还原。若当前指令只有 1 字节多解密出的部分只是提前暴露了下一条指令的开头但下一轮迭代中重加密上一条会将其修正回去——滚动机制天然兼容指令长度差异无需动态长度分析。 源码走读GetXorKeyIndexForAsm 的密钥对齐技巧XOR 密钥是循环使用的加密器voidgate-master/XorEncryptPayload/XorEncryptPayload.cpp第 23 行起从密钥首字符开始逐字节异或用尽后循环回开头。解密侧必须精确对齐否则解出来的就是乱码。GetXorKeyIndexForAsmVoidgate.cpp第 94–100 行的解法极其简洁密钥起点 (当前指令地址 − payload 基址) % 密钥长度因为每个字节加密时密钥游标的进度恰好等于它距入口的字节偏移所以一行取模运算就能恢复任意指令对应的密钥位置。这也解释了为什么循环 XOR 密钥是这项技术能按指令粒度精准解密的前提。实战演示msfvenom Voidgate 的完整复现流程官方 POC 的完整步骤用 msfvenom 生成windows/x64/shell_reverse_tcppayload导出为 raw 格式运行XorEncryptPayload.exe对 payload 做 XOR 加密输出 C 数组形式的加密字节把加密字节填入voidgate-master/Voidgate/main.cpp的payload数组并确认密钥与加密器一致第 9 行在攻击机上启动监听如nc -nvlp 443运行Voidgate.exe——VEH 开始逐条解密执行shellcode 最终连回监听端。上图即为 POC 实际运行效果voidgate.exe 进程执行期间配套的进程工具与十六进制编辑器中payload 所在内存区域始终呈现为一段看起来毫无规律的加密字节。⚠️ 局限性哪些场景这套技术不适用项目文档明确列出三个限制对照源码也能一一印证执行速度大幅下降每条指令执行都会触发 VEH 介入每次额外执行约 300 条汇编。msfvenom 这类体积优先、性能其次的 shellcode 可能需要 15 秒以上才能跑完新建线程不继承硬件断点若 shellcode 调用NtCreateThread且入口位于新线程内该线程没有 HWBPVEH 不会对其单步执行payload 会直接失败官方标注为待完善payload 内嵌数据不可直接引用若 shellcode 内部存放字符串或常量并靠固定偏移读取那部分数据执行时仍是密文读取即出错而通过push指令临时压栈的参数则不受影响。因此它最适合初始访问阶段的小型 shellcode如反弹 shell或作为 stage-1 载荷从 C2 下载真正的 payload。✅ 小结Voidgate 用三个经典的 Windows 调试原语组合出了一套指令级滚动解密机制硬件断点 Dr0负责在入口触发第一次异常VEH充当常驻的隐形调试器处理每次单步异常TRAP 标志保证单步循环永不中断15 字节解密窗口 取模密钥对齐保证任意指令都能被精准还原。读懂VehDecryptHeapAsm这 70 行源码你也就掌握了 Windows 异常调试机制——异常记录、上下文结构、EFlags 控制位——最核心的实战用法。【免费下载链接】VoidgateA technique that can be used to bypass AV/EDR memory scanners. This can be used to hide well-known and detected shellcodes (such as msfvenom) by performing on-the-fly decryption of individual encrypted assembly instructions, thus rendering memory scanners useless for that specific memory page.项目地址: https://gitcode.com/gh_mirrors/vo/Voidgate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考