Skip to content

Commit ad0c685

Browse files
committed
Add Stack Plugin
1 parent a8f9c94 commit ad0c685

File tree

1 file changed

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

1 file changed

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

0 commit comments

Comments
 (0)