Skip to content

Commit 64ecd65

Browse files
committed
Windows: Improve type-hints in thrdscan, extensions
This improves type-hinting in the `ThrdScan` class and in the `ETHREAD` extension class through narrowing the return type of some methods from `interfaces.objects.ObjectInterface` to their actual return type, `extensions.ETHREAD`. Also creates a `NamedTuple` for holding thread info, which cleans up the type signature and makes the returned value easier for consumers to use.
1 parent 842d0e7 commit 64ecd65

File tree

2 files changed

+41
-46
lines changed

2 files changed

+41
-46
lines changed

volatility3/framework/plugins/windows/thrdscan.py

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
##
22
## plugin for testing addition of threads scan support to poolscanner.py
33
##
4-
import logging
54
import datetime
6-
from typing import Callable, Iterable, Tuple, Optional, Dict
5+
import logging
6+
from typing import Callable, Dict, NamedTuple, Optional, Union, Tuple, Iterator
77

8-
from volatility3.framework import renderers, interfaces, exceptions
8+
from volatility3.framework import exceptions, interfaces, objects, renderers
99
from volatility3.framework.configuration import requirements
1010
from volatility3.framework.renderers import format_hints
11-
from volatility3.plugins.windows import poolscanner, pe_symbols
11+
from volatility3.framework.symbols.windows import extensions as win_extensions
1212
from volatility3.plugins import timeliner
13+
from volatility3.plugins.windows import pe_symbols, poolscanner
1314

1415
vollog = logging.getLogger(__name__)
1516

@@ -21,6 +22,17 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
2122
_required_framework_version = (2, 6, 0)
2223
_version = (2, 0, 0)
2324

25+
class ThreadInfo(NamedTuple):
26+
offset: int
27+
pid: objects.Pointer
28+
tid: objects.Pointer
29+
start_addr: objects.Pointer
30+
start_path: Optional[str]
31+
win32_start_addr: objects.Pointer
32+
win32_start_path: Optional[str]
33+
create_time: Union[datetime.datetime, interfaces.renderers.BaseAbsentValue]
34+
exit_time: Union[datetime.datetime, interfaces.renderers.BaseAbsentValue]
35+
2436
def __init__(self, *args, **kwargs):
2537
super().__init__(*args, **kwargs)
2638
self.implementation = self.scan_threads
@@ -51,7 +63,7 @@ def scan_threads(
5163
cls,
5264
context: interfaces.context.ContextInterface,
5365
module_name: str,
54-
) -> Iterable[interfaces.objects.ObjectInterface]:
66+
) -> Iterator[win_extensions.ETHREAD]:
5567
"""Scans for threads using the poolscanner module and constraints.
5668
5769
Args:
@@ -77,19 +89,9 @@ def scan_threads(
7789
@classmethod
7890
def gather_thread_info(
7991
cls,
80-
ethread: interfaces.objects.ObjectInterface,
81-
vads_cache: Dict[int, pe_symbols.ranges_type] = None,
82-
) -> Tuple[
83-
int,
84-
int,
85-
int,
86-
int,
87-
Optional[str],
88-
int,
89-
Optional[str],
90-
Optional[datetime.datetime],
91-
Optional[datetime.datetime],
92-
]:
92+
ethread: win_extensions.ETHREAD,
93+
vads_cache: Optional[Dict[int, pe_symbols.ranges_type]] = None,
94+
) -> Optional[ThreadInfo]:
9395
try:
9496
thread_offset = ethread.vol.offset
9597
owner_proc_pid = ethread.Cid.UniqueProcess
@@ -135,19 +137,19 @@ def gather_thread_info(
135137
start_path = None
136138
win32start_path = None
137139

138-
return (
139-
format_hints.Hex(thread_offset),
140+
return cls.ThreadInfo(
141+
thread_offset,
140142
owner_proc_pid,
141143
thread_tid,
142-
format_hints.Hex(thread_start_addr),
144+
thread_start_addr,
143145
start_path,
144-
format_hints.Hex(thread_win32start_addr),
146+
thread_win32start_addr,
145147
win32start_path,
146148
thread_create_time,
147149
thread_exit_time,
148150
)
149151

150-
def _generator(self, filter_func: Callable):
152+
def _generator(self, filter_func: Callable) -> Iterator[Tuple[int, Tuple]]:
151153
kernel_name = self.config["kernel"]
152154

153155
vads_cache: Dict[int, pe_symbols.ranges_type] = {}
@@ -156,27 +158,16 @@ def _generator(self, filter_func: Callable):
156158
info = self.gather_thread_info(ethread, vads_cache)
157159

158160
if info:
159-
(
160-
offset,
161-
pid,
162-
tid,
163-
start_addr,
164-
start_path,
165-
win32start_addr,
166-
win32start_path,
167-
create_time,
168-
exit_time,
169-
) = info
170161
yield 0, (
171-
offset,
172-
pid,
173-
tid,
174-
start_addr,
175-
start_path or renderers.NotAvailableValue(),
176-
win32start_addr,
177-
win32start_path or renderers.NotAvailableValue(),
178-
create_time,
179-
exit_time,
162+
format_hints.Hex(info.offset),
163+
info.pid,
164+
info.tid,
165+
format_hints.Hex(info.start_addr),
166+
info.start_path or renderers.NotAvailableValue(),
167+
format_hints.Hex(info.win32_start_addr),
168+
info.win32_start_path or renderers.NotAvailableValue(),
169+
info.create_time,
170+
info.exit_time,
180171
)
181172

182173
def generate_timeline(self):

volatility3/framework/symbols/windows/extensions/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -568,16 +568,20 @@ def is_valid(self) -> bool:
568568
# passed all validations
569569
return True
570570

571-
def get_create_time(self):
571+
def get_create_time(
572+
self,
573+
) -> Union[datetime.datetime, interfaces.renderers.BaseAbsentValue]:
572574
# For Windows XPs
573575
if self.has_member("ThreadsProcess"):
574576
return conversion.wintime_to_datetime(self.CreateTime.QuadPart >> 3)
575577
return conversion.wintime_to_datetime(self.CreateTime.QuadPart)
576578

577-
def get_exit_time(self):
579+
def get_exit_time(
580+
self,
581+
) -> Union[datetime.datetime, interfaces.renderers.BaseAbsentValue]:
578582
return conversion.wintime_to_datetime(self.ExitTime.QuadPart)
579583

580-
def owning_process(self) -> interfaces.objects.ObjectInterface:
584+
def owning_process(self) -> "EPROCESS":
581585
"""Return the EPROCESS that owns this thread."""
582586

583587
# For Windows XPs

0 commit comments

Comments
 (0)