about summary refs log tree commit diff stats
path: root/miasm/loader/minidump_init.py
blob: dd37f730cfff533e81db7458565464ff0d1f9d2d (plain) (blame)
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]