背景
在内核里想要写入“别人的”内存(一般指 NTOS 等系统模块的内存空间),需要遵守一个规则 : IRQL和内存保护。
- IRQL称为中断请求级别,从0~31 共32个级别
- 内存保护可以打开和关闭,如果在内存处于保护状态时写入,会导致蓝屏
一般来说,要写入“别人的”内核内存,必须关闭内存写保护,并把 IRQL提升到 2 才行(绝大多数候时候 IRQL 都为 0 ,当IRQL=2时,会阻断大部分线程执行,防止执行出错)。
内存是否处于写保护的状态记录在 CR0 寄存器上,因此直接修改CR0寄存器的值即可。而提升或降低IRQL则使用KeRaiseIrqlToDpcLevel和KeLowerIrql实现(WIN64的IRQL值记录在CR8寄存器上,而WIN32的IRQL值记录在KPCR上)。
代码示例
// 需要 #include <intrin.h>
// write protect off
KIRQL WPOFFx64()
{
// 提高中断请求优先级到 DISPATCH_LEVEL 级别
KIRQL irql = KeRaiseIrqlToDpcLevel();
// 获取 cr0 寄存器中的值(cr0 控制 cpu 的操作模式)
UINT64 cr0 = __readcr0();
// 修改写保护
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
// 禁用中断
_disable();
return irql;
}
// write protect on
void WPONx64(KIRQL irql)
{
// 获取 cr0 中的值
UINT64 cr0 = __readcr0();
// 恢复写保护
cr0 |= 0x10000;
__writecr0(cr0);
// 恢复中断
_enable();
// 恢复 IRQL 到原始值
KeLowerIrql(irql);
}