From 8c617d6b3fcc8a029696a0065d00d9c63738072c Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Fri, 7 Feb 2025 15:06:32 -0600 Subject: [PATCH 1/2] Make a generic DLL enumeration function that ensures the base address is set and we return as many entries as possible #1475 --- .../symbols/windows/extensions/__init__.py | 109 +++++++++--------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 230d59f95..fb03d304a 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -491,9 +491,9 @@ class FILE_OBJECT(objects.StructType, pool.ExecutiveObject): ].is_valid(self.FileName.Buffer) def file_name_with_device(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: - name: Union[str, interfaces.renderers.BaseAbsentValue] = ( - renderers.UnreadableValue() - ) + name: Union[ + str, interfaces.renderers.BaseAbsentValue + ] = renderers.UnreadableValue() # this pointer needs to be checked against native_layer_name because the object may # be instantiated from a primary (virtual) layer or a memory (physical) layer. @@ -848,69 +848,64 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): sym_table = self._32bit_table_name return sym_table + def _walk_ldr_list( + self, list_member: str, link_member: str + ) -> Iterable[interfaces.objects.ObjectInterface]: + """ + Walks LDR_DATA_TABLEs and enforces the entries at least have a valid base address + This function also breaks up exception handling as much as possible to ensure the + most data is returned as possible + """ + pebs = [] + + try: + peb = self.get_peb() + if peb: + pebs.append(peb) + except exceptions.InvalidAddressException: + vollog.debug(f"Process at {self.vol.offset:#x} has invalid PEB") + + try: + peb32 = self.get_peb32() + if peb32: + pebs.append(peb32) + except exceptions.InvalidAddressException: + vollog.debug(f"Process at {self.vol.offset:#x} has invalid 32 bit PEB") + + for peb in pebs: + sym_table = self.get_symbol_table_name() + if peb.Ldr.vol.type_name.split(constants.BANG)[-1] == ("unsigned long"): + sym_table = self.set_types(peb) + + for ldr in peb.Ldr.member(list_member).to_list( + f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", link_member + ): + try: + # Several samples in testing crashed from DLLs being returned + # where DllBase was on the next page and that page was not in memory + # Not being able to retrieve the base makes the entry pretty useless + # So we enforce here its presence + ldr.DllBase + yield ldr + except exceptions.InvalidAddressException: + continue + def load_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: """Generator for DLLs in the order that they were loaded.""" - try: - pebs = [ - self.get_peb(), - self.get_peb32(), - ] - for peb in pebs: - if peb: - sym_table = self.get_symbol_table_name() - if peb.Ldr.vol.type_name.split(constants.BANG)[-1] == ( - "unsigned long" - ): - sym_table = self.set_types(peb) - yield from peb.Ldr.InLoadOrderModuleList.to_list( - f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", - "InLoadOrderLinks", - ) - except exceptions.InvalidAddressException: - return None + + yield from self._walk_ldr_list("InLoadOrderModuleList", "InLoadOrderLinks") def init_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: """Generator for DLLs in the order that they were initialized""" - try: - pebs = [ - self.get_peb(), - self.get_peb32(), - ] - for peb in pebs: - if peb: - sym_table = self.get_symbol_table_name() - if peb.Ldr.vol.type_name.split(constants.BANG)[-1] == ( - "unsigned long" - ): - sym_table = self.set_types(peb) - yield from peb.Ldr.InInitializationOrderModuleList.to_list( - f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", - "InInitializationOrderLinks", - ) - except exceptions.InvalidAddressException: - return None + yield from self._walk_ldr_list( + "InInitializationOrderModuleList", "InInitializationOrderLinks" + ) def mem_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: """Generator for DLLs in the order that they appear in memory""" - try: - pebs = [ - self.get_peb(), - self.get_peb32(), - ] - for peb in pebs: - if peb: - sym_table = self.get_symbol_table_name() - if peb.Ldr.vol.type_name.split(constants.BANG)[-1] == ( - "unsigned long" - ): - sym_table = self.set_types(peb) - yield from peb.Ldr.InMemoryOrderModuleList.to_list( - f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", - "InMemoryOrderLinks", - ) - except exceptions.InvalidAddressException: - return None + + yield from self._walk_ldr_list("InMemoryOrderModuleList", "InMemoryOrderLinks") def get_handle_count(self): try: From 67c001ab50564204ff7be56f43c57212c2420a3a Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Fri, 7 Feb 2025 15:12:06 -0600 Subject: [PATCH 2/2] Update for black --- .../framework/symbols/windows/extensions/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index fb03d304a..1d6040265 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -491,9 +491,9 @@ class FILE_OBJECT(objects.StructType, pool.ExecutiveObject): ].is_valid(self.FileName.Buffer) def file_name_with_device(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: - name: Union[ - str, interfaces.renderers.BaseAbsentValue - ] = renderers.UnreadableValue() + name: Union[str, interfaces.renderers.BaseAbsentValue] = ( + renderers.UnreadableValue() + ) # this pointer needs to be checked against native_layer_name because the object may # be instantiated from a primary (virtual) layer or a memory (physical) layer.