from filetypes.base import *
import malcat
import datetime
import bisect
import struct
from filetypes.PE import FixedFileInfo, CVInfoPdb70, align

# https://github.com/libyal/libmdmp/blob/master/documentation/Minidump%20(MDMP)%20format.asciidoc

class FileHeader(Struct):

    def parse(self):
        yield String(4, name="Signature")
        yield UInt16(name="FormatVersion", comment="format version (MINIDUMP_VERSION)")
        yield UInt16(name="ImplementationVersion", comment="implementation specific version")
        yield UInt32(name="NumberOfStreams", comment="number of streams")
        yield Offset32(name="StreamsDirectory", comment="streams directory RVA")
        yield UInt32(name="Checksum", comment="")
        yield Timestamp(name="TimeDateStamp", comment="dump creation time")
        yield BitsField(
            Bit(name="WithDataSegs", comment="include the data sections from all loaded modules"),
            Bit(name="WithFullMemory", comment="include all accessible memory in the process"),
            Bit(name="WithHandleData", comment="include high-level information about the operating system handles that are active when the minidump is made"),
            Bit(name="FilterMemory", comment="stack and backing store memory written to the minidump file should be filtered to remove all but the pointer values necessary to reconstruct a stack trace"),
            Bit(name="ScanMemory", comment="stack and backing store memory should be scanned for pointer references to modules in the module list. If a module is referenced by stack or backing store memory, the ModuleWriteFlags member of the MINIDUMP_CALLBACK_OUTPUT structure is set to ModuleReferencedByMemory"),
            Bit(name="WithUnloadedModules", comment="include information from the list of modules that were recently unloaded, if this information is maintained by the operating system"),
            Bit(name="WithIndirectlyReferencedMemory", comment="include pages with data referenced by locals or other stack memory"),
            Bit(name="FilterModulePaths", comment="filter module paths for information such as user names or important directories. This option may prevent the system from locating the image file and should be used only in special situations"),
            Bit(name="WithProcessThreadData", comment="include complete per-process and per-thread information from the operating system"),
            Bit(name="WithPrivateReadWriteMemory", comment="scan the virtual address space for PAGE_READWRITE memory to be included"),
            Bit(name="WithoutOptionalData", comment="reduce the data that is dumped by eliminating memory regions that are not essential to meet criteria specified for the dump"),
            Bit(name="WithFullMemoryInfo", comment="include memory region information"),
            Bit(name="WithThreadInfo", comment="include thread state information"),
            Bit(name="WithCodeSegs", comment="include all code and code-related sections from loaded modules to capture executable content"),
            Bit(name="WithoutAuxiliaryState", comment="turns off secondary auxiliary-supported memory gathering"),
            Bit(name="WithFullAuxiliaryState", comment="requests that auxiliary data providers include their state in the dump image"),
            Bit(name="WithPrivateWriteCopyMemory", comment="scans the virtual address space for PAGE_WRITECOPY memory to be included"),
            Bit(name="IgnoreInaccessibleMemory", comment="if you specify MiniDumpWithFullMemory, the MiniDumpWriteDump function will fail if the function cannot read the memory regions; however, if you include MiniDumpIgnoreInaccessibleMemory, the MiniDumpWriteDump function will ignore the memory read failures and continue to generate the dump"),
            Bit(name="WithTokenInformation", comment="adds security token related data"),
            Bit(name="WithModuleHeaders", comment="adds module header related data"),
            Bit(name="WithFilterTriage", comment="adds filter triage related data"),
            NullBits(64-21),
            name="FileFlags", comment="minidump flags")


class LocationDescriptor(Struct):

    def parse(self):
        yield UInt32(name="DataSize", comment="size of data")
        yield Offset32(name="DataRva", comment="rva of data")


class MinidumpDirectory(Struct):

    def parse(self):
        yield UInt32(name="StreamType", values=[
            ("UnusedStream", 0),
            ("ReservedStream0", 1),
            ("ReservedStream1", 2),
            ("ThreadListStream", 3),
            ("ModuleListStream", 4),
            ("MemoryListStream", 5),
            ("ExceptionStream", 6),
            ("SystemInfoStream", 7),
            ("ThreadExListStream", 8),
            ("Memory64ListStream", 9),
            ("CommentStreamA", 10),
            ("CommentStreamW", 11),
            ("HandleDataStream", 12),
            ("FunctionTableStream", 13),
            ("UnloadedModuleListStream", 14),
            ("MiscInfoStream", 15),
            ("MemoryInfoListStream", 16),
            ("ThreadInfoListStream", 17),
            ("HandleOperationListStream", 18),
            ("TokenStream", 19),
            ("JavascriptDataStream", 20),
            ("SystemMemoryInfoStream", 21),
            ("ProcessVmCountersStream", 22),
            ("IptTraceStream", 23),
            ("ThreadNamesStream", 24),
            ])
        yield LocationDescriptor(name="Location", comment="location of stream")


class Module(Struct):

    def parse(self):
        yield Va64(name="BaseOfImage", comment="base address of the module executable image in memory")
        yield UInt32(name="SizeOfImage", comment="size of the module executable image in memory, in bytes")
        yield UInt32(name="Checksum", comment="checksum value of the module executable image")
        yield Timestamp(name="TimeDateStamp", comment="timestamp value of the module executable image")
        yield Offset32(name="ModuleName", hint=StringUtf16le(0, zero_terminated=True), base=4, comment="RVA to a MINIDUMP_STRING structure that specifies the name of the module")
        yield FixedFileInfo(name="FileInformation", comment="VS_FIXEDFILEINFO structure that specifies the version of the module")
        yield LocationDescriptor(name="CvRecord", comment="CodeView record of the module")
        yield LocationDescriptor(name="MiscRecord", comment="miscellaneous  record of the module")
        yield UInt64(name="Reserved0", comment="")
        yield UInt64(name="Reserved1", comment="")


class ModuleList(Struct):

    def parse(self):
        num = yield UInt32(name="NumberOfModules")
        if num:
            yield Array(num, Module(), name="Modules")



class Thread(Struct):

    def parse(self):
        yield UInt32(name="ThreadId", comment="identifier of the thread")
        yield UInt32(name="SuspendCount", comment="if the suspend count is greater than zero, the thread is suspended; otherwise, the thread is not suspended")
        yield UInt32(name="PriorityClass")
        yield UInt32(name="Priority")
        yield Va64(name="TEB")
        yield MemoryRange(name="Stack")
        yield LocationDescriptor(name="ThreadContext")


class ThreadList(Struct):

    def parse(self):
        num = yield UInt32(name="NumberOfThreads")
        if num:
            yield Array(num, Thread(), name="Threads")


class ThreadInfo(Struct):

    def parse(self):
        yield UInt32(name="ThreadId", comment="identifier of the thread")
        yield UInt32(name="DumpFlags")
        yield UInt32(name="DumpError")
        yield UInt32(name="ExitStatus")
        yield Filetime(name="CreateTime")
        yield Filetime(name="ExitTime")
        yield UInt64(name="KernelTime")
        yield UInt64(name="UserTime")
        yield Va64(name="StartAddress")
        yield UInt64(name="Affinity")


class ThreadInfoList(Struct):

    def parse(self):
        yield UInt32(name="SizeOfHeader")
        yield UInt32(name="SizeOfEntry")
        num = yield UInt32(name="NumberOfEntries")
        if num:
            yield Array(num, ThreadInfo(), name="ThreadInfos")            


class MemoryList(Struct):

    def parse(self):
        num = yield UInt32(name="NumberOfMemoryRanges")
        if num:
            yield Array(num, MemoryRange(), name="Ranges")     

class MemoryRange(StaticStruct):

    @classmethod
    def parse(cls):
        yield Va64(name="StartOfMemoryRange", comment="virtual address of memory block")
        yield UInt32(name="DataSize")             
        yield Offset32(name="DataRva")                


class MemoryList64(Struct):

    def parse(self):
        num = yield UInt64(name="NumberOfMemoryRanges")
        yield UInt64(name="BaseRva")             
        if num:
            yield Array(num, MemoryRange64(), name="Ranges")                 

class MemoryRange64(StaticStruct):

    @classmethod
    def parse(cls):
        yield Va64(name="StartOfMemoryRange", comment="virtual address of memory block")
        yield Offset64(name="DataSize")    


class MemoryInfoList(Struct):

    def parse(self):
        yield UInt32(name="SizeOfHeader")
        yield UInt32(name="SizeOfEntry")
        num = yield UInt64(name="NumberOfEntries")
        if num:
            yield Array(num, MemoryInfo(), name="MemoryInfos")     

class MemoryInfo(StaticStruct):

    @classmethod
    def parse(cls):
        yield Va64(name="BaseAddress")
        yield Va64(name="AllocationBase")
        yield UInt32(name="AllocationProtect")
        yield Unused(4)
        yield UInt64(name="RegionSize")
        yield UInt32(name="State")
        yield BitsField(
            Bit(name="PAGE_NOACCESS"),
            Bit(name="PAGE_READONLY"),
            Bit(name="PAGE_READWRITE"),
            Bit(name="PAGE_WRITECOPY"),
            Bit(name="PAGE_EXECUTE"),
            Bit(name="PAGE_EXECUTE_READ"),
            Bit(name="PAGE_EXECUTE_READWRITE"),
            Bit(name="PAGE_EXECUTE_WRITECOPY"),
            Bit(name="PAGE_GUARD"),
            NullBits(23),
            name="Protect")
        yield BitsField(
            NullBits(12),
            Bit(name="MEM_COMMIT"),
            Bit(name="MEM_RESERVE"),
            Bit(name="MEM_DECOMMIT"),
            Bit(name="MEM_RELEASE"),
            Bit(name="MEM_FREE"),
            Bit(name="MEM_PRIVATE"),
            Bit(name="MEM_MAPPED"),
            Bit(name="MEM_RESET"),
            Bit(name="MEM_TOP_DOWN"),
            Bit(name="MEM_WRITE_WATCH"),
            Bit(name="MEM_PHYSICAL"),
            NullBits(1),
            Bit(name="MEM_RESET_UNDO"),
            NullBits(4),
            Bit(name="MEM_LARGE_PAGES"),
            NullBits(34),
            name="Type")
                            
         

class CPUInformation(Struct):

    def parse(self):
        yield UInt32(name="VendorId", comment="vendor identification string as encoded in cpuid 0 ebx, edx, and ecx, represented as it appears in these registers")
        yield UInt32(name="VersionInformation", comment="family, model, and stepping ID values as encoded in cpuid 1 eax")
        yield UInt32(name="FeatureInformation", comment="capabilities as encoded in cpuid 1 edx")
        yield UInt32(name="AMDExtendedCpuFeatures", comment="supported CPU capabilities as encoded in cpuid 0x80000001 edx")
        yield UInt64(name="ProcessorFeatures ", comment="bitfields containing supported CPU capabilities as identified by bits corresponding to PF_* values passed to IsProcessorFeaturePresent()")

class SystemInformation(Struct):

    def parse(self):
        yield UInt16(name="ProcessorArchitecture", values=[
            ("AMD64", 9),
            ("ARM", 5),
            ("IA64", 6),
            ("INTEL", 0),
            ("UNKNOWN", 0xffff),
        ])
        yield UInt16(name="ProcessorLevel", values=[
            ("Intel 80386", 3),
            ("Intel 80486", 4),
            ("Intel Pentium", 5),
            ("Intel Pentium Pro or Pentium II", 6),
        ])
        yield UInt16(name="ProcessorRevision", comment="")
        yield UInt8(name="NumberOfProcessors", comment="number of processors in the system")
        yield UInt8(name="ProductType", values=[
            ("VER_NT_DOMAIN_CONTROLLER", 2),
            ("VER_NT_SERVER", 3),
            ("VER_NT_WORKSTATION", 1),
        ])
        yield UInt32(name="MajorVersion", comment="major version number of the operating system")
        yield UInt32(name="MinorVersion", comment="minor version number of the operating system")
        yield UInt32(name="BuildNumber", comment="build number of the operating system")
        yield UInt32(name="PlatformId", values=[
            ("WIN32s", 0),
            ("WIN32_WINDOWS", 1),
            ("WIN32_NT", 2),
        ])
        yield Offset32(name="CSDVersionRva", hint=StringUtf16le(0, zero_terminated=True), base=4, comment="latest Service Pack installed on the system")
        yield BitsField(
            Bit(name="VER_SUITE_SMALLBUSINESS", comment="Microsoft Small Business Server was once installed on the system"),
            Bit(name="VER_SUITE_ENTERPRISE", comment="Windows Server Enterprise is installed"),
            Bit(name="VER_SUITE_BACKOFFICE", comment="Microsoft BackOffice components are installed"),
            NullBits(1),
            Bit(name="VER_SUITE_TERMINAL", comment="Terminal Services is installed. This value is always set. If VER_SUITE_TERMINAL is set but VER_SUITE_SINGLEUSERTS is not set, the system is running in application server mode"),
            Bit(name="VER_SUITE_SMALLBUSINESS_RESTRICTED", comment="Microsoft Small Business Server is installed with the restrictive client license in force"),
            Bit(name="VER_SUITE_EMBEDDEDNT", comment="Windows Embedded is installed"),
            Bit(name="VER_SUITE_DATACENTER", comment="Windows Server DataCenter is installed"),
            Bit(name="VER_SUITE_SINGLEUSERTS", comment="Remote Desktop is supported, but only one interactive session is supported"),
            Bit(name="VER_SUITE_PERSONAL", comment="Windows Home Edition is installed"),
            Bit(name="VER_SUITE_BLADE", comment="Windows Server Web Edition is installed"),
            NullBits(2),
            Bit(name="VER_SUITE_STORAGE_SERVER", comment="Windows Storage Server is installed"),
            Bit(name="VER_SUITE_COMPUTE_SERVER", comment="Windows Server Compute Cluster Edition is installed"),

            name="SuiteMask", comment="the product suites available on the system")
        yield Unused(2)
        yield CPUInformation()


class SystemTime(StaticStruct):

    @classmethod
    def parse(cls):
        yield UInt16(name="Year", comment="The year, represented fully")
        yield UInt16(name="Month", comment="month of the year, 1 for January and 12 for December")
        yield UInt16(name="DayOfWeek", comment="day of the week, 0 for Sunday and 6 for Saturday")
        yield UInt16(name="Day", comment="day of the month, 1 through 31")
        yield UInt16(name="Hour", comment="hour of the day, 0 through 23")
        yield UInt16(name="Minute", comment="minute of the hour, 0 through 59")
        yield UInt16(name="Second", comment="second of the minute, 0 through 60")
        yield UInt16(name="Milliseconds", comment="millisecond of the second, 0 through 999")
 

class TimeZoneInformation(Struct):

    def parse(self):
        yield Int32(name="Bias", comment="number of minutes west of UTC")
        yield StringUtf16le(32, name="StandardName", zero_terminated=True, comment="name of the time zone when observing standard time")
        yield SystemTime(name="StandardDate", comment="date and time to switch from daylight saving time to standard time. It can be a specific time, or with SYSTEMTIME::wYear set to 0, it can reflect an annual recurring transition")
        yield Int32(name="StandardBias", comment="bias relative to Bias to be applied when observing standard time")
        yield StringUtf16le(32, name="DaylightName", zero_terminated=True, comment="name of the time zone when observing daylight saving time")
        yield SystemTime(name="DaylightDate", comment="date and time to switch from standard time to daylight saving time")
        yield Int32(name="DaylightBias", comment="bias relative to Bias to be applied when observing daylight saving time")


class MiscInformation(Struct):

    def parse(self):
        sz = yield UInt32(name="StreamSize", comment="size of data")
        yield BitsField(
            Bit(name="MINIDUMP_MISC1_PROCESS_ID", comment=""),
            Bit(name="MINIDUMP_MISC1_PROCESS_TIMES", comment=""),
            Bit(name="MINIDUMP_MISC1_PROCESSOR_POWER_INFO", comment=""),
            NullBits(29),
            name="Flags", comment="information flags")
        yield UInt32(name="ProcessIdentifier", comment="size of data")
        yield Timestamp(name="ProcessCreationTime")
        yield Timestamp(name="ProcessUserTime")
        yield Timestamp(name="ProcessKernelTime")
        if len(self) < sz:
            yield UInt32(name="ProcessorMaximumMhz")
            yield UInt32(name="ProcessorCurrentMhz")
            yield UInt32(name="ProcessorLimitMhz")
            yield UInt32(name="ProcessorMaximumIdleState")
            yield UInt32(name="ProcessorCurrentIdleState")
        if len(self) < sz:
            yield UInt32(name="ProcessorIntegrityLevel", values = [
                ("SECURITY_MANDATORY_MEDIUM_RID", 0x2000),
                ("SECURITY_MANDATORY_HIGH_RID", 0x3000)])
            yield UInt32(name="ProcessorExecuteFlags", comment="this appears to be returned by NtQueryInformationProcess() with an argument of ProcessExecuteFlags (34)")
            yield UInt32(name="ProtectedProcess", comment="Whether the process is protected")
            yield UInt32(name="TimezoneId", comment="Whether daylight saving time was being observed in the system’s location at the time of the snapshot")
            yield TimeZoneInformation()
        if len(self) < sz:
            yield StringUtf16le(260, name="BuildString", zero_terminated=True, comment="string identifying a specific build of the operating system")
            yield StringUtf16le(40, name="DbgBldStr", zero_terminated=True, comment="string identifying the module that produced a minidump file")


class SystemBasicInformation(Struct):

    def parse(self):
        yield UInt32(name="TimerResolution")
        yield UInt32(name="PageSize")
        yield UInt32(name="NumberOfPhysicalPages")
        yield UInt32(name="LowestPhysicalPageNumber")
        yield UInt32(name="HighestPhysicalPageNumber")
        yield UInt32(name="AllocationGranularity")
        yield Va64(name="MinimumUserModeAddress")
        yield Va64(name="MaximumUserModeAddress")
        yield UInt64(name="ActiveProcessorsAffinityMask")
        yield UInt32(name="NumberOfProcessors")


class FileCacheInformation(Struct):

    def parse(self):
        yield UInt64(name="CurrentSize")
        yield UInt64(name="PeakSize")
        yield UInt32(name="PageFaultCount")
        yield UInt64(name="MinimumWorkingSet")
        yield UInt64(name="MaximumWorkingSet")
        yield UInt64(name="CurrentSizeIncludingTransitionInPages")
        yield UInt64(name="PeakSizeIncludingTransitionInPages")
        yield UInt32(name="TransitionRePurposeCount")
        yield UInt32(name="Flags")
       

class SystemBasicPerformanceInformation(Struct):

    def parse(self):
        yield UInt64(name="AvailablePages")
        yield UInt64(name="CommittedPages")
        yield UInt64(name="CommitLimit")
        yield UInt64(name="PeakCommitment")


class SystemPerformanceInformation(Struct):

    def parse(self):
        yield UInt64(name="IdleProcessTime")
        yield UInt64(name="IoReadTransferCount")
        yield UInt64(name="IoWriteTransferCount")
        yield UInt64(name="IoOtherTransferCount")
        yield UInt32(name="IoReadOperationCount")
        yield UInt32(name="IoWriteOperationCount")
        yield UInt32(name="IoOtherOperationCount")
        yield UInt32(name="AvailablePages")
        yield UInt32(name="CommittedPages")
        yield UInt32(name="CommitLimit")
        yield UInt32(name="PeakCommitment")
        yield UInt32(name="PageFaultCount")
        yield UInt32(name="CopyOnWriteCount")
        yield UInt32(name="TransitionCount")
        yield UInt32(name="CacheTransitionCount")
        yield UInt32(name="DemandZeroCount")
        yield UInt32(name="PageReadCount")
        yield UInt32(name="PageReadIoCount")
        yield UInt32(name="CacheReadCount")
        yield UInt32(name="CacheIoCount")
        yield UInt32(name="DirtyPagesWriteCount")
        yield UInt32(name="DirtyWriteIoCount")
        yield UInt32(name="MappedPagesWriteCount")
        yield UInt32(name="MappedWriteIoCount")
        yield UInt32(name="PagedPoolPages")
        yield UInt32(name="NonPagedPoolPages")
        yield UInt32(name="PagedPoolAllocs")
        yield UInt32(name="PagedPoolFrees")
        yield UInt32(name="NonPagedPoolAllocs")
        yield UInt32(name="NonPagedPoolFrees")
        yield UInt32(name="FreeSystemPtes")
        yield UInt32(name="ResidentSystemCodePage")
        yield UInt32(name="TotalSystemDriverPages")
        yield UInt32(name="TotalSystemCodePages")
        yield UInt32(name="NonPagedPoolLookasideHits")
        yield UInt32(name="PagedPoolLookasideHits")
        yield UInt32(name="AvailablePagedPoolPages")
        yield UInt32(name="ResidentSystemCachePage")
        yield UInt32(name="ResidentPagedPoolPage")
        yield UInt32(name="ResidentSystemDriverPage")
        yield UInt32(name="CcFastReadNoWait")
        yield UInt32(name="CcFastReadWait")
        yield UInt32(name="CcFastReadResourceMiss")
        yield UInt32(name="CcFastReadNotPossible")
        yield UInt32(name="CcFastMdlReadNoWait")
        yield UInt32(name="CcFastMdlReadWait")
        yield UInt32(name="CcFastMdlReadResourceMiss")
        yield UInt32(name="CcFastMdlReadNotPossible")
        yield UInt32(name="CcMapDataNoWait")
        yield UInt32(name="CcMapDataWait")
        yield UInt32(name="CcMapDataNoWaitMiss")
        yield UInt32(name="CcMapDataWaitMiss")
        yield UInt32(name="CcPinMappedDataCount")
        yield UInt32(name="CcPinReadNoWait")
        yield UInt32(name="CcPinReadWait")
        yield UInt32(name="CcPinReadNoWaitMiss")
        yield UInt32(name="CcPinReadWaitMiss")
        yield UInt32(name="CcCopyReadNoWait")
        yield UInt32(name="CcCopyReadWait")
        yield UInt32(name="CcCopyReadNoWaitMiss")
        yield UInt32(name="CcCopyReadWaitMiss")
        yield UInt32(name="CcMdlReadNoWait")
        yield UInt32(name="CcMdlReadWait")
        yield UInt32(name="CcMdlReadNoWaitMiss")
        yield UInt32(name="CcMdlReadWaitMiss")
        yield UInt32(name="CcReadAheadIos")
        yield UInt32(name="CcLazyWriteIos")
        yield UInt32(name="CcLazyWritePages")
        yield UInt32(name="CcDataFlushes")
        yield UInt32(name="CcDataPages")
        yield UInt32(name="ContextSwitches")
        yield UInt32(name="FirstLevelTbFills")
        yield UInt32(name="SecondLevelTbFills")
        yield UInt32(name="SystemCalls")
        yield UInt64(name="CcTotalDirtyPages")
        yield UInt64(name="CcDirtyPageThreshold")
        yield Int64(name="ResidentAvailablePages")
        yield UInt64(name="SharedCommittedPages")


class SystemMemoryInformation(Struct):

    def parse(self):            
        yield UInt16(name="Revision", comment="")
        yield UInt16(name="Flags", comment="")
        yield SystemBasicInformation()
        yield FileCacheInformation()
        yield SystemBasicPerformanceInformation()
        yield SystemPerformanceInformation()

#####################################################################        


class MappedModule:
    
    def __init__(self, name, base, size, path="", type="", version="", debug_path=""):
        self.name = name
        self.base = base
        self.size = size
        self.path = path
        self.type = type
        self.version = version
        self.debug_path = debug_path
        self.regions = []

    def __eq__(self, o):
        return self.base == o.base and self.size == o.size and self.name == o.name

    def __ne__(self, o):
        return not (self == 0)

    def __lt__(self, o):
        return self.base < o.base

    def __gt__(self, o):
        return self.base > o.base

    def __le__(self, o):
        return self.base <= o.base

    def __ge__(self, o):
        return self.base >= o.base

    def __contains__(self, va):
        return va >= self.base and va < self.base + self.size


class MiniDumpAnalyzer(FileTypeAnalyzer):
    category = malcat.FileType.PROGRAM
    name = "MDMP"
    regexp = r"MDMP\x93\xA7"

    def __init__(self, *args, **kwargs):
        FileTypeAnalyzer.__init__(self, *args, **kwargs)
        self.map = []



    def get_module(self, va):
        i = bisect.bisect_right(self.map, MappedModule("", va, 1))
        if i > 0:
            mod = self.map[i-1]
            if va in mod:
                return mod
        return None

    def parse_modules_list(self, modules_list):
        if not "Modules" in modules_list:
            return
        for module in modules_list["Modules"]:
            if module["ModuleName"]:
                module_path = self.read_cstring_utf16le(4 + module["ModuleName"])
                module_name = module_path.split("\\")[-1]
            else:
                module_path = "???"
                module_name = "???"
            
            mod = MappedModule(module_name, module["BaseOfImage"], module["SizeOfImage"], module_path, module.FileInformation.FileType.enum)
            self.map.append(mod)
            if module["CvRecord"]["DataSize"] > 0:
                self.jump(module["CvRecord"]["DataRva"])
                dbg = yield CVInfoPdb70(name=f"{module_name}.DebugInfos", category=Type.DEBUG, parent=modules_list)
                mod.debug_path = dbg["Path"]
                mod.version = ".".join(map(str, struct.unpack("<HHHH", struct.pack("<II", module["FileInformation"]["FileVersionLS"], module["FileInformation"]["FileVersionMS"]))[::-1]))
        self.map = sorted(self.map)

    def parse_memory_list(self, memory_list, meminfos = {}):
        if not "Ranges" in memory_list:
            return
        if "BaseRva" in memory_list:
            data_base = memory_list["BaseRva"]  # full dump
        else:
            data_base = None
        for region in memory_list["Ranges"]:
            va = region["StartOfMemoryRange"]
            if "DataRva" in region:
                data_offset = region["DataRva"]
            else:
                data_offset = data_base
            data_size = region["DataSize"]
            if data_base is not None:
                data_base += data_size
            if data_size and data_offset + data_size <= self.size():
                self.jump(data_offset)
                if False:
                    yield Bytes(data_size, name="Region", category=Type.DATA, parent = memory_list)

                size = align(data_size, 0x1000)
                module = self.get_module(va)
                if module:
                    name = module.name
                else:
                    name = f"{va:x}"
                infos = meminfos.get(va)
                if infos is not None:
                    protect = infos["Protect"]
                    read = protect["PAGE_READONLY"] or protect["PAGE_READWRITE"] or protect["PAGE_EXECUTE_READ"] or protect["PAGE_EXECUTE_READWRITE"]
                    write = protect["PAGE_WRITECOPY"] or protect["PAGE_READWRITE"] or protect["PAGE_EXECUTE_READWRITE"] or protect["PAGE_EXECUTE_WRITECOPY"]
                    exec = protect["PAGE_EXECUTE_READWRITE"] or protect["PAGE_EXECUTE_WRITECOPY"] or protect["PAGE_EXECUTE_READ"]
                    discard = not infos["Type"]["MEM_MAPPED"]
                else:
                    read = False
                    write = False
                    exec = False
                    discard = False
                self.add_section(name, data_offset, data_size, va, size, r=read, w=write, x=exec)

    def parse_memory_info_list(self, memory_list):
        res = {}
        for meminfo in memory_list["MemoryInfos"]:
            va = meminfo["BaseAddress"]
            size = meminfo["RegionSize"]
            res[va] = meminfo
        return res


    def parse(self, hint):

        SUPPORTED_DIRECTORIES = {
            "ModuleListStream": ModuleList,
            "MemoryInfoListStream": MemoryInfoList,
            "MemoryListStream": MemoryList,
            "Memory64ListStream": MemoryList64,
            "SystemInfoStream": SystemInformation,
            "MiscInfoStream": MiscInformation,
            "ThreadListStream": ThreadList,
            "ThreadInfoListStream": ThreadInfoList,
            "SystemMemoryInfoStream": SystemMemoryInformation,
        }

        fh = yield FileHeader(category=Type.HEADER)
        self.add_metadata("Creation time", fh["TimeDateStamp"].strftime("%Y-%m-%d %H:%M:%S"))
        self.jump(fh["StreamsDirectory"])
        d = yield Array(fh["NumberOfStreams"], MinidumpDirectory(), name="MinidumpDirectory", category=Type.HEADER)
        for entry in d:
            rva = entry["Location"]["DataRva"]
            size = entry["Location"]["DataSize"]
            if size and rva + size <= self.size():
                self.add_section(entry.StreamType.enum.replace("Stream", ""), 
                        entry["Location"]["DataRva"], entry["Location"]["DataSize"],
                        0, 0, r=False)
                typ = SUPPORTED_DIRECTORIES.get(entry.StreamType.enum)
                if typ is not None:
                    self.jump(entry["Location"]["DataRva"])
                    element = yield typ(category=Type.HEADER)
            elif size:
                raise FatalError(f"invalid stream {entry.StreamType.enum} at #{hex(self.tell())} (entry #{hex(entry.offset)})")
        self.confirm()

        if "SystemInformation" in self:
            if self["SystemInformation"].ProcessorArchitecture.enum == "AMD64":
                self.set_architecture(malcat.Architecture.X64)

        if "ModuleList" in self:
            try:
                yield from self.parse_modules_list(self["ModuleList"])
            except ParsingError as e:
                print(f"Error while parsing ModuleList: {e}")

        meminfo = {}
        if "MemoryInfoList" in self:
            try:
                meminfo = self.parse_memory_info_list(self["MemoryInfoList"])
            except ParsingError as e:
                print(f"Error while parsing MemoryInfoList: {e}")

        if "MemoryList" in self:
            try:
                yield from self.parse_memory_list(self["MemoryList"], meminfo)
            except ParsingError as e:
                print(f"Error while parsing MemoryList: {e}")

        if "MemoryList64" in self:
            try:
                yield from self.parse_memory_list(self["MemoryList64"], meminfo)
            except ParsingError as e:
                print(f"Error while parsing MemoryList64: {e}")
