#816 initial PR comment fixes

This commit is contained in:
Dave Lassalle
2024-10-07 12:26:17 -05:00
parent ece2dbe9a7
commit 0dd082b7fd
14 changed files with 111 additions and 427 deletions
@@ -61,7 +61,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
cls,
conhost_proc: interfaces.context.ContextInterface,
size_filter: Optional[int] = 0x40000000,
) -> List[Tuple[int, int]]:
) -> Generator[Tuple[int, int], None, None]:
"""
Returns vads of a process with size smaller than size_filter
@@ -73,20 +73,17 @@ class CmdScan(interfaces.plugins.PluginInterface):
vad_base: the base address
vad_size: the size of the VAD
"""
vads = []
for vad in conhost_proc.get_vad_root().traverse():
base = vad.get_start()
if vad.get_size() < size_filter:
vads.append((base, vad.get_size()))
return vads
yield (base, vad.get_size())
@classmethod
def get_command_history(
cls,
context: interfaces.context.ContextInterface,
kernel_layer_name: str,
kernel_table_name: str,
kernel_symbol_table_name: str,
config_path: str,
procs: Generator[interfaces.objects.ObjectInterface, None, None],
max_history: Set[int],
@@ -100,7 +97,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
Args:
context: The context to retrieve required elements (layers, symbol tables) from
kernel_layer_name: The name of the layer on which to operate
kernel_table_name: The name of the table containing the kernel symbols
kernel_symbol_table_name: The name of the table containing the kernel symbols
config_path: The config path where to find symbol files
procs: list of process objects
max_history: an initial set of CommandHistorySize values
@@ -138,7 +135,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
conhost_symbol_table = consoles.Consoles.create_conhost_symbol_table(
context,
kernel_layer_name,
kernel_table_name,
kernel_symbol_table_name,
config_path,
proc_layer_name,
conhostexe_base,
@@ -147,6 +144,9 @@ class CmdScan(interfaces.plugins.PluginInterface):
conhost_module = context.module(
conhost_symbol_table, proc_layer_name, offset=conhostexe_base
)
command_count_max_offset = conhost_module.get_type(
"_COMMAND_HISTORY"
).relative_child_offset("CommandCountMax")
sections = cls.get_filtered_vads(conhost_proc)
found_history_for_proc = False
@@ -161,15 +161,13 @@ class CmdScan(interfaces.plugins.PluginInterface):
scanners.BytesScanner(max_history_bytes),
sections=sections,
):
command_history = None
command_history_properties = []
try:
command_history = conhost_module.object(
"_COMMAND_HISTORY",
offset=address
- conhost_module.get_type(
"_COMMAND_HISTORY"
).relative_child_offset("CommandCountMax"),
offset=address - command_count_max_offset,
absolute=True,
)
@@ -184,13 +182,13 @@ class CmdScan(interfaces.plugins.PluginInterface):
"level": 0,
"name": "_COMMAND_HISTORY",
"address": command_history.vol.offset,
"data": "",
"data": None,
}
)
command_history_properties.append(
{
"level": 1,
"name": f"_COMMAND_HISTORY.Application",
"name": "_COMMAND_HISTORY.Application",
"address": command_history.Application.vol.offset,
"data": command_history.get_application(),
}
@@ -198,7 +196,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
command_history_properties.append(
{
"level": 1,
"name": f"_COMMAND_HISTORY.ProcessHandle",
"name": "_COMMAND_HISTORY.ProcessHandle",
"address": command_history.ConsoleProcessHandle.ProcessHandle.vol.offset,
"data": hex(
command_history.ConsoleProcessHandle.ProcessHandle
@@ -208,7 +206,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
command_history_properties.append(
{
"level": 1,
"name": f"_COMMAND_HISTORY.CommandCount",
"name": "_COMMAND_HISTORY.CommandCount",
"address": None,
"data": command_history.CommandCount,
}
@@ -216,7 +214,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
command_history_properties.append(
{
"level": 1,
"name": f"_COMMAND_HISTORY.LastDisplayed",
"name": "_COMMAND_HISTORY.LastDisplayed",
"address": command_history.LastDisplayed.vol.offset,
"data": command_history.LastDisplayed,
}
@@ -224,7 +222,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
command_history_properties.append(
{
"level": 1,
"name": f"_COMMAND_HISTORY.CommandCountMax",
"name": "_COMMAND_HISTORY.CommandCountMax",
"address": command_history.CommandCountMax.vol.offset,
"data": command_history.CommandCountMax,
}
@@ -233,7 +231,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
command_history_properties.append(
{
"level": 1,
"name": f"_COMMAND_HISTORY.CommandBucket",
"name": "_COMMAND_HISTORY.CommandBucket",
"address": command_history.CommandBucket.vol.offset,
"data": "",
}
@@ -248,7 +246,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
"level": 2,
"name": f"_COMMAND_HISTORY.CommandBucket_Command_{cmd_index}",
"address": bucket_cmd.vol.offset,
"data": bucket_cmd.get_command(),
"data": bucket_cmd.get_command_string(),
}
)
except Exception as e:
@@ -264,6 +262,9 @@ class CmdScan(interfaces.plugins.PluginInterface):
found_history_for_proc = True
yield conhost_proc, command_history, command_history_properties
# if found_history_for_proc is still False, then none of the scanned locations found
# a valid _COMMAND_HISTORY for the process, so yield the process and some empty data
# so the process can at least be reported that it was found with no history
if not found_history_for_proc:
yield conhost_proc, command_history or None, []
@@ -349,7 +350,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
if proc is None:
vollog.warn("No conhost.exe processes found.")
def _conhost_proc_filter(self, proc):
def _conhost_proc_filter(self, proc: interfaces.objects.ObjectInterface):
"""
Used to filter to only conhost.exe processes
"""
@@ -209,7 +209,7 @@ class Consoles(interfaces.plugins.PluginInterface):
(10, 0, 20348, 2520): "consoles-win10-20348-2461-x64",
(10, 0, 22000, 0): "consoles-win10-22000-x64",
(10, 0, 22621, 1): "consoles-win10-22621-x64",
(10, 0, 22621, 3672): "consoles-win10-22621-3672-x64",
(10, 0, 22621, 3527): "consoles-win10-22621-3527-x64",
(10, 0, 25398, 0): "consoles-win10-22000-x64",
}
@@ -706,7 +706,7 @@ class Consoles(interfaces.plugins.PluginInterface):
"level": 3,
"name": f"_CONSOLE_INFORMATION.HistoryList.CommandHistory_{index}_Command_{cmd_index}",
"address": bucket_cmd.vol.offset,
"data": bucket_cmd.get_command(),
"data": bucket_cmd.get_command_string(),
}
)
except Exception as e:
@@ -918,7 +918,7 @@ class Consoles(interfaces.plugins.PluginInterface):
if proc is None:
vollog.warn("No conhost.exe processes found.")
def _conhost_proc_filter(self, proc):
def _conhost_proc_filter(self, proc: interfaces.objects.ObjectInterface) -> bool:
"""
Used to filter to only conhost.exe processes
"""
@@ -632,14 +632,14 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
@@ -632,14 +632,14 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
@@ -622,14 +622,14 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
@@ -622,14 +622,14 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
@@ -598,7 +598,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -628,61 +628,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -598,7 +598,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -628,61 +628,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -598,7 +598,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -628,61 +628,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -624,7 +624,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -654,61 +654,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -624,7 +624,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -654,61 +654,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -624,7 +624,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -654,61 +654,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -625,7 +625,7 @@
"ExeName": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
@@ -655,61 +655,20 @@
"Source": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 16
},
"Target": {
"type": {
"kind": "struct",
"name": "_ALIAS_STRING"
"name": "_COMMAND"
},
"offset": 48
}
},
"kind": "struct",
"size": 32
},
"_ALIAS_STRING": {
"fields": {
"Chars": {
"type": {
"count": 1,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
},
"offset": 0
},
"Pointer": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "string"
}
},
"offset": 0
},
"Length": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 16
},
"Allocated": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 24
}
},
"kind": "struct",
"size": 32
}
},
"metadata": {
@@ -2,17 +2,21 @@
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
from volatility3.framework import objects
import logging
from typing import Generator, List, Union, Tuple
from volatility3.framework import objects, interfaces
from volatility3.framework import constants
vollog = logging.getLogger(__name__)
class ROW(objects.StructType):
"""A Row Structure."""
def _valid_dbcs(self, c, n):
def _valid_dbcs(self, dbcs_attr: int, text_attr_msb: int) -> bool:
# TODO this need more research and testing
# https://github.com/search?q=repo%3Amicrosoft%2Fterminal+DbcsAttr&type=code
valid = n == 0 and c in (
valid = text_attr_msb == 0 and dbcs_attr in (
0x0,
0x1,
0x2,
@@ -43,11 +47,11 @@ class ROW(objects.StructType):
0xF0,
0xA0,
)
if n == 0 and not valid:
print("Bad Dbcs Attribute {}".format(hex(c)))
if text_attr_msb == 0 and not valid:
vollog.debug(f"Bad Dbcs Attribute {dbcs_attr:#x}")
return valid
def get_text(self, truncate=True):
def get_text(self, truncate: bool = True) -> str:
"""A convenience method to extract the text from the _ROW. The _ROW
contains a pointer CharRow to an array of CharRowCell objects. Each
CharRowCell contains the wide character and an attribute. Enumerating
@@ -70,7 +74,6 @@ class ROW(objects.StructType):
for i in range(0, len(char_row), 3)
)
except Exception as e:
print(e)
line = ""
if truncate:
@@ -82,62 +85,26 @@ class ROW(objects.StructType):
class ALIAS(objects.StructType):
"""An Alias Structure"""
def get_source(self):
if self.Source.Length < 8:
return self.Source.Chars.cast(
"string",
encoding="utf-16",
errors="replace",
max_length=self.Source.Length * 2,
)
elif self.Source.Length < 1024:
return self.Source.Pointer.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
)
def get_source(self) -> Union[str, None]:
return self.Source.get_command_string()
return None
def get_target(self):
if self.Target.Length < 8:
return self.Target.Chars.cast(
"string",
encoding="utf-16",
errors="replace",
max_length=self.Target.Length * 2,
)
elif self.Target.Length < 1024:
return self.Target.Pointer.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
)
return None
def get_target(self) -> Union[str, None]:
return self.Target.get_command_string()
class EXE_ALIAS_LIST(objects.StructType):
"""An Exe Alias List Structure"""
def get_exename(self):
def get_exename(self) -> Union[str, None]:
exe_name = self.ExeName
# Windows 10 22000 and Server 20348 removed the Pointer
if isinstance(exe_name, objects.Pointer):
exe_name = exe_name.dereference()
return exe_name.get_string()
if self.ExeName.Length < 8:
return self.ExeName.Chars.cast(
"string",
encoding="utf-16",
errors="replace",
max_length=self.ExeName.Length * 2,
)
elif self.ExeName.Length < 1024:
return self.ExeName.Pointer.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
)
return exe_name.get_command_string()
return None
def get_aliases(self):
def get_aliases(self) -> Generator[interfaces.objects.ObjectInterface, None, None]:
"""Generator for the individual aliases for a
particular executable."""
for alias in self.AliasList.to_list(
@@ -151,7 +118,7 @@ class SCREEN_INFORMATION(objects.StructType):
"""A Screen Information Structure."""
@property
def ScreenX(self):
def ScreenX(self) -> int:
# 22000 change from an array of pointers to _ROW to an array of _ROW
row = self.TextBufferInfo.BufferRows.Rows[0]
if hasattr(row, "Row"):
@@ -160,10 +127,10 @@ class SCREEN_INFORMATION(objects.StructType):
return row.RowLength2
@property
def ScreenY(self):
def ScreenY(self) -> int:
return self.TextBufferInfo.BufferCapacity
def _truncate_rows(self, rows):
def _truncate_rows(self, rows: List[str]) -> List[str]:
"""To truncate empty rows at the end, walk the list
backwards and get the last non-empty row. Use that
row index to splice. Rows are created based on the
@@ -187,7 +154,9 @@ class SCREEN_INFORMATION(objects.StructType):
return rows
def get_buffer(self, truncate_rows=True, truncate_lines=True):
def get_buffer(
self, truncate_rows: bool = True, truncate_lines: bool = True
) -> List[str]:
"""Get the screen buffer.
The screen buffer is comprised of the screen's Y
@@ -206,7 +175,7 @@ class SCREEN_INFORMATION(objects.StructType):
capacity = self.TextBufferInfo.BufferCapacity
start = self.TextBufferInfo.BufferStart
buffer_rows = self.TextBufferInfo.BufferRows.dereference()
buffer_rows = self.TextBufferInfo.BufferRows
buffer_rows.Rows.count = self.TextBufferInfo.BufferCapacity
for i in range(capacity):
@@ -234,10 +203,10 @@ class CONSOLE_INFORMATION(objects.StructType):
"""A Console Information Structure."""
@property
def ScreenBuffer(self):
def ScreenBuffer(self) -> interfaces.objects.ObjectInterface:
return self.GetScreenBuffer
def is_valid(self, max_buffers=4) -> bool:
def is_valid(self, max_buffers: int = 4) -> bool:
"""Determine if the structure is valid."""
# Last displayed must be between -1 and max
@@ -249,7 +218,7 @@ class CONSOLE_INFORMATION(objects.StructType):
return True
def get_screens(self):
def get_screens(self) -> Generator[interfaces.objects.ObjectInterface, None, None]:
"""Generator for screens in the console.
A console can have multiple screen buffers at a time,
@@ -277,14 +246,18 @@ class CONSOLE_INFORMATION(objects.StructType):
seen.add(cur.vol.offset)
cur = cur.Next
def get_histories(self):
for cmd_hist in self.HistoryList.dereference().to_list(
def get_histories(
self,
) -> Generator[interfaces.objects.ObjectInterface, None, None]:
for cmd_hist in self.HistoryList.to_list(
f"{self.get_symbol_table_name()}{constants.BANG}_COMMAND_HISTORY",
"ListEntry",
):
yield cmd_hist
def get_exe_aliases(self):
def get_exe_aliases(
self,
) -> Generator[interfaces.objects.ObjectInterface, None, None]:
exe_alias_list = self.ExeAliasList
# Windows 10 22000 and Server 20348 made this a Pointer
if isinstance(exe_alias_list, objects.Pointer):
@@ -295,14 +268,16 @@ class CONSOLE_INFORMATION(objects.StructType):
):
yield exe_alias_list_item
def get_processes(self):
for proc in self.ConsoleProcessList.dereference().to_list(
def get_processes(
self,
) -> Generator[interfaces.objects.ObjectInterface, None, None]:
for proc in self.ConsoleProcessList.to_list(
f"{self.get_symbol_table_name()}{constants.BANG}_CONSOLE_PROCESS_LIST",
"ListEntry",
):
yield proc
def get_title(self):
def get_title(self) -> Union[str, None]:
try:
return self.Title.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
@@ -310,7 +285,7 @@ class CONSOLE_INFORMATION(objects.StructType):
except Exception:
return ""
def get_original_title(self):
def get_original_title(self) -> Union[str, None]:
try:
return self.OriginalTitle.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
@@ -322,7 +297,7 @@ class CONSOLE_INFORMATION(objects.StructType):
class COMMAND(objects.StructType):
"""A Command Structure"""
def is_valid(self):
def is_valid(self) -> bool:
if (
self.Length < 1
or self.Allocated < 1
@@ -333,7 +308,7 @@ class COMMAND(objects.StructType):
return True
def get_command(self):
def get_command_string(self) -> Union[str, None]:
if self.Length < 8:
return self.Chars.cast(
"string",
@@ -343,7 +318,10 @@ class COMMAND(objects.StructType):
)
elif self.Length < 1024:
return self.Pointer.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
"string",
encoding="utf-16",
errors="replace",
max_length=self.Length * 2,
)
return None
@@ -353,17 +331,17 @@ class COMMAND_HISTORY(objects.StructType):
"""A Command History Structure."""
@property
def CommandCount(self):
def CommandCount(self) -> int:
command_type = self.get_symbol_table_name() + constants.BANG + "_COMMAND"
command_size = self._context.symbol_space.get_type(command_type).size
return int((self.CommandBucket.End - self.CommandBucket.Begin) / command_size)
@property
def ProcessHandle(self):
def ProcessHandle(self) -> int:
"""Allow ProcessHandle to be referenced regardless of OS version"""
return self.ConsoleProcessHandle.ProcessHandle
def is_valid(self, max_history=50):
def is_valid(self, max_history: int = 50) -> bool:
# The count must be between zero and max
if self.CommandCount < 0 or self.CommandCount > max_history:
return False
@@ -382,22 +360,12 @@ class COMMAND_HISTORY(objects.StructType):
return True
def get_application(self):
if self.Application.Length < 8:
return self.Application.Chars.cast(
"string",
encoding="utf-16",
errors="replace",
max_length=self.Application.Length * 2,
)
elif self.Application.Length < 1024:
return self.Application.Pointer.dereference().cast(
"string", encoding="utf-16", errors="replace", max_length=512
)
def get_application(self) -> Union[str, None]:
return self.Application.get_command_string()
return None
def scan_command_bucket(self, end=None):
def scan_command_bucket(
self, end: Union[int, None] = None
) -> Generator[Tuple[int, interfaces.objects.ObjectInterface], None, None]:
"""Brute force print all strings pointed to by the CommandBucket entries by
going to greater of EndCapacity or CommandCountMax*sizeof(_COMMAND)"""
@@ -418,7 +386,9 @@ class COMMAND_HISTORY(objects.StructType):
if cmd.is_valid():
yield i, cmd
def get_commands(self):
def get_commands(
self,
) -> Generator[Tuple[int, interfaces.objects.ObjectInterface], None, None]:
"""Generator for commands in the history buffer.
The CommandBucket is an array of pointers to _COMMAND