miasm/loader/minidump_init.py
"""
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]