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
);
MdlMappingNoExecute
和 MdlMappingNoWrite
标志仅在 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) 过高。这通常是
由驱动程序使用不正确的地址引起。)
请注意,如果 subrange 是 large 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
);
}
本质上,我正在寻找一个可以为内核模式做的功能,VirtualProtect
为用户模式做的事情。
我正在使用以下简化代码举例说明的逻辑分配内存。
PMDL mdl = MmAllocatePagesForMdl
(
LowAddress,
HighAddress,
SkipAddress,
size
);
ULONG flags = NormalPagePriority | MdlMappingNoExecute | MdlMappingNoWrite;
PVOID ptr = MmGetSystemAddressForMdlSafe
(
mdl,
flags
);
MdlMappingNoExecute
和 MdlMappingNoWrite
标志仅在 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) 过高。这通常是 由驱动程序使用不正确的地址引起。)
请注意,如果 subrange 是 large 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
);
}