From 29e2f4485f8e8eb782a39aff459239666a87cc1a Mon Sep 17 00:00:00 2001 From: AsafEitani Date: Sun, 30 Aug 2020 16:42:28 +0300 Subject: [PATCH] Added dlllist and netscan to timline --- .../framework/plugins/windows/dlllist.py | 45 +++++++++++++++---- .../framework/plugins/windows/filescan.py | 3 +- .../framework/plugins/windows/netscan.py | 21 +++++++-- .../framework/plugins/windows/pslist.py | 2 +- .../framework/plugins/windows/psscan.py | 2 +- 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/volatility/framework/plugins/windows/dlllist.py b/volatility/framework/plugins/windows/dlllist.py index 0a5d1b393..183b6f37d 100644 --- a/volatility/framework/plugins/windows/dlllist.py +++ b/volatility/framework/plugins/windows/dlllist.py @@ -3,19 +3,21 @@ # import logging import ntpath +import datetime from typing import List from volatility.framework import exceptions, renderers, interfaces, constants from volatility.framework.configuration import requirements -from volatility.framework.renderers import format_hints +from volatility.framework.renderers import format_hints, conversion from volatility.framework.symbols import intermed from volatility.framework.symbols.windows.extensions import pe -from volatility.plugins.windows import pslist +from volatility.plugins import timeliner +from volatility.plugins.windows import pslist, info vollog = logging.getLogger(__name__) -class DllList(interfaces.plugins.PluginInterface): +class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): """Lists the loaded modules in a particular windows memory image.""" _version = (1, 0, 0) @@ -28,7 +30,8 @@ class DllList(interfaces.plugins.PluginInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), - requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)), + requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (1, 0, 0)), + requirements.VersionRequirement(name = 'info', component = info.Info, version = (1, 0, 0)), requirements.ListRequirement(name = 'pid', element_type = int, description = "Process IDs to include (all other processes are excluded)", @@ -86,6 +89,12 @@ class DllList(interfaces.plugins.PluginInterface): "pe", class_types = pe.class_types) + kuser = info.Info.get_kuser_structure(self.context, self.config['primary'], self.config['nt_symbols']) + nt_major_version = int(kuser.NtMajorVersion) + nt_minor_version = int(kuser.NtMinorVersion) + # this only applies to versions higher or equal to Window 7 (6.1 and higher) + dll_load_time_field = (nt_major_version > 6) or (nt_major_version == 6 and nt_minor_version >= 1) + time_delta_1600 = datetime.timedelta(days=(1970 - 1601) * 365 + 89) for proc in procs: proc_id = proc.UniqueProcessId @@ -93,7 +102,7 @@ class DllList(interfaces.plugins.PluginInterface): for entry in proc.load_order_modules(): - BaseDllName = FullDllName = renderers.UnreadableValue() + BaseDllName = FullDllName = DllLoadTime = renderers.UnreadableValue() try: BaseDllName = entry.BaseDllName.get_string() # We assume that if the BaseDllName points to an invalid buffer, so will FullDllName @@ -101,8 +110,14 @@ class DllList(interfaces.plugins.PluginInterface): except exceptions.InvalidAddressException: pass + if dll_load_time_field: + try: + DllLoadTime = conversion.wintime_to_datetime(entry.LoadTime.QuadPart) + except: + pass + dumped = False - if self.config['dump']: + if self.config.get('dump'): filedata = self.dump_pe(self.context, pe_table_name, entry, proc_layer_name) if filedata: filedata.preferred_filename = "pid.{0}.".format(proc_id) + filedata.preferred_filename @@ -113,13 +128,27 @@ class DllList(interfaces.plugins.PluginInterface): proc.ImageFileName.cast("string", max_length = proc.ImageFileName.vol.count, errors = 'replace'), format_hints.Hex(entry.DllBase), - format_hints.Hex(entry.SizeOfImage), BaseDllName, FullDllName, dumped)) + format_hints.Hex(entry.SizeOfImage), BaseDllName, FullDllName, DllLoadTime, dumped)) + + def generate_timeline(self): + for row in self._generator(pslist.PsList.list_processes(context = self.context, + layer_name = self.config['primary'], + symbol_table = self.config['nt_symbols'], + filter_func = pslist.PsList.create_pid_filter(None))): + _depth, row_data = row + if not isinstance(row_data[6], datetime.datetime): + continue + description = "DLL Load: Process {} {} Loaded {} ({}) Size {} Offset {}".format(row_data[0], row_data[1], + row_data[4], row_data[5], + row_data[3], row_data[2]) + yield (description, timeliner.TimeLinerType.CREATED, row_data[6]) def run(self): filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None)) return renderers.TreeGrid([("PID", int), ("Process", str), ("Base", format_hints.Hex), - ("Size", format_hints.Hex), ("Name", str), ("Path", str), ("Dumped", bool)], + ("Size", format_hints.Hex), ("Name", str), ("Path", str), + ("LoadTime", datetime.datetime), ("Dumped", bool)], self._generator( pslist.PsList.list_processes(context = self.context, layer_name = self.config['primary'], diff --git a/volatility/framework/plugins/windows/filescan.py b/volatility/framework/plugins/windows/filescan.py index 45aa5e68d..5eb3b02ce 100644 --- a/volatility/framework/plugins/windows/filescan.py +++ b/volatility/framework/plugins/windows/filescan.py @@ -55,10 +55,11 @@ class FileScan(interfaces.plugins.PluginInterface): except exceptions.InvalidAddressException: continue - yield (0, (format_hints.Hex(fileobj.vol.offset), file_name)) + yield (0, (format_hints.Hex(fileobj.vol.offset), file_name, fileobj.Size)) def run(self): return renderers.TreeGrid([ ("Offset", format_hints.Hex), ("Name", str), + ("Size", int) ], self._generator()) diff --git a/volatility/framework/plugins/windows/netscan.py b/volatility/framework/plugins/windows/netscan.py index c2c05c0dc..b9a2dd18a 100644 --- a/volatility/framework/plugins/windows/netscan.py +++ b/volatility/framework/plugins/windows/netscan.py @@ -16,7 +16,7 @@ from volatility.plugins.windows import info, poolscanner vollog = logging.getLogger(__name__) -class NetScan(interfaces.plugins.PluginInterface): +class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): """Scans for network objects present in a particular windows memory image.""" _version = (1, 0, 0) @@ -28,8 +28,8 @@ class NetScan(interfaces.plugins.PluginInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), - requirements.PluginRequirement(name = 'poolscanner', plugin = poolscanner.PoolScanner, version = (1, 0, 0)), - requirements.PluginRequirement(name = 'info', plugin = info.Info, version= (1, 0, 0)), + requirements.VersionRequirement(name='poolscanner', component=poolscanner.PoolScanner, version=(1, 0, 0)), + requirements.VersionRequirement(name='info', component=info.Info, version=(1, 0, 0)), requirements.BooleanRequirement(name = 'include-corrupt', description = "Radically eases result validation. This will show partially overwritten data. WARNING: the results are likely to include garbage and/or corrupt data. Be cautious!", default = False, @@ -332,6 +332,21 @@ class NetScan(interfaces.plugins.PluginInterface): # this should not happen therefore we log it. vollog.debug("Found network object unsure of its type: {} of type {}".format(netw_obj, type(netw_obj))) + def generate_timeline(self): + for row in self._generator(): + _depth, row_data = row + # Skip network connections without creation time + if not isinstance(row_data[9], datetime.datetime): + continue + row_data = ["N/A" if isinstance(i, renderers.UnreadableValue) or isinstance(i, renderers.UnparsableValue) + else i for i in row_data] + description = "Network connection: Process {} {} Local Address {}:{} " \ + "Remote Address {}:{} State {} Protocol {} ".format(row_data[7], row_data[8], + row_data[2], row_data[3], + row_data[4], row_data[5], + row_data[6], row_data[1]) + yield (description, timeliner.TimeLinerType.CREATED, row_data[9]) + def run(self): show_corrupt_results = self.config.get('include-corrupt', None) diff --git a/volatility/framework/plugins/windows/pslist.py b/volatility/framework/plugins/windows/pslist.py index c5a292895..069eab8f7 100644 --- a/volatility/framework/plugins/windows/pslist.py +++ b/volatility/framework/plugins/windows/pslist.py @@ -198,7 +198,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): def generate_timeline(self): for row in self._generator(): _depth, row_data = row - description = "Process: {} ({})".format(row_data[2], row_data[3]) + description = "Process: {} {} ({})".format(row_data[0], row_data[2], row_data[3]) yield (description, timeliner.TimeLinerType.CREATED, row_data[8]) yield (description, timeliner.TimeLinerType.MODIFIED, row_data[9]) diff --git a/volatility/framework/plugins/windows/psscan.py b/volatility/framework/plugins/windows/psscan.py index 89d27a83a..80595fb98 100644 --- a/volatility/framework/plugins/windows/psscan.py +++ b/volatility/framework/plugins/windows/psscan.py @@ -76,7 +76,7 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): def generate_timeline(self): for row in self._generator(): _depth, row_data = row - description = "Process: {} ({})".format(row_data[2], row_data[3]) + description = "Process: {} {} ({})".format(row_data[0], row_data[2], row_data[3]) yield (description, timeliner.TimeLinerType.CREATED, row_data[8]) yield (description, timeliner.TimeLinerType.MODIFIED, row_data[9])