Skip to content

Commit db0a8a0

Browse files
authored
Merge pull request #1381 from the-rectifier/develop
Volshell: Add Dedicated Method to retrieve EPROCESS/Task object
2 parents d2314ed + b59f051 commit db0a8a0

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

volatility3/cli/volshell/linux.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@
33
#
44

55
from typing import Any, List, Optional, Tuple, Union
6+
from enum import Enum
67

78
from volatility3.cli.volshell import generic
89
from volatility3.framework import constants, interfaces
910
from volatility3.framework.configuration import requirements
1011
from volatility3.plugins.linux import pslist
1112

1213

14+
# Could import the enum from psscan.py to avoid code duplication
15+
class DescExitStateEnum(Enum):
16+
"""Enum for linux task exit_state as defined in include/linux/sched.h"""
17+
18+
TASK_RUNNING = 0x00000000
19+
EXIT_DEAD = 0x00000010
20+
EXIT_ZOMBIE = 0x00000020
21+
EXIT_TRACE = EXIT_ZOMBIE | EXIT_DEAD
22+
23+
1324
class Volshell(generic.Volshell):
1425
"""Shell environment to directly interact with a linux memory image."""
1526

@@ -40,6 +51,71 @@ def change_task(self, pid=None):
4051
return None
4152
print(f"No task with task ID {pid} found")
4253

54+
def get_process(self, pid=None, virtaddr=None, physaddr=None):
55+
"""Return the task_struct object that matches the pid. If a physical or a virtual address is provided, construct the task_struct object at said address. Only one parameter is allowed.
56+
57+
Args:
58+
pid (int, optional): PID to search for
59+
virtaddr (int, optional): Virtual address to construct object at
60+
physaddr (int, optional): Physical address to construct object at
61+
62+
Returns:
63+
ObjectInterface: task_struct Object
64+
"""
65+
66+
if sum(1 if x is not None else 0 for x in [pid, virtaddr, physaddr]) != 1:
67+
print("Only one parameter is accepted")
68+
return None
69+
70+
vmlinux_module_name = self.config["kernel"]
71+
vmlinux = self.context.modules[vmlinux_module_name]
72+
73+
kernel_layer_name = vmlinux.layer_name
74+
kernel_layer = self.context.layers[kernel_layer_name]
75+
76+
memory_layer_name = kernel_layer.dependencies[0]
77+
78+
task_struct_symbol = vmlinux.symbol_table_name + constants.BANG + "task_struct"
79+
80+
if virtaddr is not None:
81+
task = self.context.object(
82+
task_struct_symbol,
83+
layer_name=kernel_layer_name,
84+
offset=virtaddr,
85+
)
86+
87+
if physaddr is not None:
88+
task = self.context.object(
89+
task_struct_symbol,
90+
layer_name=memory_layer_name,
91+
offset=physaddr,
92+
native_layer_name=kernel_layer_name,
93+
)
94+
95+
if physaddr is not None or virtaddr is not None:
96+
try:
97+
DescExitStateEnum(task.exit_state)
98+
except ValueError:
99+
print(
100+
f"task_struct @ {hex(task.vol.offset)} as exit_state {task.exit_state} is likely not valid"
101+
)
102+
103+
if not (0 < task.pid < 65535):
104+
print(
105+
f"task_struct @ {hex(task.vol.offset)} as pid {task.pid} is likely not valid"
106+
)
107+
108+
return task
109+
110+
if pid is not None:
111+
tasks = self.list_tasks()
112+
for task in tasks:
113+
if task.pid == pid:
114+
return task
115+
print(f"No task with task ID {pid} found")
116+
117+
return None
118+
43119
def list_tasks(self):
44120
"""Returns a list of task objects from the primary layer"""
45121
# We always use the main kernel memory and associated symbols
@@ -50,6 +126,7 @@ def construct_locals(self) -> List[Tuple[List[str], Any]]:
50126
result += [
51127
(["ct", "change_task", "cp"], self.change_task),
52128
(["lt", "list_tasks", "ps"], self.list_tasks),
129+
(["gp", "get_process", "get_task"], self.get_process),
53130
(["symbols"], self.context.symbol_space[self.current_symbol_table]),
54131
]
55132
if self.config.get("pid", None) is not None:

volatility3/cli/volshell/windows.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,67 @@ def list_processes(self):
4444
)
4545
)
4646

47+
def get_process(self, pid=None, virtaddr=None, physaddr=None):
48+
"""Returns the _EPROCESS object that matches the pid. If a physical or a virtual address is provided, construct the _EPROCESS object at said address. Only one parameter is allowed.
49+
50+
Args:
51+
pid (int, optional): PID / UniqueProcessId to search for.
52+
virtaddr (int, optional): Virtual address to construct object at
53+
physaddr (int, optional): Physical address to construct object at
54+
55+
Returns:
56+
ObjectInterface: _EPROCESS Object
57+
"""
58+
59+
if sum(1 if x is not None else 0 for x in [pid, virtaddr, physaddr]) != 1:
60+
print("Only one parameter is accepted")
61+
return None
62+
63+
kernel_name = self.config["kernel"]
64+
kernel = self.context.modules[kernel_name]
65+
66+
kernel_layer_name = kernel.layer_name
67+
68+
kernel_layer = self.context.layers[kernel_layer_name]
69+
memory_layer_name = kernel_layer.dependencies[0]
70+
71+
eprocess_symbol = kernel.symbol_table_name + constants.BANG + "_EPROCESS"
72+
73+
if virtaddr is not None:
74+
eproc = self.context.object(
75+
eprocess_symbol,
76+
layer_name=kernel_layer_name,
77+
offset=virtaddr,
78+
)
79+
80+
return eproc
81+
82+
if physaddr is not None:
83+
eproc = self.context.object(
84+
eprocess_symbol,
85+
layer_name=memory_layer_name,
86+
offset=physaddr,
87+
native_layer_name=kernel_layer_name,
88+
)
89+
90+
return eproc
91+
92+
if pid is not None:
93+
processes = self.list_processes()
94+
for process in processes:
95+
if process.UniqueProcessId == pid:
96+
return process
97+
print(f"No process with process ID {pid} found")
98+
return None
99+
100+
return None
101+
47102
def construct_locals(self) -> List[Tuple[List[str], Any]]:
48103
result = super().construct_locals()
49104
result += [
50105
(["cp", "change_process"], self.change_process),
51106
(["lp", "list_processes", "ps"], self.list_processes),
107+
(["gp", "get_process"], self.get_process),
52108
(["symbols"], self.context.symbol_space[self.current_symbol_table]),
53109
]
54110
if self.config.get("pid", None) is not None:

0 commit comments

Comments
 (0)