mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-30 11:49:42 +02:00
Add in the automagic and allow mid-deptree resolution (without trying to re-fulfill already populated requirements).
This commit is contained in:
@@ -55,10 +55,9 @@ class CommandLine(object):
|
||||
windows = True
|
||||
if windows:
|
||||
# Traverse the dependency tree and tag the config with the appropriate page_map_offset values where not already applied
|
||||
wdf = windows_automagic.WindowsDtbFinder(volatility.framework.class_subclasses(windows_automagic.DtbTest))
|
||||
dldr.configurable_visitor(deptree, context = ctx, path = config_path,
|
||||
visitor = windows_automagic.PageMapOffsetHelper())
|
||||
print(repr(deptree))
|
||||
deptree.traverse(windows_automagic.PageMapOffsetHelper(context = ctx),
|
||||
config_path = config_path,
|
||||
short_circuit = False)
|
||||
|
||||
# Walk down the tree attempting to fulfil each requirement (recursive) and backtrack when necessary
|
||||
# Translate the parsed args to a context configuration
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import struct
|
||||
|
||||
from volatility.framework import interfaces, layers, validity, configuration
|
||||
from volatility.framework.configuration import depresolver
|
||||
|
||||
PAGE_SIZE = 0x1000
|
||||
|
||||
|
||||
def scan(ctx, layer_name, tests):
|
||||
"""Scans through layer_name at context and returns the best-guess layer type and a single best-guess DTB
|
||||
|
||||
It should be noted that this is automagical and therefore not the guaranteed correct response
|
||||
The UI should always provide the user an opportunity to specify the appropriate types and DTB values themselves
|
||||
"""
|
||||
hits = {}
|
||||
for offset in range(ctx.memory[layer_name].minimum_address,
|
||||
ctx.memory[layer_name].maximum_address - PAGE_SIZE,
|
||||
PAGE_SIZE):
|
||||
for test in tests:
|
||||
val = test.run(offset, ctx, layer_name)
|
||||
if val:
|
||||
hits[test.layer_type] = sorted(hits.get(test.layer_type, []) + [val])
|
||||
return hits
|
||||
|
||||
|
||||
class DtbTest(validity.ValidityRoutines):
|
||||
super_bit = 2
|
||||
|
||||
def __init__(self, layer_type = None, ptr_size = None, ptr_struct = None, ptr_reference = None):
|
||||
self.layer_type = self._check_class(layer_type, interfaces.layers.TranslationLayerInterface)
|
||||
self.ptr_size = self._check_type(ptr_size, int)
|
||||
self.ptr_struct = self._check_type(ptr_struct, str)
|
||||
self.ptr_reference = self._check_type(ptr_reference, int)
|
||||
|
||||
def unpack(self, value):
|
||||
return struct.unpack("<" + self.ptr_struct, value)[0]
|
||||
|
||||
def run(self, page_offset, ctx, layer_name):
|
||||
value = ctx.memory.read(layer_name, page_offset + (self.ptr_reference * self.ptr_size),
|
||||
self.ptr_size)
|
||||
ptr = self.unpack(value)
|
||||
# The value *must* be present (bit 0) since it's a mapped page
|
||||
# It's almost always writable (bit 1)
|
||||
# It's occasionally Super, but not reliably so, haven't checked when/why not
|
||||
# The top 3-bits are usually ignore (which in practice means 0
|
||||
# Need to find out why the middle 3-bits are usually 6 (0110)
|
||||
if ptr != 0 and (ptr & 0xFFFFFFFFFFFFF000 == page_offset) & (ptr & 0xFF1 == 0x61):
|
||||
dtb = (ptr & 0xFFFFFFFFFFFFF000)
|
||||
return self.second_pass(dtb, ctx, layer_name)
|
||||
|
||||
def second_pass(self, dtb, ctx, layer_name):
|
||||
data = ctx.memory.read(layer_name, dtb, PAGE_SIZE)
|
||||
usr_count, sup_count = 0, 0
|
||||
for i in range(0, PAGE_SIZE, self.ptr_size):
|
||||
val = self.unpack(data[i:i + self.ptr_size])
|
||||
if val & 0x1:
|
||||
sup_count += 0 if (val & 0x4) else 1
|
||||
usr_count += 1 if (val & 0x4) else 0
|
||||
# print(hex(dtb), usr_count, sup_count, usr_count + sup_count)
|
||||
if usr_count:
|
||||
return usr_count, dtb
|
||||
|
||||
|
||||
class DtbTest32bit(DtbTest):
|
||||
def __init__(self):
|
||||
DtbTest.__init__(self,
|
||||
layer_type = layers.intel.Intel,
|
||||
ptr_size = 4,
|
||||
ptr_struct = "I",
|
||||
ptr_reference = 0x300)
|
||||
|
||||
|
||||
class DtbTest64bit(DtbTest):
|
||||
def __init__(self):
|
||||
DtbTest.__init__(self,
|
||||
layer_type = layers.intel.Intel32e,
|
||||
ptr_size = 8,
|
||||
ptr_struct = "Q",
|
||||
ptr_reference = 0x1ED)
|
||||
|
||||
|
||||
class DtbTestPae(DtbTest):
|
||||
def __init__(self):
|
||||
DtbTest.__init__(self,
|
||||
layer_type = layers.intel.IntelPAE,
|
||||
ptr_size = 8,
|
||||
ptr_struct = "Q",
|
||||
ptr_reference = 0x3)
|
||||
|
||||
def second_pass(self, dtb, ctx, layer_name):
|
||||
dtb -= 0x4000
|
||||
data = ctx.memory.read(layer_name, dtb, PAGE_SIZE)
|
||||
val = self.unpack(data[3 * self.ptr_size: 4 * self.ptr_size])
|
||||
if (val & 0xFFFFFFFFFFFFF000 == dtb + 0x4000) and (val & 0xFFF == 0x001):
|
||||
return val, dtb
|
||||
|
||||
|
||||
class PageMapOffsetHelper(interfaces.configuration.ReqTreeVisitorInterface):
|
||||
def __init__(self, context):
|
||||
self.ctx = self._check_type(context, interfaces.context.ContextInterface)
|
||||
self.tests = dict([(test.layer_type, test) for test in [DtbTest32bit(), DtbTest64bit(), DtbTestPae()]])
|
||||
|
||||
def __call__(self, node, config_path):
|
||||
if isinstance(node, depresolver.RequirementTreeChoice):
|
||||
useful = []
|
||||
for candidate in node.candidates:
|
||||
if candidate in self.tests:
|
||||
useful.append(self.tests[candidate])
|
||||
if useful:
|
||||
depresolver.DependencyResolver().validate_dependencies(node.candidates[useful[0].layer_type], self.ctx,
|
||||
config_path)
|
||||
prefix = config_path + configuration.CONFIG_SEPARATOR
|
||||
memory_layer = self.ctx.config.get(prefix + "memory_layer", None)
|
||||
page_table_offset = self.ctx.config.get(prefix + "page_table_offset", None)
|
||||
if page_table_offset is None and memory_layer is not None:
|
||||
hits = scan(self.ctx, memory_layer, useful)
|
||||
for test in useful:
|
||||
if hits.get(test.layer_type, []):
|
||||
self.ctx.config[prefix + "page_table_offset"] = hits[test.layer_type][0][1]
|
||||
print("CONFIG", dict(self.ctx.config))
|
||||
return True
|
||||
@@ -59,7 +59,7 @@ class DependencyResolver(validity.ValidityRoutines):
|
||||
if path is None:
|
||||
path = ""
|
||||
|
||||
self._check_type(deptree, RequirementTreeList)
|
||||
self._check_type(deptree, interfaces.configuration.RequirementTreeNode)
|
||||
visitor = ValidatorVisitor(context)
|
||||
return deptree.traverse(visitor, path, short_circuit = True)
|
||||
|
||||
@@ -114,16 +114,18 @@ class ValidatorVisitor(interfaces.configuration.ReqTreeVisitorInterface):
|
||||
return True
|
||||
|
||||
if isinstance(node, RequirementTreeChoice) and not node.requirement.optional:
|
||||
for provider in node.candidates:
|
||||
try:
|
||||
provider.fulfill(self.ctx, node.requirement, config_path)
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
else:
|
||||
logging.debug(
|
||||
"Unable to fulfill requirement " + repr(node.requirement) + " - no fulfillable candidates")
|
||||
return False
|
||||
if self.ctx.config.get(config_path, None) is None:
|
||||
# Only try to provide when we're not already sorted
|
||||
for provider in node.candidates:
|
||||
try:
|
||||
provider.fulfill(self.ctx, node.requirement, config_path)
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
else:
|
||||
logging.debug(
|
||||
"Unable to fulfill requirement " + repr(node.requirement) + " - no fulfillable candidates")
|
||||
return False
|
||||
|
||||
try:
|
||||
value = self.ctx.config[config_path]
|
||||
|
||||
Reference in New Issue
Block a user