mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-13 05:07:38 +02:00
Layers: Add QEVM architecture fallback detection
This commit is contained in:
@@ -9,7 +9,7 @@ import struct
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces
|
||||
from volatility3.framework.layers import segmented
|
||||
from volatility3.framework.layers import scanners, segmented
|
||||
from volatility3.framework.symbols import intermed
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
@@ -37,11 +37,32 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer):
|
||||
SEGMENT_FLAG_XBZRLE = 0x40
|
||||
SEGMENT_FLAG_HOOK = 0x80
|
||||
|
||||
pci_hole_table = {re.compile(r"^pc-i440fx-\d\.\d$"): (0xc0000000, 0x100000000),
|
||||
re.compile(r"^pc-1440fx-eoan$"): (0xe0000000, 0x100000000),
|
||||
re.compile(r"^pc-q35$"): (0x80000000, 0x100000000),
|
||||
re.compile(r"^microvm$"): (0xc0000000, 0x100000000),
|
||||
re.compile(r"^xen$"): (0xf0000000, 0x100000000)
|
||||
# See https://qemu.readthedocs.io/en/latest/devel/memory.html for more info
|
||||
#
|
||||
# At least the following values could occur for devices using > 3-4 GB RAM:
|
||||
# +--------------------------------+--------------------------------+------------+-------------+
|
||||
# | Architecture | Reference Code | Hole Start | Hole End |
|
||||
# +--------------------------------+--------------------------------+------------+-------------+
|
||||
# | PC i440FX + PIIX "New Default" | qemu/hw/i386/pc_piix.c:98 | 0xc0000000 | 0x100000000 |
|
||||
# | PC i440FX + PIIX "Old Default" | qemu/hw/i386/pc_piix.c:98 | 0xe0000000 | 0x100000000 |
|
||||
# | PC Q35 + ICH9 | qemu/hw/i386/pc_q35.c:141 | 0x80000000 | 0x100000000 |
|
||||
# | MicroVM | qemu/hw/i386/microvm.c:291 | 0xc0000000 | 0x100000000 |
|
||||
# | Xen | qemu/hw/i386/xen/xen-hvm.c:248 | 0xf0000000 | 0x100000000 |
|
||||
# +--------------------------------+--------------------------------+------------+-------------+
|
||||
#
|
||||
# For now, we assume that the parameter max-ram-below-4g is not set, since this parameter influences the size
|
||||
# and location of the memory gap. Deviating hole sizes could eventually be detected for Linux by e.g. scanning
|
||||
# for dmesg entries with a regex like rb'\[mem (0x[0-9a-f]{4,10})-0x[0-9a-f]{4,10}\] available for PCI devices'
|
||||
|
||||
debian_re = r"artful|eoan"
|
||||
|
||||
pci_hole_table = {re.compile(r"^pc-i440fx-([23456789]|\d\d+)\.\d$"): (0xe0000000, 0xc0000000, 0x100000000),
|
||||
re.compile(r"^pc-i440fx-[01].\d$"): (0xe0000000, 0xe0000000, 0x100000000),
|
||||
re.compile(r"^pc-q35-\d.\d$"): (0xe0000000, 0x80000000, 0x100000000),
|
||||
re.compile(r"^microvm$"): (0xe0000000, 0xc0000000, 0x100000000),
|
||||
re.compile(r"^xen$"): (0xe0000000, 0xf0000000, 0x100000000),
|
||||
re.compile(r"^pc-i440fx-" + debian_re + r"$"): (0xe0000000, 0xc0000000, 0x100000000),
|
||||
re.compile(r"^pc-q35-" + debian_re + r"$"): (0xe0000000, 0x80000000, 0x100000000),
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
@@ -65,6 +86,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer):
|
||||
raise exceptions.LayerException(name, 'No QEMU magic bytes')
|
||||
if header[4:] != b'\x00\x00\x00\x03':
|
||||
raise exceptions.LayerException(name, 'Unsupported QEMU version found')
|
||||
vollog.debug("QEVM header found")
|
||||
|
||||
def _read_configuration(self, base_layer: interfaces.layers.DataLayerInterface, name: str) -> Any:
|
||||
"""Reads the JSON configuration from the end of the file"""
|
||||
@@ -160,13 +182,6 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer):
|
||||
self._architecture = self.context.object(self._qemu_table_name + constants.BANG + 'string',
|
||||
offset = index + 4, layer_name = self._base_layer,
|
||||
max_length = section_len)
|
||||
for regex in self.pci_hole_table:
|
||||
if regex.match(self._architecture):
|
||||
self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex]
|
||||
vollog.log(constants.LOGLEVEL_VVVV, f"QEVM archicture detected as: {self._architecture}")
|
||||
break
|
||||
else:
|
||||
vollog.debug(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}")
|
||||
index += 4 + section_len
|
||||
elif section_byte == self.QEVM_SECTION_START or section_byte == self.QEVM_SECTION_FULL:
|
||||
section_id = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long',
|
||||
@@ -217,6 +232,62 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer):
|
||||
else:
|
||||
raise exceptions.LayerException(self._name, f'QEMU unknown section encountered: {section_byte}')
|
||||
|
||||
# If no architecture has been set, try to determine it using fallback mechanisms
|
||||
if not self._architecture:
|
||||
self._architecture = self._fallback_determine_architecture()
|
||||
if self._architecture is None:
|
||||
vollog.log(constants.LOGLEVEL_VV, f"QEVM architecture could not be determined")
|
||||
|
||||
# Once all segments have been read, determine the PCI hole if any
|
||||
for regex in self.pci_hole_table:
|
||||
if regex.match(self._architecture):
|
||||
self._pci_hole_minimum, self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex]
|
||||
if self.maximum_address < self._pci_hole_minimum:
|
||||
# The PCI hole isn't present because we're below the minimum value
|
||||
self._pci_hole_start, self._pci_hole_end = 0, 0
|
||||
vollog.log(constants.LOGLEVEL_VVVV, f"QEVM architecture detected as: {self._architecture}")
|
||||
break
|
||||
else:
|
||||
vollog.log(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}")
|
||||
|
||||
def _fallback_determine_architecture(self) -> str:
|
||||
architecture_pattern = rb'pc-(i440fx|q35)-([0-9]{1,2}.[0-9]{1,2}(?:.[0-9]{1,2})?)'
|
||||
base_layer = self.context.layers[self._base_layer]
|
||||
|
||||
vollog.log(constants.LOGLEVEL_VVVV, "QEVM fallback architecture detection used")
|
||||
|
||||
res = scanners.RegExScanner(architecture_pattern)
|
||||
for offset in base_layer.scan(context = self.context, scanner = res):
|
||||
line = base_layer.read(offset, 64)
|
||||
regex_results = re.search(architecture_pattern, line)
|
||||
architecture = "pc-" + regex_results.groups()[0].decode()
|
||||
return architecture
|
||||
|
||||
# If that does not work, look in configuration JSON for devices specific to a certain architecture
|
||||
architecture = None
|
||||
for device in self._configuration.get('devices', []):
|
||||
device_name = device.get('vmsd_name', '').lower()
|
||||
if 'i440fx' in device_name or 'piix' in device_name:
|
||||
architecture = 'pc-i440fx-2.0'
|
||||
break
|
||||
elif 'ich9' in device_name:
|
||||
architecture = 'pc-q35-1.0'
|
||||
break
|
||||
if architecture:
|
||||
return architecture
|
||||
|
||||
# Still haven't found architecture, switch to fallback-method
|
||||
architecture_pattern = rb'Standard PC \((i440FX|Q35)'
|
||||
res = scanners.RegExScanner(architecture_pattern)
|
||||
for offset in base_layer.scan(context = self.context, scanner = res):
|
||||
line = base_layer.read(offset, 64)
|
||||
regex_results = re.search(architecture_pattern, line)
|
||||
architecture = "pc-" + regex_results.groups()[0].decode().lower()
|
||||
return architecture
|
||||
|
||||
vollog.warning("Could not determine QEMU target architecture!")
|
||||
return None
|
||||
|
||||
def extract_data(self, index, name, version_id):
|
||||
if name == 'ram':
|
||||
if version_id != 4:
|
||||
|
||||
Reference in New Issue
Block a user