KbTriggerCopyOnWrite BSOD immediately sample
debug_me.exe is a simple application which call MessageBoxA when button clicked.
Now we use KbWriteProcessMemory with TriggleCoW to user32.MessageBoxA like this:
VOID BSOD_Test() {
WdkTypes::PEPROCESS Process;
DWORD ProcessId = GetProcessIdByName(TEXT("debug_me.exe")); // A wow64 process
PVOID Address = (PVOID)0x76311F70; // user32.MessageBoxA
Processes::Descriptors::KbGetEprocess(ProcessId, &Process);
printf("MessageBoxA: VA:%p, PA:0x%I64X\n", Address, GetPhysAddr(Process, Address));
{
BYTE Buffer[1] = { 0 };
BOOL Status = Processes::MemoryManagement::KbReadProcessMemory(ProcessId, (WdkTypes::PVOID)Address, Buffer, 1);
printf("MessageBoxA: KbReadProcessMemory: 0x%02X\n", Buffer[0]);
}
{
BYTE* NewBuffer = new BYTE[1];
NewBuffer[0] = 0xC3;
BOOL Status = Processes::MemoryManagement::KbWriteProcessMemory(ProcessId, (WdkTypes::PVOID)Address, NewBuffer, 1, TRUE);
delete[] NewBuffer;
printf("MessageBoxA: KbWriteProcessMemory: %d\n", Status);
printf("MessageBoxA: PA:0x%I64X\n", GetPhysAddr(Process, Address));
}
Processes::Descriptors::KbDereferenceObject(Process);
}
The debug_me.exe will crash obviously because the user32.MessageBoxA is changed to 0xC3 and caused some stack error.
Then it will cause immediately BSOD.
QUOTA_UNDERFLOW (21)
This bugcheck occurs if a kernel component mishandles quota charges and
returns more quota than was previously charged to a particular quota block.
Arguments:
Arg1: ffffc9872b1ee080, The process (if any) that was initially charged.
Arg2: 0000000000000002, The quota type in question (paged pool, nonpaged pool, etc.)
Arg3: ffffffffffffffff, The initial charge amount to return.
Arg4: fffffffffffae8bd, The remaining (unreturned) charge.
------------------
os:
Windows 10 1809
stack:
[0x4] nt!PspReturnQuota + 0x180085
[0x5] nt!PsReturnProcessPageFileQuota + 0x25
[0x6] nt!MiReturnFullProcessCharges + 0x4b
[0x7] nt!MiRemoveVadCharges + 0xab
[0x8] nt!MiFinishVadDeletion + 0xf1
[0x9] nt!MiDeleteVad + 0x15f2
[0xa] nt!MiUnmapVad + 0x49
[0xb] nt!MiCleanVad + 0x30
[0xc] nt!MmCleanProcessAddressSpace + 0x113
[0xd] nt!PspRundownSingleProcess + 0x129
[0xe] nt!PspProcessRundownWorkerSingle + 0x32
[0xf] nt!ExpWorkerThread + 0x16a
[0x10] nt!PspSystemThreadStartup + 0x55
[0x11] nt!KiStartSystemThread + 0x1c
Since the KbTriggerCopyOnWrite will still take some minnutes/hours to cause a BSOD, which meen it difficult to debug.
This maybe helpful to find the problem.
Thanx a lot for this sample! I'm in developing of the Kernel-Bridge 2 - a fully rewritten implementation. And I'll check your code there a bit later. But it seems that the best solution is to use ZwProtectVirtualMemory.
I thought the SEC_NOCHANGE would cause the ZwProtectVirtualMemory to failed, which is used by some game or game protector to map ntdll to avoid being hooked.
But in fact, there should be no impact. If some memory reagion was created with SEC_NOCHANGE, then the COW flag in the pte will be meaningless, (At least it's not a memory created/shared by the system, and it won't cause a system crash. ) So we don't need to trigger cow on this kind of memory region.