Windows 7 内核模式下如何设置内存区域保护

How to set memory region's protection in kernel mode under Windows 7

本质上,我正在寻找一个可以为内核模式做的功能,VirtualProtect 为用户模式做的事情。

我正在使用以下简化代码举例说明的逻辑分配内存。

    PMDL mdl = MmAllocatePagesForMdl    
    (
        LowAddress,
        HighAddress,
        SkipAddress,
        size
    );

    ULONG flags = NormalPagePriority | MdlMappingNoExecute | MdlMappingNoWrite;
    PVOID ptr = MmGetSystemAddressForMdlSafe
    (
        mdl, 
        flags
    );

MdlMappingNoExecuteMdlMappingNoWrite 标志仅在 Win8+ 上有效。
此外,仅使用 MmGetSystemAddressForMdlSafe 我无法为内存区域分配 NoAccess 保护。

是否有任何额外的或替代的 API-s 我可以使用,以便我可以修改分配内存的页面保护?
hack 也可以,因为目前此功能不会在生产代码中使用。

C:\Windows\System32>dumpbin /exports ntdll.dll | find "Protect"
        391  17E 0004C030 NtProtectVirtualMemory
       1077  42C 000CE8F0 RtlProtectHeap
       1638  65D 0004C030 ZwProtectVirtualMemory

我认为您可以从内核模式调用 Zw 函数,并且参数通常与相应的 Nt 函数相同。虽然 ZwProtectVirtualMemory 没有记录,但有一个记录的 ZwAllocateVirtualMemory 接受保护标志。

另一种方法可能是在用户模式下分配和保护虚拟内存,将缓冲区传递给您的驱动程序,然后在那里创建相应的 MDL。

我目前最终使用的代码如下。
所有使用的API都是官方的。
在这里,我为分配内存的 subrange 创建另一个 mdl 并更改该子范围的保护。

如果您绊倒了使用以下方法保护的内存,那么:

  • IRQL < DISPATCH_LEVEL 你会得到 PAGE_FAULT_IN_NONPAGED_AREA 错误 (引用了无效的系统内存。这不能被 try-except 保护, 它必须由探测器保护。通常这个地址很糟糕或者它 指向释放的内存。)
  • IRQL == DISPATCH_LEVEL你会得到 DRIVER_IRQL_NOT_LESS_OR_EQUAL 故障 (试图访问位于一个可分页(或完全无效)的地址 中断请求级别 (IRQL) 过高。这通常是 由驱动程序使用不正确的地址引起。)

请注意,如果 subrangelarge page 分配的一部分,更改保护可能会失败。然后 status 将很可能 STATUS_NOT_SUPPORTED
如果最初分配的内存区域的大小和对齐方式(取决于SkipAddress中的变量,则可能会发生分配这个问题)是合适的,并且满足了一些我不熟悉的附加前提条件(可能从某些 OS 版本开始)。

        PMDL guard_mdl = IoAllocateMdl
        (
            NULL, 
            PAGE_SIZE * guardPageCount, 
            FALSE,           
            FALSE,  
            NULL        
        );

        if (guard_mdl)
        {
            IoBuildPartialMdl
            (
                mdl,    
                guard_mdl,  
                (PVOID)(0),  // **offset** from the beginning of allocated memory ptr
                PAGE_SIZE * guardPageCount
            );

            status = MmProtectMdlSystemAddress
            (
                guard_mdl,
                PAGE_NOACCESS
            );
        }