Skip to content

Commit 573ba72

Browse files
committed
Add Stack Plugin
1 parent a8f9c94 commit 573ba72

File tree

1 file changed

+118
-0
lines changed
  • volatility3/framework/plugins/windows

1 file changed

+118
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import struct
2+
3+
from typing import List
4+
5+
from volatility3.framework.symbols.windows import extensions
6+
from volatility3.framework.configuration import requirements
7+
from volatility3.framework import interfaces, renderers, constants
8+
from volatility3.framework.renderers import format_hints
9+
from volatility3.plugins.windows import pslist
10+
11+
12+
class Stack(interfaces.plugins.PluginInterface):
13+
"""Lists the Stack boundaries and dump Stack"""
14+
15+
_required_framework_version = (2, 0, 0)
16+
_version = (3, 0, 1)
17+
18+
@classmethod
19+
def get_requirements(cls):
20+
return [
21+
requirements.ModuleRequirement(
22+
name="kernel",
23+
description="Windows Kernel",
24+
architectures=["Intel32", "Intel64"],
25+
),
26+
requirements.VersionRequirement(
27+
name="pslist", component=pslist.PsList, version=(3, 0, 0)
28+
),
29+
requirements.ListRequirement(
30+
name="pid",
31+
element_type=int,
32+
description="Process IDs to operate on",
33+
optional=False,
34+
),
35+
requirements.BooleanRequirement(
36+
name="dump",
37+
description="Whether to dump the stack",
38+
default=False,
39+
optional=True,
40+
),
41+
]
42+
43+
def _generator(self):
44+
file_output = "Disabled"
45+
46+
kernel = self.context.modules[self.config["kernel"]]
47+
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid"), None)
48+
49+
procs: List[extensions.EPROCESS] = pslist.PsList.list_processes(
50+
context=self.context,
51+
kernel_module_name=self.config["kernel"],
52+
filter_func=filter_func,
53+
)
54+
55+
for proc in procs:
56+
proc_layer_name = proc.add_process_layer()
57+
proc_layer = self.context.layers[proc_layer_name]
58+
59+
thread_list: List[extensions.ETHREAD] = list(
60+
proc.ThreadListHead.to_list(
61+
f"{kernel.symbol_table_name}{constants.BANG}_ETHREAD",
62+
"ThreadListEntry",
63+
)
64+
)
65+
66+
# no need to parse the time
67+
active_thread_list: List[extensions.ETHREAD] = [
68+
t for t in thread_list if t.ExitTime.QuadPart < 0
69+
]
70+
71+
for thread in active_thread_list:
72+
trap_frame = thread.Tcb.TrapFrame.dereference()
73+
thread_rsp = trap_frame.Rsp
74+
75+
# first entry of TEB is NT_TIB
76+
# Stack Base is at NT_TIB + 8
77+
# https://github.com/wine-mirror/wine/blob/master/include/winternl.h#L494
78+
# https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L2445
79+
stack_base = struct.unpack(
80+
"Q", proc_layer.read(offset=thread.Tcb.Teb + 8, length=8)
81+
)[0]
82+
83+
stack_size = stack_base - thread_rsp
84+
85+
if self.config["dump"]:
86+
fname = f"{proc.UniqueProcessId}.{thread.Cid.UniqueThread}.dmp"
87+
stack = proc_layer.read(offset=thread_rsp, length=stack_size)
88+
with self.open(fname) as f:
89+
f.write(stack)
90+
91+
file_output = fname
92+
93+
yield (
94+
0,
95+
(
96+
proc.UniqueProcessId,
97+
thread.Cid.UniqueThread,
98+
format_hints.Hex(thread.vol.offset),
99+
format_hints.Hex(thread_rsp),
100+
format_hints.Hex(stack_base),
101+
format_hints.Hex(stack_size),
102+
file_output,
103+
),
104+
)
105+
106+
def run(self):
107+
return renderers.TreeGrid(
108+
[
109+
("PID", int),
110+
("TID", int),
111+
("Thread", format_hints.Hex),
112+
("RSP", format_hints.Hex),
113+
("Stack Base", format_hints.Hex),
114+
("Stack Size", format_hints.Hex),
115+
("File Output", str),
116+
],
117+
self._generator(),
118+
)

0 commit comments

Comments
 (0)