Skip to content

Commit 4f7f3ad

Browse files
committed
Change the recursion to iteration to avoid stack depth issues
1 parent 1f9d58d commit 4f7f3ad

File tree

1 file changed

+52
-56
lines changed

1 file changed

+52
-56
lines changed

volatility3/framework/plugins/windows/strings.py

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# This file is Copyright 2019 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+
from __future__ import annotations
45

5-
from dataclasses import dataclass
66
import logging
77
import re
8-
from typing import Dict, Generator, List, Set, Tuple, Optional
8+
from dataclasses import dataclass
9+
from typing import Generator
910

10-
from volatility3.framework import interfaces, renderers, exceptions, constants
11+
from volatility3.framework import constants, exceptions, interfaces, renderers
1112
from volatility3.framework.configuration import requirements
12-
from volatility3.framework.layers import intel, resources, linear
13+
from volatility3.framework.layers import intel, linear, resources
1314
from volatility3.framework.renderers import format_hints
1415
from volatility3.plugins.windows import pslist
1516

@@ -18,45 +19,41 @@
1819

1920
@dataclass
2021
class MappingNode:
21-
def __init__(
22-
self,
23-
physical_addr_start,
24-
physical_addr_end,
25-
virtual_addr_start,
26-
virtual_addr_end,
27-
process_id,
28-
region,
29-
) -> None:
30-
self.physical_addr_start = physical_addr_start
31-
self.physical_addr_end = physical_addr_end
32-
self.virtual_addr_start = virtual_addr_start
33-
self.virtual_addr_end = virtual_addr_end
34-
self.process_id = process_id
35-
self.region = region
22+
physical_addr_start: int
23+
physical_addr_end: int
24+
virtual_addr_start: int
25+
virtual_addr_end: int
26+
process_id: int | str
27+
region: str
3628

3729

30+
@dataclass
3831
class MappingTree:
39-
def __init__(self, root=None) -> None:
40-
self.root = root
41-
self.left = None
42-
self.right = None
43-
44-
def add(self, node):
45-
if isinstance(node, MappingNode):
46-
if self.root == None:
47-
self.root = node
48-
elif node.physical_addr_start < self.root.physical_addr_start:
49-
if self.left == None:
50-
self.left = MappingTree(node)
32+
root: MappingNode | None = None
33+
left: MappingTree | None = None
34+
right: MappingTree | None = None
35+
36+
def add(self, node: MappingNode, depth: int = 0) -> None:
37+
# Iteratively add to avoid recursion issues
38+
if not isinstance(node, MappingNode):
39+
raise TypeError
40+
parent_node: MappingTree | None = self
41+
while parent_node is not None:
42+
if parent_node.root is None:
43+
parent_node.root = node
44+
parent_node = None
45+
elif node.physical_addr_start < parent_node.root.physical_addr_start:
46+
if parent_node.left is None:
47+
parent_node.left = MappingTree(node)
48+
parent_node = None
5149
else:
52-
self.left.add(node)
50+
parent_node = parent_node.left
5351
else:
54-
if self.right == None:
55-
self.right = MappingTree(node)
52+
if parent_node.right is None:
53+
parent_node.right = MappingTree(node)
54+
parent_node = None
5655
else:
57-
self.right.add(node)
58-
else:
59-
raise TypeError()
56+
parent_node = parent_node.right
6057

6158
def at(self, point):
6259
if self.root:
@@ -80,7 +77,7 @@ class Strings(interfaces.plugins.PluginInterface):
8077
strings_pattern = re.compile(rb"^(?:\W*)([0-9]+)(?:\W*)(\w[\w\W]+)\n?")
8178

8279
@classmethod
83-
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
80+
def get_requirements(cls) -> list[interfaces.configuration.RequirementInterface]:
8481
return [
8582
requirements.ModuleRequirement(
8683
name="kernel",
@@ -113,13 +110,14 @@ def run(self):
113110
self._generator(),
114111
)
115112

116-
def _generator(self) -> Generator[Tuple, None, None]:
113+
def _generator(self) -> Generator[tuple, None, None]:
117114
"""Generates results from a strings file."""
118-
string_list: List[Tuple[int, bytes]] = []
115+
string_list: list[tuple[int, bytes]] = []
119116

120117
# Test strings file format is accurate
121-
accessor = resources.ResourceAccessor()
122-
strings_fp = accessor.open(self.config["strings_file"], "rb")
118+
strings_fp = resources.ResourceAccessor().open(
119+
self.config["strings_file"], "rb"
120+
)
123121
line = strings_fp.readline()
124122
count: float = 0
125123
while line:
@@ -140,9 +138,9 @@ def _generator(self) -> Generator[Tuple, None, None]:
140138
pid_list=self.config["pid"],
141139
)
142140

143-
last_prog: float = 0
141+
_last_prog: float = 0
144142
line_count: float = 0
145-
num_strings = len(string_list)
143+
_num_strings = len(string_list)
146144

147145
for phys_offset, string in string_list:
148146
line_count += 1
@@ -177,7 +175,7 @@ def _generator(self) -> Generator[Tuple, None, None]:
177175
),
178176
)
179177

180-
def _parse_line(self, line: bytes) -> Tuple[int, bytes]:
178+
def _parse_line(self, line: bytes) -> tuple[int, bytes]:
181179
"""Parses a single line from a strings file.
182180
183181
Args:
@@ -200,8 +198,8 @@ def generate_mapping(
200198
layer_name: str,
201199
symbol_table: str,
202200
progress_callback: constants.ProgressCallback = None,
203-
pid_list: Optional[List[int]] = None,
204-
) -> Dict[int, Set[Tuple[str, int]]]:
201+
pid_list: list[int] | None = None,
202+
) -> MappingTree:
205203
"""Creates a reverse mapping between virtual addresses and physical
206204
addresses.
207205
@@ -219,7 +217,7 @@ def generate_mapping(
219217
revmap_tree = MappingTree()
220218

221219
# start with kernel mappings
222-
layer = context.layers[layer_name]
220+
layer: intel.Intel = context.layers[layer_name]
223221
min_kernel_addr = 2 ** (layer._maxvirtaddr - 1)
224222
if isinstance(layer, intel.Intel):
225223
# We don't care about errors, we just wanted chunks that map correctly
@@ -247,7 +245,7 @@ def generate_mapping(
247245
if progress_callback:
248246
progress_callback(
249247
(virt_offset * 100) / layer.maximum_address,
250-
f"Creating custom tree mapping for kernel",
248+
f"Creating custom tree mapping for kernel at offset : {virt_offset:x}",
251249
)
252250

253251
# now process normal processes, ignoring kernel addrs
@@ -259,13 +257,11 @@ def generate_mapping(
259257
proc_layer_name = process.add_process_layer()
260258
except exceptions.InvalidAddressException as excp:
261259
vollog.debug(
262-
"Process {}: invalid address {} in layer {}".format(
263-
proc_id, excp.invalid_address, excp.layer_name
264-
)
260+
f"Process {proc_id}: invalid address {excp.invalid_address} in layer {excp.layer_name}"
265261
)
266262
continue
267263

268-
proc_layer = context.layers[proc_layer_name]
264+
proc_layer: intel.Intel = context.layers[proc_layer_name]
269265
max_proc_addr = (2 ** (proc_layer._maxvirtaddr - 1)) - 1
270266
if isinstance(proc_layer, linear.LinearlyMappedLayer):
271267
for mapval in proc_layer.mapping(
@@ -284,14 +280,14 @@ def generate_mapping(
284280
phy_offset + phy_mapping_size,
285281
virt_offset,
286282
virt_offset + virt_size,
287-
proc_id,
288-
"Process",
283+
process_id=proc_id,
284+
region="Process",
289285
)
290286
revmap_tree.add(node)
291287

292288
if progress_callback:
293289
progress_callback(
294290
(virt_offset * 100) / max_proc_addr,
295-
f"Creating custom tree mapping for task {proc_id}",
291+
f"Creating custom tree mapping for task {proc_id}: {virt_offset:x}",
296292
)
297293
return revmap_tree

0 commit comments

Comments
 (0)