From 00a7f9ce2d2978f69e2095a56bf5afe50c3fed7f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 13 Sep 2018 00:24:57 +0100 Subject: [PATCH] Rework the way verinfo works. --- volatility/plugins/windows/verinfo.py | 118 ++++++++++---------------- 1 file changed, 47 insertions(+), 71 deletions(-) diff --git a/volatility/plugins/windows/verinfo.py b/volatility/plugins/windows/verinfo.py index c91a64e48..0440ae93d 100644 --- a/volatility/plugins/windows/verinfo.py +++ b/volatility/plugins/windows/verinfo.py @@ -33,11 +33,11 @@ class VerInfo(interfaces_plugins.PluginInterface): requirements.SymbolRequirement(name = "nt_symbols", description = "Windows OS"), ] @classmethod - def get_version_entries(cls, - context: interfaces.context.ContextInterface, - pe_table_name: str, - layer_name: str, - base_address: int) -> typing.Dict[str, typing.Any]: + def get_version_information(cls, + context: interfaces.context.ContextInterface, + pe_table_name: str, + layer_name: str, + base_address: int) -> typing.Optional[typing.Tuple[int]]: """Get File and Product version information from PE files Args: @@ -46,45 +46,30 @@ class VerInfo(interfaces_plugins.PluginInterface): base_address: base address of the PE (where MZ is found) """ + if layer_name is None: + raise ValueError("Layer must be a string not None") + pe_data = io.BytesIO() - entries = {} # type: typing.Dict[str, typing.Any] dos_header = context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", offset = base_address, layer_name = layer_name) - try: - for offset, data in dos_header.reconstruct(): - pe_data.seek(offset) - pe_data.write(data) + for offset, data in dos_header.reconstruct(): + pe_data.seek(offset) + pe_data.write(data) - pe = pefile.PE(data = pe_data.getvalue(), fast_load = True) - pe.parse_data_directories([pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_RESOURCE"]]) + pe = pefile.PE(data = pe_data.getvalue(), fast_load = True) + pe.parse_data_directories([pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_RESOURCE"]]) - entries = {} + major = pe.VS_FIXEDFILEINFO.ProductVersionMS >> 16 + minor = pe.VS_FIXEDFILEINFO.ProductVersionMS & 0xFFFF + product = pe.VS_FIXEDFILEINFO.ProductVersionLS >> 16 + build = pe.VS_FIXEDFILEINFO.ProductVersionLS & 0xFFFF - for finfo in pe.FileInfo: - if finfo.name == "StringFileInfo": - for table in finfo.StringTable: - entries.update(table.entries) + pe_data.close() - except ValueError: - vollog.log(constants.LOGLEVEL_VVV, - "Error parsing PE at {0:#x} in layer {1}".format(base_address, layer_name)) - - except AttributeError: - vollog.log(constants.LOGLEVEL_VVV, - "Error locating the string resources for PE at {0:#x} in layer {1}".format(base_address, - layer_name)) - - except exceptions.InvalidAddressException as exp: - vollog.log(constants.LOGLEVEL_VVV, - "Required data at {0:#x} in layer {1} is unavailable".format(exp.invalid_address, layer_name)) - - finally: - pe_data.close() - - return entries + return (major, minor, product, build) def _generator(self, procs: typing.Generator[interfaces.objects.ObjectInterface, None, None], @@ -110,32 +95,24 @@ class VerInfo(interfaces_plugins.PluginInterface): BaseDllName = renderers.UnreadableValue() session_layer_name = moddump.ModDump.find_session_layer(self.context, session_layers, mod.DllBase) - if session_layer_name is None: - file_version = renderers.UnreadableValue() - product_version = renderers.UnreadableValue() - else: - entries = self.get_version_entries(self._context, - pe_table_name, - session_layer_name, - mod.DllBase) - - try: - file_version = entries[b"FileVersion"].decode() - except KeyError: - file_version = renderers.UnreadableValue() - - try: - product_version = entries[b"ProductVersion"].decode() - except KeyError: - product_version = renderers.UnreadableValue() + (major, minor, product, build) = [renderers.NotAvailableValue()] * 4 + try: + (major, minor, product, build) = self.get_version_information(self._context, + pe_table_name, + session_layer_name, + mod.DllBase) + except (exceptions.InvalidAddressException, ValueError, AttributeError): + (major, minor, product, build) = [renderers.UnreadableValue()] * 4 # the pid and process are not applicable for kernel modules yield (0, (renderers.NotApplicableValue(), renderers.NotApplicableValue(), format_hints.Hex(mod.DllBase), BaseDllName, - file_version, - product_version)) + major, + minor, + product, + build)) # now go through the process and dll lists for proc in procs: @@ -147,20 +124,15 @@ class VerInfo(interfaces_plugins.PluginInterface): except exceptions.InvalidAddressException: BaseDllName = renderers.UnreadableValue() - entries = self.get_version_entries(self._context, - pe_table_name, - proc_layer_name, - entry.DllBase) - + session_layer_name = moddump.ModDump.find_session_layer(self.context, session_layers, mod.DllBase) + (major, minor, product, build) = [renderers.NotAvailableValue()] * 4 try: - file_version = entries[b"FileVersion"].decode() - except KeyError: - file_version = renderers.UnreadableValue() - - try: - product_version = entries[b"ProductVersion"].decode() - except KeyError: - product_version = renderers.UnreadableValue() + (major, minor, product, build) = self.get_version_information(self._context, + pe_table_name, + proc_layer_name, + entry.DllBase) + except (exceptions.InvalidAddressException, ValueError, AttributeError): + (major, minor, product, build) = [renderers.UnreadableValue()] * 4 yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast("string", @@ -168,8 +140,10 @@ class VerInfo(interfaces_plugins.PluginInterface): errors = "replace"), format_hints.Hex(entry.DllBase), BaseDllName, - file_version, - product_version)) + major, + minor, + product, + build)) def run(self): procs = pslist.PsList.list_processes(self.context, @@ -189,6 +163,8 @@ class VerInfo(interfaces_plugins.PluginInterface): ("Process", str), ("Base", format_hints.Hex), ("Name", str), - ("File", str), - ("Product", str)], + ("Major", int), + ("Minor", int), + ("Product", int), + ("Build", int)], self._generator(procs, mods, session_layers))