about summary refs log tree commit diff stats
path: root/src/miasm/loader/minidump_init.py
diff options
context:
space:
mode:
authorTheofilos Augoustis <theofilos.augoustis@gmail.com>2025-10-14 09:09:29 +0000
committerTheofilos Augoustis <theofilos.augoustis@gmail.com>2025-10-14 09:09:29 +0000
commit579cf1d03fb932083e6317967d1613d5c2587fb6 (patch)
tree629f039935382a2a7391bce9253f6c9968159049 /src/miasm/loader/minidump_init.py
parent51c15d3ea2e16d4fc5f0f01a3b9befc66b1f982e (diff)
downloadfocaccia-miasm-579cf1d03fb932083e6317967d1613d5c2587fb6.tar.gz
focaccia-miasm-579cf1d03fb932083e6317967d1613d5c2587fb6.zip
Convert to src-layout ta/nix
Diffstat (limited to 'src/miasm/loader/minidump_init.py')
-rw-r--r--src/miasm/loader/minidump_init.py194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/miasm/loader/minidump_init.py b/src/miasm/loader/minidump_init.py
new file mode 100644
index 00000000..dd37f730
--- /dev/null
+++ b/src/miasm/loader/minidump_init.py
@@ -0,0 +1,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]