diff --git a/cle/backends/minidump/__init__.py b/cle/backends/minidump/__init__.py index 31c578a06..5b44c9bec 100644 --- a/cle/backends/minidump/__init__.py +++ b/cle/backends/minidump/__init__.py @@ -8,8 +8,10 @@ from minidump.streams import SystemInfoStream from cle.backends.backend import Backend, register_backend -from cle.backends.region import Section, Segment -from cle.errors import CLEError, CLEInvalidBinaryError +from cle.backends.region import Segment +from cle.errors import CLEError + +from .regions import DumpSection class MinidumpMissingStreamError(Exception): @@ -64,14 +66,32 @@ def __init__(self, *args, **kwargs): self.memory.add_backer(segment.start_virtual_address, data) for module in self._mdf.modules.modules: + # A module can span multiple segments + module_start = module.baseaddress + module_end = module.baseaddress + module.size + for segment in segments: - if segment.start_virtual_address == module.baseaddress: - break - else: - raise CLEInvalidBinaryError("Missing segment for loaded module: " + module.name) - section = Section(module.name, segment.start_file_address, module.baseaddress, module.size) - self.sections.append(section) - self.sections_map[ntpath.basename(section.name)] = section + seg_start = segment.start_virtual_address + seg_end = segment.start_virtual_address + segment.size + + # Check for overlap + overlap_start = max(module_start, seg_start) + overlap_end = min(module_end, seg_end) + + if overlap_start < overlap_end: + # find protection for this overlap + protect = 0 + if self._mdf.memory_info is not None: + for info in self._mdf.memory_info.infos: + if info.BaseAddress <= overlap_start < info.BaseAddress + info.RegionSize: + protect = info.Protect + break + + section = DumpSection( + module, segment, protect, vaddr=overlap_start, size=overlap_end - overlap_start + ) + self.sections.append(section) + self.sections_map[ntpath.basename(section.name)] = section self._thread_data = {} @@ -83,6 +103,9 @@ def __init__(self, *args, **kwargs): self._binary_stream.seek(0) self._thread_data[tid] = (teb, data) + if self._mdf.exception is not None and self._mdf.exception.exception_records: + self._entry = self._mdf.exception.exception_records[0].ExceptionRecord.ExceptionAddress + def close(self): super().close() self._mdf.file_handle.close() diff --git a/cle/backends/minidump/regions.py b/cle/backends/minidump/regions.py new file mode 100644 index 000000000..6fa8c27b5 --- /dev/null +++ b/cle/backends/minidump/regions.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from minidump.streams import MemoryInfoListStream + +from cle.backends.region import Section + +AP = MemoryInfoListStream.AllocationProtect + + +class DumpSection(Section): + """ + Represents a mapped memory section in a dump. + """ + + def __init__( + self, + module, + segment, + protect, + vaddr=None, + size=None, + ): + super().__init__( + module.name, + ( + segment.start_file_address + (vaddr - segment.start_virtual_address) + if vaddr + else segment.start_file_address + ), + vaddr or module.baseaddress, + size or module.size, + ) + self.protect = protect + + @property + def is_readable(self) -> bool: + readable = ( + AP.PAGE_READONLY.value + | AP.PAGE_READWRITE.value + | AP.PAGE_WRITECOPY.value + | AP.PAGE_EXECUTE_READ.value + | AP.PAGE_EXECUTE_READWRITE.value + | AP.PAGE_EXECUTE_WRITECOPY.value + ) + return bool(self.protect.value & readable) + + @property + def is_writable(self) -> bool: + writable = AP.PAGE_READWRITE.value | AP.PAGE_EXECUTE_READWRITE.value + return bool(self.protect.value & writable) + + @property + def is_executable(self) -> bool: + executable = ( + AP.PAGE_EXECUTE.value + | AP.PAGE_EXECUTE_READ.value + | AP.PAGE_EXECUTE_READWRITE.value + | AP.PAGE_EXECUTE_WRITECOPY.value + ) + return bool(self.protect.value & executable)