diff --git a/volatility3/framework/plugins/windows/cmdscan.py b/volatility3/framework/plugins/windows/cmdscan.py index b92d7efab..09cb0f316 100644 --- a/volatility3/framework/plugins/windows/cmdscan.py +++ b/volatility3/framework/plugins/windows/cmdscan.py @@ -14,17 +14,9 @@ from volatility3.framework.configuration import requirements from volatility3.framework.layers import scanners from volatility3.framework.objects import utility from volatility3.framework.renderers import format_hints -from volatility3.plugins.windows import pslist, vadinfo, info, verinfo, consoles -from volatility3.plugins.windows.registry import hivelist +from volatility3.plugins.windows import pslist, consoles -try: - import capstone - - has_capstone = True -except ImportError: - has_capstone = False - vollog = logging.getLogger(__name__) @@ -32,6 +24,7 @@ class CmdScan(interfaces.plugins.PluginInterface): """Looks for Windows Command History lists""" _required_framework_version = (2, 4, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls): @@ -46,7 +39,7 @@ class CmdScan(interfaces.plugins.PluginInterface): name="pslist", component=pslist.PsList, version=(2, 0, 0) ), requirements.PluginRequirement( - name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0) + name="consoles", plugin=consoles.Consoles, version=(1, 0, 0) ), requirements.BooleanRequirement( name="no_registry", @@ -70,7 +63,7 @@ class CmdScan(interfaces.plugins.PluginInterface): size_filter: Optional[int] = 0x40000000, ) -> List[Tuple[int, int]]: """ - Returns vads of a process with smaller than size_filter + Returns vads of a process with size smaller than size_filter Args: conhost_proc: the process object for conhost.exe @@ -114,12 +107,10 @@ class CmdScan(interfaces.plugins.PluginInterface): Returns: The conhost process object, the command history structure, a dictionary of properties for - that command historyn structure. + that command history structure. """ - conhost_symbol_table = consoles.Consoles.create_conhost_symbol_table( - context, kernel_layer_name, kernel_table_name, config_path - ) + conhost_symbol_table = None for conhost_proc, proc_layer_name in consoles.Consoles.find_conhost_proc(procs): if not conhost_proc: @@ -143,11 +134,22 @@ class CmdScan(interfaces.plugins.PluginInterface): proc_layer = context.layers[proc_layer_name] + if conhost_symbol_table is None: + conhost_symbol_table = consoles.Consoles.create_conhost_symbol_table( + context, + kernel_layer_name, + kernel_table_name, + config_path, + proc_layer_name, + conhostexe_base, + ) + conhost_module = context.module( conhost_symbol_table, proc_layer_name, offset=conhostexe_base ) sections = cls.get_filtered_vads(conhost_proc) + found_history_for_proc = False # scan for potential _COMMAND_HISTORY structures by using the CommandHistorySize for max_history_value in max_history: max_history_bytes = struct.pack("H", max_history_value) @@ -258,7 +260,12 @@ class CmdScan(interfaces.plugins.PluginInterface): f"reading {command_history} encountered exception {e}" ) - yield conhost_proc, command_history, command_history_properties + if command_history and command_history_properties: + found_history_for_proc = True + yield conhost_proc, command_history, command_history_properties + + if not found_history_for_proc: + yield conhost_proc, command_history or None, [] def _generator( self, procs: Generator[interfaces.objects.ObjectInterface, None, None] @@ -276,19 +283,18 @@ class CmdScan(interfaces.plugins.PluginInterface): no_registry = self.config.get("no_registry") if no_registry is False: - max_history, _max_buffers = ( - consoles.Consoles.get_console_settings_from_registry( - self.context, - self.config_path, - kernel.layer_name, - kernel.symbol_table_name, - max_history, - [], - ) + max_history, _ = consoles.Consoles.get_console_settings_from_registry( + self.context, + self.config_path, + kernel.layer_name, + kernel.symbol_table_name, + max_history, + [], ) vollog.debug(f"Possible CommandHistorySize values: {max_history}") + proc = None for ( proc, command_history, @@ -302,13 +308,14 @@ class CmdScan(interfaces.plugins.PluginInterface): max_history, ): process_name = utility.array_to_string(proc.ImageFileName) + process_pid = proc.UniqueProcessId if command_history and command_history_properties: for command_history_property in command_history_properties: yield ( command_history_property["level"], ( - proc.UniqueProcessId, + process_pid, process_name, format_hints.Hex(command_history.vol.offset), command_history_property["name"], @@ -322,6 +329,25 @@ class CmdScan(interfaces.plugins.PluginInterface): str(command_history_property["data"]), ), ) + else: + yield ( + 0, + ( + process_pid, + process_name, + ( + format_hints.Hex(command_history.vol.offset) + if command_history + else renderers.NotApplicableValue() + ), + "_COMMAND_HISTORY", + renderers.NotApplicableValue(), + "History Not Found", + ), + ) + + if proc is None: + vollog.warn("No conhost.exe processes found.") def _conhost_proc_filter(self, proc): """ diff --git a/volatility3/framework/plugins/windows/consoles.py b/volatility3/framework/plugins/windows/consoles.py index 89f9fcc80..a2eade321 100644 --- a/volatility3/framework/plugins/windows/consoles.py +++ b/volatility3/framework/plugins/windows/consoles.py @@ -17,19 +17,11 @@ from volatility3.framework.layers import scanners from volatility3.framework.objects import utility from volatility3.framework.renderers import format_hints from volatility3.framework.symbols import intermed -from volatility3.framework.symbols.windows import pdbutil from volatility3.framework.symbols.windows.extensions import pe, consoles -from volatility3.plugins.windows import pslist, vadinfo, info, verinfo +from volatility3.plugins.windows import pslist, info, verinfo from volatility3.plugins.windows.registry import hivelist -try: - import capstone - - has_capstone = True -except ImportError: - has_capstone = False - vollog = logging.getLogger(__name__) @@ -37,6 +29,7 @@ class Consoles(interfaces.plugins.PluginInterface): """Looks for Windows console buffers""" _required_framework_version = (2, 4, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls): @@ -53,9 +46,6 @@ class Consoles(interfaces.plugins.PluginInterface): requirements.VersionRequirement( name="verinfo", component=verinfo.VerInfo, version=(1, 0, 0) ), - requirements.VersionRequirement( - name="pdbutil", component=pdbutil.PDBUtility, version=(1, 0, 0) - ), requirements.PluginRequirement( name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0) ), @@ -443,6 +433,7 @@ class Consoles(interfaces.plugins.PluginInterface): conhost_symbol_table, proc_layer_name, offset=conhostexe_base ) + found_console_info_for_proc = False # scan for potential _CONSOLE_INFORMATION structures by using the CommandHistorySize for max_history_value in max_history: max_history_bytes = struct.pack("H", max_history_value) @@ -790,7 +781,12 @@ class Consoles(interfaces.plugins.PluginInterface): ) continue - yield conhost_proc, console_info, console_properties + if console_info and console_properties: + found_console_info_for_proc = True + yield conhost_proc, console_info, console_properties + + if not found_console_info_for_proc: + yield conhost_proc, console_info or None, [] @classmethod def get_console_settings_from_registry( @@ -903,8 +899,20 @@ class Consoles(interfaces.plugins.PluginInterface): ), ) else: - vollog.warn( - f"_CONSOLE_INFORMATION not found for {process_name} with pid {process_pid}." + yield ( + 0, + ( + process_pid, + process_name, + ( + format_hints.Hex(console_info.vol.offset) + if console_info + else renderers.NotApplicableValue() + ), + "_CONSOLE_INFORMATION", + renderers.NotApplicableValue(), + "Console Information Not Found", + ), ) if proc is None: