前言
在开始讲解本文章技术之前,我们需要先了解什么是内存属性以及VEH。
内存属性(Memory Protection Attributes)用于控制进程对内存页的访问权限,防止非法访问和保护数据安全。就像是给每块内存加上不同的“门禁”,它的作用是防止程序随意读取、修改或执行不该访问的内存,从而提高安全性,避免程序崩溃或被恶意攻击。
VEH全称(Vectored Exception Handling),中文名向量异常处理,简单来说就是程序执行的时候报错了如果设置了异常处理那就会走到里面。
本文将演示如何去通过修改内存属性来加载shellcode,让杀软无法读取shellcode执行的那片内存空间,但我们能通过VEH来接管这篇内存空间让我们的shellcode执行。
内存属性
基本属性
属性 描述 PAGE_NOACCESS 不能读取、写入或执行该页内容,访问会导致访问冲突(Access Violation)。 PAGE_READONLY 只能读取该页内容,尝试写入会导致访问冲突。 PAGE_READWRITE 可以读取和写入该页内容。 PAGE_WRITECOPY 允许读,写入时会触发 写时复制(Copy-on-Write) 机制,分配新页。 PAGE_EXECUTE 只能执行该页内容,不能读取或写入。 PAGE_EXECUTE_READ 可以执行和读取该页内容,但不能写入。 PAGE_EXECUTE_READWRITE 可以执行、读取和写入该页内容。 PAGE_EXECUTE_WRITECOPY 允许执行和读取,写入时触发写时复制机制。
特殊属性(可与基本属性组合)
属性 描述 PAGE_GUARD 使页面成为“保护页 ”,首次访问时会触发异常,之后 PAGE_GUARD 标志被移除。 PAGE_NOCACHE 禁用 CPU 缓存,直接访问物理内存,通常用于硬件交互。 PAGE_WRITECOMBINE 允许写入合并,提高写入性能,常用于 GPU 显存映射。
我们主要关注的是PAGE_GUARD属性。PAGE_GUARD是内存属性里面的一种,他的作用是,当你访问一个没有被分配的内存时候,会触发一个异常,这个异常可以被我们捕获,然后我们可以在异常处理函数中,对这个内存进行分配,然后返回到原来的地方继续执行。
原理
在读取shellcode的时候,我们将shellcode的那片内存空间设置为不可访问,当访问这块内存的时候就会报错,因为设置了VEH就会走到VEH里面,VEH将这块内存的属性取消不可访问,然后shellcode在继续去执行就不会报错了,执行完之后继续修改内存属性为不可访问,循环往复。
代码实现
我们需要先定义一个全局变量,这个全局变量会在异常处理函数中使用,用于知道是哪些需要保护的区域触发了异常
VOID* bufferAddress; DWORD bufferSize;
然后开始构建我们需要使用到的函数
PageGuardMemory(): 这个函数将一个地址空间页面标记为guarded,以便当程序尝试读写该页面时触发硬件异常。它首先使用VirtualQuery()函数查询给定地址所在的页面信息、设置PAGE_GUARD标志并调用VirtualProtect()函数将该页面标记为guarded。
UnPageGuardMemory(): 这个函数将一个页面标记为非guarded,调用和‘PageGuardMemory()`类似,但是它将PAGE_GUARD标志从保护权限中移除。
VectoredExceptionHandler(): 此功能作为VEH的入口点,其目的是监听发生在用户代码内的异常。如果错误代码为EXCEPTION_GUARD_PAGE,则此函数将将执行点标记为单步操作标志;如果错误代码为EXCEPTION_SINGLE_STEP,则在发生单步异常后回到用户程序中,而VEH会检查当前异常是否与某些已被标记为guarded的页面有关。如果是,它将解除页面保护,使得进程继续执行。
注册异常处理。通过AddVectoredExceptionHandler函数设置异常走向我们的VEH函数。
我们可以看一下这个函数的定义。这个函数的作用就是添加VEH,简单说就是指定报错后处理的函数。
First 这个参数是确定调用优先级的,将我们的函数优先级设为1,优先调用我们的VEH函数。
Handler 异常处理的函数指针, 这里将其指向我们的VEH函数
然后修改shellcode加载函数,在加载shellcode之前将其地址设置成受保护状态。
这样我们就实现了通过动态修改内存属性对 Shellcode 所在内存区域进行保护,并借助VEH机制捕获访问异常,在触发异常时临时解除内存限制以执行代码,执行后立即恢复内存保护状态。该方案通过隐式内存权限切换和异常接管流程,有效规避了杀毒软件对敏感内存区域的直接扫描与检测。