Convert all helper_ properties to get_ methods.

This commit is contained in:
Mike Auty
2018-03-09 00:47:00 +00:00
parent 6d618903d5
commit bc8429a2b0
9 changed files with 133 additions and 133 deletions
+2 -2
View File
@@ -103,7 +103,7 @@ class RegistryHive(interfaces.layers.TranslationLayerInterface):
found_key = [] # type: typing.List[str]
while key_array and node_key:
for subkey in node_key.get_subkeys():
if subkey.helper_name == key_array[0]:
if subkey.get_name() == key_array[0]:
node_key = subkey
found_key, key_array = found_key + [key_array[0]], key_array[1:]
break
@@ -153,7 +153,7 @@ class RegistryHive(interfaces.layers.TranslationLayerInterface):
table = storage.Map.Directory[dir_index]
entry = table.Table[table_index]
return entry.helper_block_offset + suboffset
return entry.get_block_offset() + suboffset
def mapping(self,
offset: int,
+2 -2
View File
@@ -584,9 +584,9 @@ class Struct(interfaces.objects.ObjectInterface):
vollog.debug("Deprecated non-helper attribute {} requested from class override {}".format(attr,
self.vol.type_name))
# Uncomment the following line if we want to prohibit using non-helper properties
# return self.__getattr_(attr)
# return self.__getattr__(attr)
# Change this to an attribute error if we want to prohibit rather than deprecate member collisisons
# Change this to an attribute error if we want to prohibit rather than deprecate member collisisons
return object.__getattribute__(self, attr)
def __getattr__(self, attr: str) -> typing.Any:
@@ -47,8 +47,7 @@ class _CM_KEY_BODY(objects.Struct):
"""This represents an open handle to a registry key and
is not tied to the registry hive file format on disk."""
@property
def helper_full_key_name(self) -> str:
def get_full_key_name(self) -> str:
output = []
kcb = self.KeyControlBlock
while kcb.ParentKcb:
@@ -63,8 +62,7 @@ class _CM_KEY_BODY(objects.Struct):
class _DEVICE_OBJECT(objects.Struct, ExecutiveObject):
@property
def helper_device_name(self) -> str:
def get_device_name(self) -> str:
header = self.object_header()
return header.NameInfo.Name.String # type: ignore
@@ -73,7 +71,7 @@ class _FILE_OBJECT(objects.Struct, ExecutiveObject):
def file_name_with_device(self) -> str:
name = ""
if self._context.memory[self.vol.layer_name].is_valid(self.DeviceObject):
name = "\\Device\\{}".format(self.DeviceObject.helper_device_name)
name = "\\Device\\{}".format(self.DeviceObject.get_device_name())
try:
name += self.FileName.String
@@ -124,15 +122,14 @@ class _ETHREAD(objects.Struct):
class _UNICODE_STRING(objects.Struct):
@property
def helper_string(self) -> interfaces.objects.ObjectInterface:
def get_string(self) -> interfaces.objects.ObjectInterface:
# We explicitly do *not* catch errors here, we allow an exception to be thrown
# (otherwise there's no way to determine anything went wrong)
# It's up to the user of this method to catch exceptions
return self.Buffer.dereference().cast("string", max_length = self.Length, errors = "replace",
encoding = "utf16")
String = helper_string
String = property(get_string)
class _EPROCESS(generic.GenericIntelProcess):
@@ -27,8 +27,7 @@ class RegValueTypes(enum.Enum):
class _HMAP_ENTRY(objects.Struct):
@property
def helper_block_offset(self) -> int:
def get_block_offset(self) -> int:
try:
return self.PermanentBinAddress ^ (self.PermanentBinAddress & 0x3)
except AttributeError:
@@ -36,28 +35,26 @@ class _HMAP_ENTRY(objects.Struct):
class _CMHIVE(objects.Struct):
@property
def helper_name(self) -> typing.Optional[interfaces.objects.ObjectInterface]:
def get_name(self) -> typing.Optional[interfaces.objects.ObjectInterface]:
"""Determine a name for the hive. Note that some attributes are
unpredictably blank across different OS versions while others are populated,
so we check all possibilities and take the first one that's not empty"""
for attr in ["FileFullPath", "FileUserName", "HiveRootPath"]:
try:
return getattr(self, attr).helper_string
return getattr(self, attr).get_string()
except (AttributeError, exceptions.InvalidAddressException):
pass
return None
name = helper_name
name = property(get_name)
class _CM_KEY_NODE(objects.Struct):
"""Extension to allow traversal of registry keys"""
@property
def helper_volatile(self) -> bool:
def get_volatile(self) -> bool:
if not isinstance(self._context.memory[self.vol.layer_name], RegistryHive):
raise ValueError("Cannot determine volatility of registry key without an offset in a RegistryHive layer")
return bool(self.vol.offset & 0x80000000)
@@ -114,8 +111,7 @@ class _CM_KEY_NODE(objects.Struct):
if node.vol.type_name.endswith(constants.BANG + '_CM_KEY_VALUE'):
yield node
@property
def helper_name(self) -> interfaces.objects.ObjectInterface:
def get_name(self) -> interfaces.objects.ObjectInterface:
"""Since this is just a casting convenience, it can be a property"""
return self.Name.cast("string", max_length = self.NameLength, encoding = "latin-1")
@@ -124,15 +120,14 @@ class _CM_KEY_NODE(objects.Struct):
# Using the offset adds a significant delay (since it cannot be cached easily)
# if self.vol.offset == reg.get_node(reg.root_cell_offset).vol.offset:
if self.vol.offset == reg.root_cell_offset + 4:
return self.helper_name
return reg.get_node(self.Parent).get_key_path() + '\\' + self.helper_name
return self.get_name()
return reg.get_node(self.Parent).get_key_path() + '\\' + self.get_name()
class _CM_KEY_VALUE(objects.Struct):
"""Extensions to extract data from CM_KEY_VALUE nodes"""
@property
def helper_name(self) -> interfaces.objects.ObjectInterface:
def get_name(self) -> interfaces.objects.ObjectInterface:
"""Since this is just a casting convenience, it can be a property"""
self.Name.count = self.NameLength
return self.Name.cast("string", max_length = self.NameLength, encoding = "latin-1")
@@ -168,15 +163,15 @@ class _CM_KEY_VALUE(objects.Struct):
self_type = RegValueTypes(self.Type)
if self_type == RegValueTypes.REG_DWORD:
if len(data) != struct.calcsize("<L"):
raise ValueError("Size of data does not match the type of registry value {}".format(self.helper_name))
raise ValueError("Size of data does not match the type of registry value {}".format(self.get_name()))
return struct.unpack("<L", data)[0]
if self_type == RegValueTypes.REG_DWORD_BIG_ENDIAN:
if len(data) != struct.calcsize(">L"):
raise ValueError("Size of data does not match the type of registry value {}".format(self.helper_name))
raise ValueError("Size of data does not match the type of registry value {}".format(self.get_name()))
return struct.unpack(">L", data)[0]
if self_type == RegValueTypes.REG_QWORD:
if len(data) != struct.calcsize("<Q"):
raise ValueError("Size of data does not match the type of registry value {}".format(self.helper_name))
raise ValueError("Size of data does not match the type of registry value {}".format(self.get_name()))
return struct.unpack("<Q", data)[0]
if self_type in [RegValueTypes.REG_SZ, RegValueTypes.REG_EXPAND_SZ, RegValueTypes.REG_LINK]:
# truncate after \x00\x00 to ensure it can
@@ -192,5 +187,5 @@ class _CM_KEY_VALUE(objects.Struct):
return ''
# Fall back if it's something weird
vollog.debug("Unknow registry value type encountered: {}", self.Type)
vollog.debug("Unknown registry value type encountered: {}", self.Type)
return self.Data.cast("string", max_length = self.DataLength, encoding = "latin-1")
+2 -2
View File
@@ -20,9 +20,9 @@ class DllList(interfaces_plugins.PluginInterface):
BaseDllName = FullDllName = ""
try:
BaseDllName = entry.BaseDllName.helper_string
BaseDllName = entry.BaseDllName.get_string()
# We assume that if the BaseDllName points to an invalid buffer, so will FullDllName
FullDllName = entry.FullDllName.helper_string
FullDllName = entry.FullDllName.get_string()
except exceptions.InvalidAddressException:
pass
+101 -93
View File
@@ -1,20 +1,22 @@
import logging
import volatility.framework.interfaces.plugins as interfaces_plugins
import volatility.plugins.windows.pslist as pslist
from volatility.framework import exceptions, renderers
from volatility.framework.configuration import requirements
from volatility.framework.renderers import format_hints
from volatility.framework import constants
from volatility.framework import exceptions, renderers
from volatility.framework.objects import utility
import logging
from volatility.framework.renderers import format_hints
vollog = logging.getLogger()
try:
import capstone
has_capstone = True
except ImportError:
has_capstone = False
class Handles(interfaces_plugins.PluginInterface):
"""Lists process open handles"""
@@ -31,61 +33,62 @@ class Handles(interfaces_plugins.PluginInterface):
return pslist.PsList.get_requirements() + []
def _decode_pointer(self, value, magic):
"""Windows encodes pointers to objects and decodes them on the fly
"""Windows encodes pointers to objects and decodes them on the fly
before using them. This function mimics the decoding routine so we
can generate the proper pointer values as well."""
value = value & 0xFFFFFFFFFFFFFFF8
value = value >> magic
#if (value & (1 << 47)):
# if (value & (1 << 47)):
# value = value | 0xFFFF000000000000
return value
def _get_item(self, handle_table_entry, handle_value):
"""Given a handle table entry (_HANDLE_TABLE_ENTRY) structure from
a process' handle table, determine where the corresponding object's
a process' handle table, determine where the corresponding object's
_OBJECT_HEADER can be found."""
virtual = self.config["primary"]
try:
# before windows 7
# before windows 7
if not self.context.memory[virtual].is_valid(handle_table_entry.Object):
return None
fast_ref = handle_table_entry.Object.cast(self.config["nt_symbols"] + constants.BANG + "_EX_FAST_REF")
object_header = fast_ref.dereference().cast(self.config["nt_symbols"] + constants.BANG + "_OBJECT_HEADER")
object_header = fast_ref.dereference().cast(self.config["nt_symbols"] + constants.BANG + "_OBJECT_HEADER")
object_header.GrantedAccess = handle_table_entry.GrantedAccess
except AttributeError:
# starting with windows 8
# starting with windows 8
if handle_table_entry.LowValue == 0:
return None
magic = self.find_sar_value()
# is this the right thing to raise here?
if magic == None:
raise AttributeError("Unable to find the SAR value for decoding handle table pointers")
offset = self._decode_pointer(handle_table_entry.LowValue, magic)
#print("LowValue: {0:#x} Magic: {1:#x} Offset: {2:#x}".format(handle_table_entry.InfoTable, magic, offset))
object_header = self.context.object(self.config["nt_symbols"] + constants.BANG + "_OBJECT_HEADER", virtual, offset = offset)
# print("LowValue: {0:#x} Magic: {1:#x} Offset: {2:#x}".format(handle_table_entry.InfoTable, magic, offset))
object_header = self.context.object(self.config["nt_symbols"] + constants.BANG + "_OBJECT_HEADER", virtual,
offset = offset)
object_header.GrantedAccess = handle_table_entry.GrantedAccessBits
object_header.HandleValue = handle_value
return object_header
def find_sar_value(self):
"""Locate ObpCaptureHandleInformationEx if it exists in the
"""Locate ObpCaptureHandleInformationEx if it exists in the
sample. Once found, parse it for the SAR value that we need
to decode pointers in the _HANDLE_TABLE_ENTRY which allows us
to decode pointers in the _HANDLE_TABLE_ENTRY which allows us
to find the associated _OBJECT_HEADER."""
if self._sar_value is None:
if not has_capstone:
return None
virtual_layer_name = self.config['primary']
kvo = self.context.memory[virtual_layer_name].config['kernel_virtual_offset']
ntkrnlmp = self.context.module(self.config["nt_symbols"], layer_name = virtual_layer_name, offset = kvo)
@@ -94,35 +97,35 @@ class Handles(interfaces_plugins.PluginInterface):
func_addr = ntkrnlmp.get_symbol("ObpCaptureHandleInformationEx").address
except AttributeError:
return None
data = self.context.memory.read(virtual_layer_name, kvo + func_addr, 0x200)
if data == None:
return None
md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
for (address, size, mnemonic, op_str) in md.disasm_lite(data, kvo + func_addr):
#print("{} {} {} {}".format(address, size, mnemonic, op_str))
# print("{} {} {} {}".format(address, size, mnemonic, op_str))
if mnemonic.startswith("sar"):
# if we don't want to parse op strings, we can disasm the
# if we don't want to parse op strings, we can disasm the
# single sar instruction again, but we use disasm_lite for speed
self._sar_value = int(op_str.split(",")[1].strip(), 16)
break
break
return self._sar_value
def list_objects(self):
"""List the executive object types (_OBJECT_TYPE) using the
ObTypeIndexTable or ObpObjectTypes symbol (differs per OS).
This method will be necessary for determining what type of
object we have given an object header.
Note: The object type index map was hard coded into profiles
"""List the executive object types (_OBJECT_TYPE) using the
ObTypeIndexTable or ObpObjectTypes symbol (differs per OS).
This method will be necessary for determining what type of
object we have given an object header.
Note: The object type index map was hard coded into profiles
in vol2, but we generate it dynamically now."""
if self._type_map is None:
self._type_map = {}
virtual_layer = self.config['primary']
@@ -133,61 +136,64 @@ class Handles(interfaces_plugins.PluginInterface):
table_addr = ntkrnlmp.get_symbol("ObTypeIndexTable").address
except AttributeError:
table_addr = ntkrnlmp.get_symbol("ObpObjectTypes").address
ptrs = ntkrnlmp.object(type_name = "array", offset = kvo + table_addr,
subtype = ntkrnlmp.get_type("pointer"),
count = 100)
ptrs = ntkrnlmp.object(type_name = "array", offset = kvo + table_addr,
subtype = ntkrnlmp.get_type("pointer"),
count = 100)
for i, ptr in enumerate(ptrs):
# the first entry in the table is always null. break the
# loop when we encounter the first null entry after that
if i > 0 and ptr == 0:
break
break
objt = ptr.dereference().cast(self.config["nt_symbols"] + constants.BANG + "_OBJECT_TYPE")
try:
type_name = objt.Name.String
except exceptions.PagedInvalidAddressException:
vollog.log(constants.LOGLEVEL_VVV, "Cannot access _OBJECT_HEADER.Name at {0:#x}".format(objt.Name.vol.offset))
vollog.log(constants.LOGLEVEL_VVV,
"Cannot access _OBJECT_HEADER.Name at {0:#x}".format(objt.Name.vol.offset))
continue
self._type_map[i] = type_name
return self._type_map
return self._type_map
def object_type(self, object_header, type_map):
"""Across all Windows versions, the _OBJECT_HEADER embeds details on the type of
"""Across all Windows versions, the _OBJECT_HEADER embeds details on the type of
object (i.e. process, file) but the way its embedded differs between versions.
This API abstracts away those details."""
try:
# vista and earlier have a Type member
# vista and earlier have a Type member
return object_header.Type.Name.String
except AttributeError:
# windows 7 and later have a TypeIndex, but windows 10
# further encodes the index value with nt1!ObHeaderCookie
# further encodes the index value with nt1!ObHeaderCookie
virtual = self.config["primary"]
try:
if self._cookie is None:
offset = self.context.symbol_space.get_symbol(self.config["nt_symbols"] + constants.BANG + "ObHeaderCookie").address
offset = self.context.symbol_space.get_symbol(
self.config["nt_symbols"] + constants.BANG + "ObHeaderCookie").address
kvo = self.context.memory[virtual].config['kernel_virtual_offset']
self._cookie = self.context.object(self.config["nt_symbols"] + constants.BANG + "unsigned int", virtual, offset = kvo + offset)
self._cookie = self.context.object(self.config["nt_symbols"] + constants.BANG + "unsigned int",
virtual, offset = kvo + offset)
type_index = ((object_header.vol.offset >> 8) ^ self._cookie ^ ord(object_header.TypeIndex)) & 0xFF
type_index = ((object_header.vol.offset >> 8) ^ self._cookie ^ ord(object_header.TypeIndex)) & 0xFF
except AttributeError:
type_index = ord(object_header.TypeIndex)
return type_map.get(type_index)
def _make_handle_array(self, offset, level, depth = 0):
"""Parse a process' handle table and yield valid handle table
"""Parse a process' handle table and yield valid handle table
entries, going as deep into the table "levels" as necessary."""
virtual = self.config["primary"]
kvo = self.context.memory[virtual].config['kernel_virtual_offset']
ntkrnlmp = self.context.module(self.config["nt_symbols"], layer_name = virtual, offset = kvo)
ntkrnlmp = self.context.module(self.config["nt_symbols"], layer_name = virtual, offset = kvo)
if level > 0:
subtype = ntkrnlmp.get_type("pointer")
count = 0x1000 / subtype.size
@@ -198,29 +204,29 @@ class Handles(interfaces_plugins.PluginInterface):
if not self.context.memory[virtual].is_valid(offset):
raise StopIteration
table = ntkrnlmp.object(type_name = "array", offset = offset,
subtype = subtype, count = int(count))
table = ntkrnlmp.object(type_name = "array", offset = offset,
subtype = subtype, count = int(count))
layer_object = self.context.memory[virtual]
masked_offset = layer_object._mask(offset, 0, layer_object._maxvirtaddr)
for entry in table:
if level > 0:
for x in self._make_handle_array(entry, level - 1, depth):
yield x
depth += 1
depth += 1
else:
handle_multiplier = 4
handle_level_base = depth * count * handle_multiplier
handle_value = ((entry.vol.offset - masked_offset) /
(subtype.size / handle_multiplier)) + handle_level_base
(subtype.size / handle_multiplier)) + handle_level_base
item = self._get_item(entry, handle_value)
if item == None:
continue
continue
try:
if item.TypeIndex != 0x0:
@@ -239,60 +245,62 @@ class Handles(interfaces_plugins.PluginInterface):
except exceptions.PagedInvalidAddressException:
vollog.log(constants.LOGLEVEL_VVV, "Handle table parsing was aborted due to an invalid address exception")
raise StopIteration
for handle_table_entry in self._make_handle_array(TableCode, table_levels):
yield handle_table_entry
def _generator(self, procs):
type_map = self.list_objects()
for proc in procs:
try:
object_table = proc.ObjectTable
except exceptions.PagedInvalidAddressException:
vollog.log(constants.LOGLEVEL_VVV, "Cannot access _EPROCESS.ObjectType at {0:#x}".format(proc.ObjectTable.vol.offset))
vollog.log(constants.LOGLEVEL_VVV,
"Cannot access _EPROCESS.ObjectType at {0:#x}".format(proc.ObjectTable.vol.offset))
continue
process_name = utility.array_to_string(proc.ImageFileName)
for entry in self.handles(object_table):
try:
obj_type = self.object_type(entry, type_map)
if obj_type == None:
continue
if obj_type == "File":
item = entry.Body.cast(self.config["nt_symbols"] + constants.BANG + "_FILE_OBJECT")
obj_name = item.file_name_with_device()
elif obj_type == "Process":
item = entry.Body.cast(self.config["nt_symbols"] + constants.BANG + "_EPROCESS")
obj_name = "{} Pid {}".format(utility.array_to_string(proc.ImageFileName),
item.UniqueProcessId)
item.UniqueProcessId)
elif obj_type == "Thread":
item = entry.Body.cast(self.config["nt_symbols"] + constants.BANG + "_ETHREAD")
obj_name = "Tid {} Pid {}".format(item.Cid.UniqueThread, item.Cid.UniqueProcess)
elif obj_type == "Key":
item = entry.Body.cast(self.config["nt_symbols"] + constants.BANG + "_CM_KEY_BODY")
obj_name = item.helper_full_key_name
obj_name = item.get_full_key_name()
else:
try:
obj_name = entry.NameInfo.Name.String
except exceptions.InvalidAddressException:
obj_name = ""
except (exceptions.InvalidAddressException):
vollog.log(constants.LOGLEVEL_VVV, "Cannot access _OBJECT_HEADER at {0:#x}".format(entry.vol.offset))
vollog.log(constants.LOGLEVEL_VVV,
"Cannot access _OBJECT_HEADER at {0:#x}".format(entry.vol.offset))
continue
yield (0, (proc.UniqueProcessId,
process_name,
format_hints.Hex(entry.HandleValue),
obj_type,
format_hints.Hex(entry.GrantedAccess),
obj_name))
yield (0, (proc.UniqueProcessId,
process_name,
format_hints.Hex(entry.HandleValue),
obj_type,
format_hints.Hex(entry.GrantedAccess),
obj_name))
def run(self):
+1 -1
View File
@@ -21,7 +21,7 @@ class HiveList(plugins.PluginInterface):
for hive in self.list_hives():
yield (0, (format_hints.Hex(hive.vol.offset),
hive.helper_name or ""))
hive.get_name() or ""))
def list_hives(self):
"""Lists all the hives in the primary layer"""
+2 -2
View File
@@ -22,12 +22,12 @@ class Modules(plugins.PluginInterface):
for mod in self.list_modules():
try:
BaseDllName = mod.BaseDllName.helper_string
BaseDllName = mod.BaseDllName.get_string()
except exceptions.InvalidAddressException:
BaseDllName = ""
try:
FullDllName = mod.FullDllName.helper_string
FullDllName = mod.FullDllName.get_string()
except exceptions.InvalidAddressException:
FullDllName = ""
+5 -5
View File
@@ -49,9 +49,9 @@ class PrintKey(plugins.PluginInterface):
(str(datetime.datetime.utcfromtimestamp(unix_time)),
"Key",
key_path,
key_node.helper_name,
key_node.get_name(),
"",
key_node.helper_volatile))
key_node.get_volatile()))
yield result
for value_node in node.get_values():
@@ -59,14 +59,14 @@ class PrintKey(plugins.PluginInterface):
(str(datetime.datetime.utcfromtimestamp(unix_time)),
RegValueTypes(value_node.Type).name,
key_path,
value_node.helper_name,
value_node.get_name(),
str(value_node.decode_data()),
node.helper_volatile))
node.get_volatile()))
yield result
if self.config['recurse']:
for node in node.get_subkeys():
yield from self.hive_walker(hive, node, key_path + "\\" + node.helper_name)
yield from self.hive_walker(hive, node, key_path + "\\" + node.get_name())
def registry_walker(self):
"""Walks through a registry, hive by hive"""