mirror of
https://github.com/winterknife/EVENSTAR
synced 2026-06-21 14:13:51 +00:00
135 lines
5.6 KiB
C++
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 |