4 | 4 |
#
|
5 | 5 |
import struct
|
6 | 6 |
import ntpath
|
7 | |
from .common_structs import *
|
|
7 |
from .common_structs import *
|
8 | 8 |
from .streams.SystemInfoStream import PROCESSOR_ARCHITECTURE
|
9 | 9 |
|
10 | 10 |
class MinidumpBufferedMemorySegment:
|
11 | 11 |
def __init__(self, memory_segment, file_handle):
|
12 | 12 |
self.start_address = memory_segment.start_virtual_address
|
13 | 13 |
self.end_address = memory_segment.end_virtual_address
|
14 | |
|
|
14 |
|
15 | 15 |
file_handle.seek(memory_segment.start_file_address)
|
16 | 16 |
self.data = file_handle.read(memory_segment.size)
|
17 | |
|
|
17 |
|
18 | 18 |
def inrange(self, position):
|
19 | 19 |
return self.start_address <= position <= self.end_address
|
20 | |
|
|
20 |
|
21 | 21 |
def remaining_len(self, position):
|
22 | 22 |
if not self.inrange(position):
|
23 | 23 |
return None
|
24 | 24 |
return self.end_address - position
|
25 | |
|
|
25 |
|
26 | 26 |
class MinidumpBufferedReader:
|
27 | 27 |
def __init__(self, reader):
|
28 | 28 |
self.reader = reader
|
29 | 29 |
self.memory_segments = []
|
30 | |
|
|
30 |
|
31 | 31 |
self.current_segment = None
|
32 | 32 |
self.current_position = None
|
33 | |
|
|
33 |
|
34 | 34 |
def _select_segment(self, requested_position):
|
35 | 35 |
"""
|
36 | |
|
|
36 |
|
37 | 37 |
"""
|
38 | 38 |
# check if we have semgnet for requested address in cache
|
39 | 39 |
for memory_segment in self.memory_segments:
|
|
41 | 41 |
self.current_segment = memory_segment
|
42 | 42 |
self.current_position = requested_position
|
43 | 43 |
return
|
44 | |
|
|
44 |
|
45 | 45 |
# not in cache, check if it's present in memory space. if yes then create a new buffered memeory object, and copy data
|
46 | 46 |
for memory_segment in self.reader.memory_segments:
|
47 | 47 |
if memory_segment.inrange(requested_position):
|
|
50 | 50 |
self.current_segment = newsegment
|
51 | 51 |
self.current_position = requested_position
|
52 | 52 |
return
|
53 | |
|
|
53 |
|
54 | 54 |
raise Exception('Memory address 0x%08x is not in process memory space' % requested_position)
|
55 | |
|
|
55 |
|
56 | 56 |
def seek(self, offset, whence = 0):
|
57 | 57 |
"""
|
58 | 58 |
Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets.
|
|
69 | 69 |
t = self.current_segment.end_address - offset
|
70 | 70 |
else:
|
71 | 71 |
raise Exception('Seek function whence value must be between 0-2')
|
72 | |
|
|
72 |
|
73 | 73 |
if not self.current_segment.inrange(t):
|
74 | 74 |
raise Exception('Seek would cross memory segment boundaries (use move)')
|
75 | |
|
|
75 |
|
76 | 76 |
self.current_position = t
|
77 | 77 |
return
|
78 | |
|
|
78 |
|
79 | 79 |
def move(self, address):
|
80 | 80 |
"""
|
81 | 81 |
Moves the buffer to a virtual address specified by address
|
82 | 82 |
"""
|
83 | 83 |
self._select_segment(address)
|
84 | 84 |
return
|
85 | |
|
|
85 |
|
86 | 86 |
def align(self, alignment = None):
|
87 | 87 |
"""
|
88 | 88 |
Repositions the current reader to match architecture alignment
|
|
98 | 98 |
offset_to_aligned = (alignment - offset) % alignment
|
99 | 99 |
self.seek(offset_to_aligned, 1)
|
100 | 100 |
return
|
101 | |
|
|
101 |
|
102 | 102 |
def tell(self):
|
103 | 103 |
"""
|
104 | 104 |
Returns the current virtual address
|
105 | 105 |
"""
|
106 | 106 |
return self.current_position
|
107 | |
|
|
107 |
|
108 | 108 |
def peek(self, length):
|
109 | 109 |
"""
|
110 | 110 |
Returns up to length bytes from the current memory segment
|
|
113 | 113 |
if not self.current_segment.inrange(t):
|
114 | 114 |
raise Exception('Would read over segment boundaries!')
|
115 | 115 |
return self.current_segment.data[self.current_position - self.current_segment.start_address :t - self.current_segment.start_address]
|
116 | |
|
|
116 |
|
117 | 117 |
def read(self, size = -1):
|
118 | 118 |
"""
|
119 | 119 |
Returns data bytes of size size from the current segment. If size is -1 it returns all the remaining data bytes from memory segment
|
|
124 | 124 |
t = self.current_segment.remaining_len(self.current_position)
|
125 | 125 |
if not t:
|
126 | 126 |
return None
|
127 | |
|
|
127 |
|
128 | 128 |
old_new_pos = self.current_position
|
129 | 129 |
self.current_position = self.current_segment.end_address
|
130 | 130 |
return self.current_segment.data[old_new_pos - self.current_segment.start_address:]
|
131 | |
|
|
131 |
|
132 | 132 |
t = self.current_position + size
|
133 | 133 |
if not self.current_segment.inrange(t):
|
134 | 134 |
raise Exception('Would read over segment boundaries!')
|
135 | |
|
|
135 |
|
136 | 136 |
old_new_pos = self.current_position
|
137 | |
self.current_position = t
|
|
137 |
self.current_position = t
|
138 | 138 |
return self.current_segment.data[old_new_pos - self.current_segment.start_address :t - self.current_segment.start_address]
|
139 | |
|
|
139 |
|
140 | 140 |
def read_int(self):
|
141 | 141 |
"""
|
142 | |
Reads an integer. The size depends on the architecture.
|
|
142 |
Reads an integer. The size depends on the architecture.
|
143 | 143 |
Reads a 4 byte small-endian singed int on 32 bit arch
|
144 | 144 |
Reads an 8 byte small-endian singed int on 64 bit arch
|
145 | 145 |
"""
|
|
147 | 147 |
return int.from_bytes(self.read(8), byteorder = 'little', signed = True)
|
148 | 148 |
else:
|
149 | 149 |
return int.from_bytes(self.read(4), byteorder = 'little', signed = True)
|
150 | |
|
|
150 |
|
151 | 151 |
def read_uint(self):
|
152 | 152 |
"""
|
153 | |
Reads an integer. The size depends on the architecture.
|
|
153 |
Reads an integer. The size depends on the architecture.
|
154 | 154 |
Reads a 4 byte small-endian unsinged int on 32 bit arch
|
155 | 155 |
Reads an 8 byte small-endian unsinged int on 64 bit arch
|
156 | 156 |
"""
|
|
158 | 158 |
return int.from_bytes(self.read(8), byteorder = 'little', signed = False)
|
159 | 159 |
else:
|
160 | 160 |
return int.from_bytes(self.read(4), byteorder = 'little', signed = False)
|
161 | |
|
|
161 |
|
162 | 162 |
def find(self, pattern):
|
163 | 163 |
"""
|
164 | 164 |
Searches for a pattern in the current memory segment
|
|
167 | 167 |
if pos == -1:
|
168 | 168 |
return -1
|
169 | 169 |
return pos + self.current_position
|
170 | |
|
|
170 |
|
171 | 171 |
def find_all(self, pattern):
|
172 | 172 |
"""
|
173 | 173 |
Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
|
|
179 | 179 |
if last_found == -1:
|
180 | 180 |
break
|
181 | 181 |
pos.append(last_found + self.current_segment.start_address)
|
182 | |
|
|
182 |
|
183 | 183 |
return pos
|
184 | |
|
|
184 |
|
185 | 185 |
def find_global(self, pattern):
|
186 | 186 |
"""
|
187 | 187 |
Searches for the pattern in the whole process memory space and returns the first occurrence.
|
|
190 | 190 |
pos_s = self.reader.search(pattern)
|
191 | 191 |
if len(pos_s) == 0:
|
192 | 192 |
return -1
|
193 | |
|
|
193 |
|
194 | 194 |
return pos_s[0]
|
195 | |
|
|
195 |
|
196 | 196 |
def find_all_global(self, pattern):
|
197 | 197 |
"""
|
198 | 198 |
Searches for the pattern in the whole process memory space and returns a list of addresses where the pattern begins.
|
199 | 199 |
This is exhaustive!
|
200 | 200 |
"""
|
201 | 201 |
return self.reader.search(pattern)
|
202 | |
|
|
202 |
|
203 | 203 |
def get_ptr(self, pos):
|
204 | 204 |
self.move(pos)
|
205 | 205 |
return self.read_uint()
|
206 | 206 |
#raw_data = self.read(pos, self.sizeof_ptr)
|
207 | 207 |
#return struct.unpack(self.unpack_ptr, raw_data)[0]
|
208 | |
|
|
208 |
|
209 | 209 |
def get_ptr_with_offset(self, pos):
|
210 | 210 |
if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
|
211 | 211 |
self.move(pos)
|
|
214 | 214 |
else:
|
215 | 215 |
self.move(pos)
|
216 | 216 |
return self.read_uint()
|
217 | |
|
|
217 |
|
218 | 218 |
def find_in_module(self, module_name, pattern):
|
219 | 219 |
t = self.reader.search_module(module_name, pattern)
|
220 | 220 |
return t
|
221 | |
|
222 | |
|
223 | |
|
224 | |
|
|
221 |
|
|
222 |
|
|
223 |
|
|
224 |
|
225 | 225 |
class MinidumpFileReader:
|
226 | 226 |
def __init__(self, minidumpfile):
|
227 | 227 |
self.modules = minidumpfile.modules.modules
|
228 | 228 |
self.sysinfo = minidumpfile.sysinfo
|
229 | |
|
|
229 |
|
230 | 230 |
if minidumpfile.memory_segments_64:
|
231 | 231 |
self.memory_segments = minidumpfile.memory_segments_64.memory_segments
|
232 | 232 |
self.is_fulldump = True
|
233 | |
|
|
233 |
|
234 | 234 |
else:
|
235 | 235 |
self.memory_segments = minidumpfile.memory_segments.memory_segments
|
236 | 236 |
self.is_fulldump = False
|
237 | |
|
|
237 |
|
238 | 238 |
self.filename = minidumpfile.filename
|
239 | 239 |
self.file_handle = minidumpfile.file_handle
|
240 | |
|
|
240 |
|
241 | 241 |
#reader params
|
242 | 242 |
self.sizeof_long = 4
|
243 | 243 |
self.unpack_long = '<L'
|
244 | |
if minidumpfile.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
|
|
244 |
if minidumpfile.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.AMD64, PROCESSOR_ARCHITECTURE.AARCH64]:
|
245 | 245 |
self.sizeof_ptr = 8
|
246 | 246 |
self.unpack_ptr = '<Q'
|
247 | |
elif self.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL:
|
|
247 |
elif self.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.INTEL,
|
|
248 |
PROCESSOR_ARCHITECTURE.ARM]:
|
248 | 249 |
self.sizeof_ptr = 4
|
249 | 250 |
self.unpack_ptr = '<L'
|
250 | 251 |
else:
|
251 | 252 |
raise Exception('Unknown processor architecture %s! Please fix and submit PR!' % self.sysinfo.ProcessorArchitecture)
|
252 | |
|
|
253 |
|
253 | 254 |
def get_buffered_reader(self):
|
254 | 255 |
return MinidumpBufferedReader(self)
|
255 | |
|
|
256 |
|
256 | 257 |
def get_module_by_name(self, module_name):
|
257 | 258 |
for mod in self.modules:
|
258 | 259 |
if ntpath.basename(mod.name).find(module_name) != -1:
|
259 | 260 |
return mod
|
260 | 261 |
return None
|
261 | |
|
|
262 |
|
262 | 263 |
def search_module(self, module_name, pattern):
|
263 | 264 |
mod = self.get_module_by_name(module_name)
|
264 | 265 |
if mod is None:
|
|
267 | 268 |
for ms in self.memory_segments:
|
268 | 269 |
if mod.baseaddress <= ms.start_virtual_address < mod.endaddress:
|
269 | 270 |
t+= ms.search(pattern, self.file_handle)
|
270 | |
|
|
271 |
|
271 | 272 |
return t
|
272 | |
|
|
273 |
|
273 | 274 |
def search(self, pattern):
|
274 | 275 |
t = []
|
275 | 276 |
for ms in self.memory_segments:
|
276 | 277 |
t+= ms.search(pattern, self.file_handle)
|
277 | |
|
|
278 |
|
278 | 279 |
return t
|
279 | |
|
|
280 |
|
280 | 281 |
def read(self, virt_addr, size):
|
281 | 282 |
for segment in self.memory_segments:
|
282 | 283 |
if segment.inrange(virt_addr):
|