diff --git a/volatility/framework/layers/registry.py b/volatility/framework/layers/registry.py index 1f69d8b35..2f12fda6a 100644 --- a/volatility/framework/layers/registry.py +++ b/volatility/framework/layers/registry.py @@ -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, diff --git a/volatility/framework/objects/__init__.py b/volatility/framework/objects/__init__.py index ec80672b5..ac16e6133 100644 --- a/volatility/framework/objects/__init__.py +++ b/volatility/framework/objects/__init__.py @@ -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: diff --git a/volatility/framework/symbols/windows/extensions/__init__.py b/volatility/framework/symbols/windows/extensions/__init__.py index d1510d987..935514605 100644 --- a/volatility/framework/symbols/windows/extensions/__init__.py +++ b/volatility/framework/symbols/windows/extensions/__init__.py @@ -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): diff --git a/volatility/framework/symbols/windows/extensions/registry.py b/volatility/framework/symbols/windows/extensions/registry.py index de72476cf..ea7528da7 100644 --- a/volatility/framework/symbols/windows/extensions/registry.py +++ b/volatility/framework/symbols/windows/extensions/registry.py @@ -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_QWORD: if len(data) != struct.calcsize("> 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): diff --git a/volatility/plugins/windows/hivelist.py b/volatility/plugins/windows/hivelist.py index 7d6464ef2..82e1632ab 100644 --- a/volatility/plugins/windows/hivelist.py +++ b/volatility/plugins/windows/hivelist.py @@ -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""" diff --git a/volatility/plugins/windows/modules.py b/volatility/plugins/windows/modules.py index c6d3c985d..2ab3da38d 100644 --- a/volatility/plugins/windows/modules.py +++ b/volatility/plugins/windows/modules.py @@ -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 = "" diff --git a/volatility/plugins/windows/printkey.py b/volatility/plugins/windows/printkey.py index d816296b9..724ee82e0 100644 --- a/volatility/plugins/windows/printkey.py +++ b/volatility/plugins/windows/printkey.py @@ -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"""