Kernel-Bridge icon indicating copy to clipboard operation
Kernel-Bridge copied to clipboard

KbTriggerCopyOnWrite BSOD immediately sample

Open Tai7sy opened this issue 5 years ago • 2 comments

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.

Tai7sy avatar Sep 21 '20 08:09 Tai7sy

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.

HoShiMin avatar Sep 21 '20 16:09 HoShiMin

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.

Tai7sy avatar Sep 21 '20 17:09 Tai7sy