Add in the automagic and allow mid-deptree resolution (without trying to re-fulfill already populated requirements).

This commit is contained in:
Mike Auty
2016-02-21 00:42:04 +00:00
parent 9d7e777c28
commit c6a3c2d6b1
4 changed files with 137 additions and 15 deletions
+3 -4
View File
@@ -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
+121
View File
@@ -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]