Files
winterknife-EVENSTAR/PagingDbgExt/Src/GetKernelDirectoryTableBase.cpp
2025-05-18 15:44:46 -04:00

135 lines
5.6 KiB
C++

// ========================================================================
// File: GetKernelDirectoryTableBase.cpp
//
// Author: winterknife
//
// Description: Source file that contains the necessary routines to find the
// kernel's DirectoryTableBase using an arbitrary physical memory read primitive
// to locate the Low Stub with heuristic scanning
//
// Modifications:
// 2025-05-13 Created
// 2025-05-18 Updated
// ========================================================================
// ========================================================================
// Includes
// ========================================================================
#include "../Inc/GetKernelDirectoryTableBase.h"
#include "../Inc/DbgExt.h"
// ========================================================================
// Routines
// ========================================================================
#pragma region ROUTINES
_Use_decl_annotations_
VOID __stdcall get_kernel_dtb(
HANDLE hCurrentProcess,
HANDLE hCurrentThread,
QWORD qwCurrentPc,
DWORD dwProcessor,
PCSTR strArgs
) {
UNREFERENCED_PARAMETER(hCurrentProcess);
UNREFERENCED_PARAMETER(hCurrentThread);
UNREFERENCED_PARAMETER(qwCurrentPc);
UNREFERENCED_PARAMETER(dwProcessor);
UNREFERENCED_PARAMETER(strArgs);
// Init local variables
PVOID pBuffer = NULL;
DWORD dwActiveProcessorCount = 0;
QWORD qwPhysicalAddress = 0;
DWORD dwBytesRead = 0;
PPROCESSOR_START_BLOCK pProcessorStartBlock = NULL;
QWORD qwKernelDirectoryTableBase = 0;
// Determine if the target uses 64-bit pointers
if (!IsPtr64()) {
dprintf("[-] IA-32 target is not supported by this extension!\n");
goto cleanup;
}
// Allocate 0x1000 (PAGE_SIZE_4KB) bytes worth of heap memory to read the Low Stub
pBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
if (pBuffer == NULL) {
dprintf("[-] Error allocating heap memory!\n");
goto cleanup;
}
// This technique of finding the Low Stub doesn't work if the number of Application Processors (APs) == 1
// This happens because the nt!_PROCESSOR_START_BLOCK structure is filled incorrectly in this case
// Get nt!_KUSER_SHARED_DATA.ActiveProcessorCount
if (!ReadMemory((KUSER_SHARED_DATA_RO_MAPPING_X64_KVA + ACTIVEPROCESSORCOUNT_OFFSET), &dwActiveProcessorCount, 0x04, NULL)) {
dprintf("[-] Error reading virtual memory!\n");
goto cleanup;
}
// Check nt!_KUSER_SHARED_DATA.ActiveProcessorCount
if (dwActiveProcessorCount < 2) {
dprintf("[-] This technique does not work for UniProcessor (UP) systems!\n");
goto cleanup;
}
// Locate the Low Stub with heuristic scanning
// The Low Stub is always present on x86 APIC systems in one of the physical pages in the range starting at 0x1000 (4 KB) and less than 0x100000 (1 MB)
// It is a piece of 16-bit code that aids in CPU mode transitions (Real Mode to Protected Mode to Long Mode) on boot or waking up from S2 and S3 sleeps
// nt!HalpLowStub contains the KVA of nt!_PROCESSOR_START_BLOCK structure
// nt!HalpLowStubPhysicalAddress contains the PA of nt!_PROCESSOR_START_BLOCK structure
for (qwPhysicalAddress = 0x1000; qwPhysicalAddress < 0x100000; qwPhysicalAddress += 0x1000) {
ReadPhysical(qwPhysicalAddress, pBuffer, 0x1000, &dwBytesRead);
if (dwBytesRead != 0x1000) {
dprintf("[-] Error reading physical memory!\n");
goto cleanup;
}
// Low Stub is the nt!_PROCESSOR_START_BLOCK structure
pProcessorStartBlock = (PPROCESSOR_START_BLOCK)pBuffer;
// nt!_PROCESSOR_START_BLOCK structure starts with the following bytes decoded as the JMP rel16 instruction:
// E9 4D 06 jmp 0x650
// This piece of 16-bit x86 code jumps to the end of the nt!_PROCESSOR_START_BLOCK structure
// The size of this structure is 0x650/0n1616 bytes starting from Windows 10 1703 Creators Update (RS2) Build 15063
if (pProcessorStartBlock->Jmp.OpCode != 0xE9)
continue;
if (pProcessorStartBlock->Jmp.Offset != 0x06)
continue;
// nt!_PROCESSOR_START_BLOCK.CompletionFlag should be set when the Application Processor (AP) has been initialized
if (pProcessorStartBlock->CompletionFlag != 1)
continue;
// nt!_PROCESSOR_START_BLOCK.LmTarget should point to nt!HalpLMStub
if (pProcessorStartBlock->LmTarget == 0)
continue;
// nt!_PROCESSOR_START_BLOCK.ProcessorState.SpecialRegisters.Cr3 should not be zero
if (pProcessorStartBlock->ProcessorState.SpecialRegisters.Cr3 == 0)
continue;
// Found!
dprintf("[+] nt!_PROCESSOR_START_BLOCK structure found at PA=0x%I64X\n", qwPhysicalAddress);
// Get the base address of the HAL Heap which is pointed to by nt!HalpOriginalHeapStart
dprintf("[+] HAL Heap base KVA=0x%I64X\n", ((QWORD)(pProcessorStartBlock->SelfMap) & 0xFFFFFFFFF0000000));
// Get the physical address of the first table that is used in virtual address translation for KVAS contained in CR3
qwKernelDirectoryTableBase = pProcessorStartBlock->ProcessorState.SpecialRegisters.Cr3;
dprintf("[+] nt!_PROCESSOR_START_BLOCK.ProcessorState.SpecialRegisters.Cr3=0x%I64X\n", qwKernelDirectoryTableBase);
break;
}
// Cleanup
cleanup:
if (pBuffer)
HeapFree(GetProcessHeap(), 0, pBuffer);
return;
}
#pragma endregion