1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
"""
High-level abstraction of Minidump file
"""
from builtins import range
import struct
from miasm.loader.strpatchwork import StrPatchwork
from miasm.loader import minidump as mp
class MemorySegment(object):
"""Stand for a segment in memory with additional information"""
def __init__(self, offset, memory_desc, module=None, memory_info=None):
self.offset = offset
self.memory_desc = memory_desc
self.module = module
self.memory_info = memory_info
self.minidump = self.memory_desc.parent_head
@property
def address(self):
return self.memory_desc.StartOfMemoryRange
@property
def size(self):
if isinstance(self.memory_desc, mp.MemoryDescriptor64):
return self.memory_desc.DataSize
elif isinstance(self.memory_desc, mp.MemoryDescriptor):
return self.memory_desc.Memory.DataSize
raise TypeError
@property
def name(self):
if not self.module:
return ""
name = mp.MinidumpString.unpack(self.minidump._content,
self.module.ModuleNameRva.rva,
self.minidump)
return b"".join(
struct.pack("B", x) for x in name.Buffer
).decode("utf-16")
@property
def content(self):
return self.minidump._content[self.offset:self.offset + self.size]
@property
def protect(self):
if self.memory_info:
return self.memory_info.Protect
return None
@property
def pretty_protect(self):
if self.protect is None:
return "UNKNOWN"
return mp.memProtect[self.protect]
class Minidump(object):
"""Stand for a Minidump file
Here is a few limitation:
- only < 4GB Minidump are supported (LocationDescriptor handling)
- only Stream relative to memory mapping are implemented
Official description is available on MSDN:
https://msdn.microsoft.com/en-us/library/ms680378(VS.85).aspx
"""
_sex = 0
_wsize = 32
def __init__(self, minidump_str):
self._content = StrPatchwork(minidump_str)
# Specific streams
self.modulelist = None
self.memory64list = None
self.memorylist = None
self.memoryinfolist = None
self.systeminfo = None
# Get information
self.streams = []
self.threads = None
self.parse_content()
# Memory information
self.memory = {} # base address (virtual) -> Memory information
self.build_memory()
def parse_content(self):
"""Build structures corresponding to current content"""
# Header
offset = 0
self.minidumpHDR = mp.MinidumpHDR.unpack(self._content, offset, self)
assert self.minidumpHDR.Magic == 0x504d444d
# Streams
base_offset = self.minidumpHDR.StreamDirectoryRva.rva
empty_stream = mp.StreamDirectory(
StreamType=0,
Location=mp.LocationDescriptor(
DataSize=0,
Rva=mp.Rva(rva=0)
)
)
streamdir_size = len(empty_stream)
for i in range(self.minidumpHDR.NumberOfStreams):
stream_offset = base_offset + i * streamdir_size
stream = mp.StreamDirectory.unpack(self._content, stream_offset, self)
self.streams.append(stream)
# Launch specific action depending on the stream
datasize = stream.Location.DataSize
offset = stream.Location.Rva.rva
if stream.StreamType == mp.streamType.ModuleListStream:
self.modulelist = mp.ModuleList.unpack(self._content, offset, self)
elif stream.StreamType == mp.streamType.MemoryListStream:
self.memorylist = mp.MemoryList.unpack(self._content, offset, self)
elif stream.StreamType == mp.streamType.Memory64ListStream:
self.memory64list = mp.Memory64List.unpack(self._content, offset, self)
elif stream.StreamType == mp.streamType.MemoryInfoListStream:
self.memoryinfolist = mp.MemoryInfoList.unpack(self._content, offset, self)
elif stream.StreamType == mp.streamType.SystemInfoStream:
self.systeminfo = mp.SystemInfo.unpack(self._content, offset, self)
# Some streams need the SystemInfo stream to work
for stream in self.streams:
datasize = stream.Location.DataSize
offset = stream.Location.Rva.rva
if (self.systeminfo is not None and
stream.StreamType == mp.streamType.ThreadListStream):
self.threads = mp.ThreadList.unpack(self._content, offset, self)
def build_memory(self):
"""Build an easier to use memory view based on ModuleList and
Memory64List streams"""
addr2module = dict((module.BaseOfImage, module)
for module in (self.modulelist.Modules if
self.modulelist else []))
addr2meminfo = dict((memory.BaseAddress, memory)
for memory in (self.memoryinfolist.MemoryInfos if
self.memoryinfolist else []))
mode64 = self.minidumpHDR.Flags & mp.minidumpType.MiniDumpWithFullMemory
if mode64:
offset = self.memory64list.BaseRva
memranges = self.memory64list.MemoryRanges
else:
memranges = self.memorylist.MemoryRanges
for memory in memranges:
if not mode64:
offset = memory.Memory.Rva.rva
# Create a MemorySegment with augmented information
base_address = memory.StartOfMemoryRange
module = addr2module.get(base_address, None)
meminfo = addr2meminfo.get(base_address, None)
self.memory[base_address] = MemorySegment(offset, memory,
module, meminfo)
if mode64:
offset += memory.DataSize
# Sanity check
if mode64:
assert all(addr in self.memory for addr in addr2module)
def get(self, virt_start, virt_stop):
"""Return the content at the (virtual addresses)
[virt_start:virt_stop]"""
# Find the corresponding memory segment
for addr in self.memory:
if virt_start <= addr <= virt_stop:
break
else:
return b""
memory = self.memory[addr]
shift = addr - virt_start
last = virt_stop - addr
if last > memory.size:
raise RuntimeError("Multi-page not implemented")
return self._content[memory.offset + shift:memory.offset + last]
|