diff --git a/volatility3/framework/automagic/pdbscan.py b/volatility3/framework/automagic/pdbscan.py index 729c48063..f9c0d853d 100644 --- a/volatility3/framework/automagic/pdbscan.py +++ b/volatility3/framework/automagic/pdbscan.py @@ -376,8 +376,70 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface): valid_kernel = (virtual_layer_name, address, res[0]) return valid_kernel + def method_low_stub_offset( + self, + context: interfaces.context.ContextInterface, + vlayer: layers.intel.Intel, + progress_callback: constants.ProgressCallback = None, + ) -> Optional[ValidKernelType]: + # This method is only valid for x64 systems + if not isinstance(vlayer, intel.Intel32e): + return None + kernel_hint = 0 + kernel_base = 0 + physical_layer = context.layers.get("memory_layer") + + # Try locating kernel base via x64 Low Stub in lower 1MB starting from second page (4KB) + # If "Discard Low Memory" setting is disabled in BIOS, the Low Stub may be at the third/fourth or further pages + for offset in range(0x1000, 0x100000, 0x1000): + jmp_and_completion_values = int.from_bytes( + physical_layer.read(offset, 0x8), "little" + ) + if ( + 0xFFFFFFFFFFFF00FF & jmp_and_completion_values + != constants.windows.JMP_AND_COMPLETION_SIGNATURE + ): + continue + cr3_value = int.from_bytes( + physical_layer.read( + offset + constants.windows.PROCESSOR_START_BLOCK_CR3_OFFSET, 0x8 + ), + "little", + ) + + # Compare previously observed valid page table address that's stored in vlayer._initial_entry + # with PROCESSOR_START_BLOCK->ProcessorState->SpecialRegisters->Cr3 + # which was observed to be an invalid page address, so add 1 (to make it valid too) + if (cr3_value + 1) != vlayer._initial_entry: + continue + potential_kernel_hint = int.from_bytes( + physical_layer.read( + offset + constants.windows.PROCESSOR_START_BLOCK_LM_TARGET_OFFSET, + 0x8, + ), + "little", + ) + if 0x3 & potential_kernel_hint: + continue + kernel_hint = potential_kernel_hint & 0xFFFFFFFFFFFF + kernel_base = kernel_hint & (~0x1FFFFF) & 0xFFFFFFFFFFFF + break + + if kernel_base: + # Scanning 32mb in 2mb chunks for the 'ntoskrnl' base address + while (kernel_base + 0x2000000) > kernel_hint: + for i in range(0, 0x200000, 0x1000): + valid_kernel = self.check_kernel_offset( + context, vlayer, kernel_base, progress_callback + ) + if valid_kernel: + return valid_kernel + kernel_base -= 0x200000 + return None + # List of methods to be run, in order, to determine the valid kernels methods = [ + method_low_stub_offset, method_kdbg_offset, method_module_offset, method_fixed_mapping, diff --git a/volatility3/framework/constants/windows/__init__.py b/volatility3/framework/constants/windows/__init__.py index 7face984a..6f37acd2d 100644 --- a/volatility3/framework/constants/windows/__init__.py +++ b/volatility3/framework/constants/windows/__init__.py @@ -10,3 +10,21 @@ KERNEL_MODULE_NAMES = ["ntkrnlmp", "ntkrnlpa", "ntkrpamp", "ntoskrnl"] """The list of names that kernel modules can have within the windows OS""" PE_MAX_EXTRACTION_SIZE = 1024 * 1024 * 256 + +""" +The following constants represent the layout of the Low Stub which exists only on x64 machines with no virtualization/emulation, +responsible for transitioning from Real Mode(16 bit) to Protected Mode(32 bit) and Long Mode(64 bit) on boot/return from sleep. +Contains offsets to fields and structures within the undocumented structure _PROCESSOR_START_BLOCK. +Here's a reference: https://github.com/mic101/windows/blob/master/WRK-v1.2/base/ntos/inc/amd64.h#L3334 +""" +# Expected signature for validation, constructed from: +# PROCESSOR_START_BLOCK->Jmp->OpCode | PROCESSOR_START_BLOCK->Jmp->Offset | PROCESSOR_START_BLOCK->CompletionFlag +JMP_AND_COMPLETION_SIGNATURE = 0x00000001000600E9 + +# Address of LmTarget (Long Mode target) +PROCESSOR_START_BLOCK_LM_TARGET_OFFSET = ( + 0x70 # PROCESSOR_START_BLOCK->LmTarget, PVOID 8 bytes +) + +# CR3 register within structures describing initial processor state to be started +PROCESSOR_START_BLOCK_CR3_OFFSET = 0xA0 # PROCESSOR_START_BLOCK->ProcessorState->SpecialRegisters->Cr3, ULONG64 8 bytes