33##
44import logging
55import datetime
6- from typing import Callable , Iterable
6+ from typing import Callable , Iterable , Tuple , Optional , Dict
77
88from volatility3 .framework import renderers , interfaces , exceptions
99from volatility3 .framework .configuration import requirements
1010from volatility3 .framework .renderers import format_hints
11- from volatility3 .plugins .windows import poolscanner
11+ from volatility3 .plugins .windows import poolscanner , pe_symbols
1212from volatility3 .plugins import timeliner
1313
1414vollog = logging .getLogger (__name__ )
@@ -19,7 +19,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
1919
2020 # version 2.6.0 adds support for scanning for 'Ethread' structures by pool tags
2121 _required_framework_version = (2 , 6 , 0 )
22- _version = (1 , 1 , 0 )
22+ _version = (2 , 0 , 0 )
2323
2424 def __init__ (self , * args , ** kwargs ):
2525 super ().__init__ (* args , ** kwargs )
@@ -67,39 +67,109 @@ def scan_threads(
6767 yield mem_object
6868
6969 @classmethod
70- def gather_thread_info (cls , ethread ):
70+ def gather_thread_info (
71+ cls ,
72+ ethread : interfaces .objects .ObjectInterface ,
73+ vads_cache : Dict [int , pe_symbols .ranges_type ] = None ,
74+ ) -> Tuple [
75+ int ,
76+ int ,
77+ int ,
78+ int ,
79+ Optional [str ],
80+ int ,
81+ Optional [str ],
82+ Optional [datetime .datetime ],
83+ Optional [datetime .datetime ],
84+ ]:
7185 try :
7286 thread_offset = ethread .vol .offset
7387 owner_proc_pid = ethread .Cid .UniqueProcess
7488 thread_tid = ethread .Cid .UniqueThread
7589 thread_start_addr = ethread .StartAddress
90+ thread_win32start_addr = ethread .Win32StartAddress
7691 thread_create_time = (
7792 ethread .get_create_time ()
7893 ) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
7994 thread_exit_time = (
8095 ethread .get_exit_time ()
8196 ) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
97+
98+ owner_proc = None
99+ if vads_cache is not None :
100+ owner_proc = ethread .owning_process ()
82101 except exceptions .InvalidAddressException :
83102 vollog .debug (f"Thread invalid address { ethread .vol .offset :#x} " )
84103 return None
85104
105+ if vads_cache is not None :
106+ vads = pe_symbols .PESymbols .get_vads_for_process_cache (
107+ vads_cache , owner_proc
108+ )
109+ # no vads = terminated/smeared, pid 4 = kernel = don't check VADs
110+ if (
111+ owner_proc_pid != 4
112+ and owner_proc .InheritedFromUniqueProcessId != 4
113+ and (not vads or len (vads ) < 5 )
114+ ):
115+ vollog .debug (
116+ f"No vads for process at { owner_proc .vol .offset :#x} . Skipping thread at { ethread .vol .offset :#x} "
117+ )
118+ return None
119+
120+ start_path = pe_symbols .PESymbols .filepath_for_address (
121+ vads , thread_start_addr
122+ )
123+ win32start_path = pe_symbols .PESymbols .filepath_for_address (
124+ vads , thread_win32start_addr
125+ )
126+ else :
127+ start_path = None
128+ win32start_path = None
129+
86130 return (
87131 format_hints .Hex (thread_offset ),
88132 owner_proc_pid ,
89133 thread_tid ,
90134 format_hints .Hex (thread_start_addr ),
135+ start_path ,
136+ format_hints .Hex (thread_win32start_addr ),
137+ win32start_path ,
91138 thread_create_time ,
92139 thread_exit_time ,
93140 )
94141
95142 def _generator (self , filter_func : Callable ):
96143 kernel_name = self .config ["kernel" ]
97144
145+ vads_cache : Dict [int , pe_symbols .ranges_type ] = {}
146+
98147 for ethread in self .implementation (self .context , kernel_name ):
99- info = self .gather_thread_info (ethread )
148+ info = self .gather_thread_info (ethread , vads_cache )
100149
101150 if info :
102- yield (0 , info )
151+ (
152+ offset ,
153+ pid ,
154+ tid ,
155+ start_addr ,
156+ start_path ,
157+ win32start_addr ,
158+ win32start_path ,
159+ create_time ,
160+ exit_time ,
161+ ) = info
162+ yield 0 , (
163+ offset ,
164+ pid ,
165+ tid ,
166+ start_addr ,
167+ start_path or renderers .NotAvailableValue (),
168+ win32start_addr ,
169+ win32start_path or renderers .NotAvailableValue (),
170+ create_time ,
171+ exit_time ,
172+ )
103173
104174 def generate_timeline (self ):
105175 filt_func = self .filter_func (self .config )
@@ -145,6 +215,9 @@ def run(self):
145215 ("PID" , int ),
146216 ("TID" , int ),
147217 ("StartAddress" , format_hints .Hex ),
218+ ("StartPath" , str ),
219+ ("Win32StartAddress" , format_hints .Hex ),
220+ ("Win32StartPath" , str ),
148221 ("CreateTime" , datetime .datetime ),
149222 ("ExitTime" , datetime .datetime ),
150223 ],
0 commit comments