11# This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0
22# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33#
4+ import datetime
45from typing import Any , Callable , Iterable , List , Tuple
56
67from volatility3 .framework import interfaces , renderers
910from volatility3 .framework .renderers import format_hints
1011from volatility3 .framework .symbols import intermed
1112from volatility3 .framework .symbols .linux .extensions import elf
13+ from volatility3 .plugins import timeliner
1214from volatility3 .plugins .linux import elfs
1315
1416
15- class PsList (interfaces .plugins .PluginInterface ):
17+ class PsList (interfaces .plugins .PluginInterface , timeliner . TimeLinerInterface ):
1618 """Lists the processes present in a particular linux memory image."""
1719
1820 _required_framework_version = (2 , 0 , 0 )
1921
20- _version = (2 , 2 , 1 )
22+ _version = (2 , 3 , 0 )
2123
2224 @classmethod
2325 def get_requirements (cls ) -> List [interfaces .configuration .RequirementInterface ]:
@@ -81,7 +83,7 @@ def filter_func(x):
8183 @classmethod
8284 def get_task_fields (
8385 cls , task : interfaces .objects .ObjectInterface , decorate_comm : bool = False
84- ) -> Tuple [int , int , int , str ]:
86+ ) -> Tuple [int , int , int , int , str , datetime . datetime ]:
8587 """Extract the fields needed for the final output
8688
8789 Args:
@@ -96,13 +98,14 @@ def get_task_fields(
9698 tid = task .pid
9799 ppid = task .parent .tgid if task .parent else 0
98100 name = utility .array_to_string (task .comm )
101+ start_time = task .get_create_time ()
99102 if decorate_comm :
100103 if task .is_kernel_thread :
101104 name = f"[{ name } ]"
102105 elif task .is_user_thread :
103106 name = f"{{{ name } }}"
104107
105- task_fields = (task .vol .offset , pid , tid , ppid , name )
108+ task_fields = (task .vol .offset , pid , tid , ppid , name , start_time )
106109 return task_fields
107110
108111 def _get_file_output (self , task : interfaces .objects .ObjectInterface ) -> str :
@@ -177,14 +180,17 @@ def _generator(
177180 else :
178181 file_output = "Disabled"
179182
180- offset , pid , tid , ppid , name = self .get_task_fields (task , decorate_comm )
183+ offset , pid , tid , ppid , name , creation_time = self .get_task_fields (
184+ task , decorate_comm
185+ )
181186
182187 yield 0 , (
183188 format_hints .Hex (offset ),
184189 pid ,
185190 tid ,
186191 ppid ,
187192 name ,
193+ creation_time or renderers .NotAvailableValue (),
188194 file_output ,
189195 )
190196
@@ -233,8 +239,23 @@ def run(self):
233239 ("TID" , int ),
234240 ("PPID" , int ),
235241 ("COMM" , str ),
242+ ("CREATION TIME" , datetime .datetime ),
236243 ("File output" , str ),
237244 ]
238245 return renderers .TreeGrid (
239246 columns , self ._generator (filter_func , include_threads , decorate_comm , dump )
240247 )
248+
249+ def generate_timeline (self ):
250+ pids = self .config .get ("pid" )
251+ filter_func = self .create_pid_filter (pids )
252+ for task in self .list_tasks (
253+ self .context , self .config ["kernel" ], filter_func , include_threads = True
254+ ):
255+ offset , user_pid , user_tid , _user_ppid , name , creation_time = (
256+ self .get_task_fields (task )
257+ )
258+
259+ description = f"Process { user_pid } /{ user_tid } { name } ({ offset } )"
260+
261+ yield (description , timeliner .TimeLinerType .CREATED , creation_time )
0 commit comments