From 2efea18c841f290b1a817103a9c3c86af6cbee42 Mon Sep 17 00:00:00 2001 From: SkelSec Date: Sun, 5 Jan 2020 17:17:41 +0100 Subject: [PATCH 1/7] updating writer code --- minidump/common_structs.py | 59 +++-- minidump/directory.py | 6 + minidump/header.py | 6 + minidump/minidumpfile.py | 56 +++- minidump/streams/MemoryInfoListStream.py | 7 + minidump/streams/ModuleListStream.py | 61 +++-- minidump/streams/SystemInfoStream.py | 66 +++-- minidump/writer.py | 310 ++++++++++++----------- 8 files changed, 335 insertions(+), 236 deletions(-) diff --git a/minidump/common_structs.py b/minidump/common_structs.py index 271b57b..39516ec 100644 --- a/minidump/common_structs.py +++ b/minidump/common_structs.py @@ -1,9 +1,9 @@ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680383(v=vs.85).aspx class MINIDUMP_LOCATION_DESCRIPTOR: - def __init__(self): - self.DataSize = None - self.Rva = None + def __init__(self, DataSize = None, Rva = None): + self.DataSize = DataSize + self.Rva = Rva def get_size(self): return 8 @@ -49,10 +49,19 @@ def __str__(self): return t class MINIDUMP_STRING: - def __init__(self): + def __init__(self, data = None, encoding = 'utf-16-le'): self.Length = None self.Buffer = None + self.encoding = encoding + if data is not None: + self.Buffer = data.encode(self.encoding) + self.Length = len(self.Buffer) + def to_bytes(self): + t = self.Length.to_bytes(4, byteorder = 'little', signed = False) + t += self.Buffer + return t + @staticmethod def parse(buff): ms = MINIDUMP_STRING() @@ -66,7 +75,7 @@ def get_from_rva(rva, buff): buff.seek(rva, 0) ms = MINIDUMP_STRING.parse(buff) buff.seek(pos, 0) - return ms.Buffer.decode('utf-16-le') + return ms.Buffer.decode(ms.encoding) class MinidumpMemorySegment: def __init__(self): @@ -167,42 +176,42 @@ def hexdump( src, length=16, sep='.', start = 0): @note Full support for python2 and python3 ! ''' - result = []; + result = [] # Python3 support try: - xrange(0,1); + xrange(0,1) except NameError: - xrange = range; + xrange = range for i in xrange(0, len(src), length): - subSrc = src[i:i+length]; - hexa = ''; - isMiddle = False; + subSrc = src[i:i+length] + hexa = '' + isMiddle = False for h in xrange(0,len(subSrc)): if h == length/2: - hexa += ' '; - h = subSrc[h]; + hexa += ' ' + h = subSrc[h] if not isinstance(h, int): - h = ord(h); - h = hex(h).replace('0x',''); + h = ord(h) + h = hex(h).replace('0x','') if len(h) == 1: - h = '0'+h; - hexa += h+' '; - hexa = hexa.strip(' '); - text = ''; + h = '0'+h + hexa += h+' ' + hexa = hexa.strip(' ') + text = '' for c in subSrc: if not isinstance(c, int): - c = ord(c); + c = ord(c) if 0x20 <= c < 0x7F: - text += chr(c); + text += chr(c) else: - text += sep; + text += sep if start == 0: - result.append(('%08x: %-'+str(length*(2+1)+1)+'s |%s|') % (i, hexa, text)); + result.append(('%08x: %-'+str(length*(2+1)+1)+'s |%s|') % (i, hexa, text)) else: - result.append(('%08x(+%04x): %-'+str(length*(2+1)+1)+'s |%s|') % (start+i, i, hexa, text)); - return '\n'.join(result); + result.append(('%08x(+%04x): %-'+str(length*(2+1)+1)+'s |%s|') % (start+i, i, hexa, text)) + return '\n'.join(result) def construct_table(lines, separate_head=True): """Prints a formatted table given a 2 dimensional array""" diff --git a/minidump/directory.py b/minidump/directory.py index aee35bd..4a8e790 100644 --- a/minidump/directory.py +++ b/minidump/directory.py @@ -7,6 +7,12 @@ def __init__(self): self.StreamType = None self.Location = None + def to_buffer(self, buffer): + """ + Locaton must be set for the correct location in the databuffer! + """ + buffer.write(self.to_bytes()) + def to_bytes(self): t = self.StreamType.value.to_bytes(4, byteorder = 'little', signed = False) t += self.Location.to_bytes() diff --git a/minidump/header.py b/minidump/header.py index fca5399..6346537 100644 --- a/minidump/header.py +++ b/minidump/header.py @@ -14,6 +14,12 @@ def __init__(self): self.TimeDateStamp = 0 self.Flags = None + def to_buffer(self, buffer): + self.StreamDirectoryRva = buffer.tell() + 16 # stream directory starts right after the end of the header + buffer.write(self.to_bytes()) + # no need to seek here, the directories will be serialized by the file object + + def to_bytes(self): t = self.Signature.encode('ascii') t += self.Version.to_bytes(2, byteorder = 'little', signed = False) diff --git a/minidump/minidumpfile.py b/minidump/minidumpfile.py index ab94da5..4db6626 100644 --- a/minidump/minidumpfile.py +++ b/minidump/minidumpfile.py @@ -16,7 +16,7 @@ from minidump.common_structs import * from minidump.constants import MINIDUMP_STREAM_TYPE from minidump.directory import MINIDUMP_DIRECTORY - +from minidump.constants import MINIDUMP_TYPE class MinidumpFile: def __init__(self): @@ -40,6 +40,60 @@ def __init__(self): self.memory_info = None self.thread_info = None + self.writer = None + + def to_buffer(self, buffer): + """ + Serializes the contents of the minidumpfile to a buffer/file handle + Writer must be already specified! + """ + databuffer = io.BytesIO() # data buffer is where the actual directory data will be stored, except the full memory dump! + mem64_present = False + mem_present = False + + self.header = MinidumpHeader() + self.header.Version = 1 + self.header.ImplementationVersion = 1 + self.header.NumberOfStreams = len([self.writer.get_available_directories]) + self.header.Flags = MINIDUMP_TYPE.MiniDumpWithFullMemory + + hdr_bytes = self.header.to_bytes() + buffer.write(hdr_bytes) + databuffer.write(b'\x00' * len(hdr_bytes)) + for directory in self.writer.get_available_directories: + databuffer.write(b'\x00' * 8) + + for directory in self.writer.get_available_directories: + directory.Location = databuffer.tell() + if directory.StreamType == MINIDUMP_STREAM_TYPE.SystemInfoStream: + self.writer.get_sysinfo(databuffer) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.ModuleListStream: + self.writer.get_modules(databuffer) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.MemoryInfoListStream: + self.writer.get_sections(databuffer) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.Memory64ListStream: + mem64_present = True + continue #skipping this! + elif directory.StreamType == MINIDUMP_STREAM_TYPE.MemoryListStream: + mem_present = True + continue #skipping this! + + directory.to_buffer(buffer) + + if mem64_present is True: + # if memory is present, we add one more directory entry to the directory list, and finalize the header + memdir = MINIDUMP_DIRECTORY() + memdir.Location = databuffer.tell() + memdir.StreamType = MINIDUMP_STREAM_TYPE.Memory64ListStream + memdir.to_buffer(buffer) + databuffer.seek(0,0) + buffer.write(databuffer.read()) + self.writer.get_memory(buffer) # here we use the merged buffer (or the actual file) because memory to dump might be huge + return + + elif mem_present is True: + raise Exception('Not yet implemented!') + @staticmethod def parse(filename): mf = MinidumpFile() diff --git a/minidump/streams/MemoryInfoListStream.py b/minidump/streams/MemoryInfoListStream.py index 47f95ab..bb99017 100644 --- a/minidump/streams/MemoryInfoListStream.py +++ b/minidump/streams/MemoryInfoListStream.py @@ -70,6 +70,13 @@ def __init__(self): self.NumberOfEntries = None self.entries = [] + def to_buffer(self, buffer): + buffer.write(self.SizeOfHeader.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.SizeOfEntry.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(len(self.entries).to_bytes(8, byteorder = 'little', signed = False)) + for ent in self.entries: + buffer.write(ent.to_bytes()) + def get_size(self): return self.SizeOfHeader + len(self.entries)*MINIDUMP_MEMORY_INFO().get_size() diff --git a/minidump/streams/ModuleListStream.py b/minidump/streams/ModuleListStream.py index 649207a..541491d 100644 --- a/minidump/streams/ModuleListStream.py +++ b/minidump/streams/ModuleListStream.py @@ -74,7 +74,8 @@ def __init__(self): self.dwFileDateMS = None self.dwFileDateLS = None - def get_size(self): + @staticmethod + def get_size(): return 13*4 def to_bytes(self): @@ -138,22 +139,40 @@ def __init__(self): #for writer self.ModuleName = None - def get_size(self): - return 8+4+4+4+4+8+8+VS_FIXEDFILEINFO().get_size() + 2 * MINIDUMP_LOCATION_DESCRIPTOR().get_size() - - def to_bytes(self): - t = self.BaseOfImage.to_bytes(8, byteorder = 'little', signed = False) - t += self.SizeOfImage.to_bytes(4, byteorder = 'little', signed = False) - t += self.CheckSum.to_bytes(4, byteorder = 'little', signed = False) - t += self.TimeDateStamp.to_bytes(4, byteorder = 'little', signed = False) - t += self.ModuleNameRva.to_bytes(4, byteorder = 'little', signed = False) - t += self.VersionInfo.to_bytes() - t += self.CvRecord.to_bytes() - t += self.MiscRecord.to_bytes() - t += self.Reserved0.to_bytes(8, byteorder = 'little', signed = False) - t += self.Reserved1.to_bytes(8, byteorder = 'little', signed = False) - return t + def to_buffer(self, buffer): + #beware: MINIDUMP_LOCATION_DESCRIPTOR is used here regardless that sometimes data might be stored above 4GB. FIXME + buffer.write(self.BaseOfImage.to_bytes(8, byteorder = 'little', signed = False)) + buffer.write(self.SizeOfImage.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.CheckSum.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.TimeDateStamp.to_bytes(4, byteorder = 'little', signed = False)) + rva_modname = buffer.tell() + 32 + VS_FIXEDFILEINFO.get_size() + data_modname = MINIDUMP_STRING(self.ModuleName).to_bytes() + buffer.write(rva_modname.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.VersionInfo.to_bytes()) + data_cvrecord = b'' + if self.CvRecord is not None: + data_cvrecord = self.CvRecord.to_bytes() + buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(len(data_cvrecord), 24 + buffer.tell() + len(data_modname))) + else: + buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(0, 0).to_bytes()) + + data_miscrecord = b'' + if self.MiscRecord is not None: + data_miscrecord = self.MiscRecord.to_bytes() + buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(len(data_miscrecord), 16 + buffer.tell() + len(data_modname) + len(data_cvrecord))) + else: + buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(0, 0).to_bytes()) + buffer.write(self.Reserved0.to_bytes(8, byteorder = 'little', signed = False)) + buffer.write(self.Reserved1.to_bytes(8, byteorder = 'little', signed = False)) + #RVAs + buffer.write(data_modname) + if self.CvRecord is not None: + buffer.write(data_cvrecord) + if self.MiscRecord is not None: + buffer.write(data_miscrecord) + + @staticmethod def parse(buff): mm = MINIDUMP_MODULE() @@ -181,14 +200,10 @@ def __init__(self): self.NumberOfModules = None self.Modules = [] - def get_size(self): - return 4 + len(self.Modules) * MINIDUMP_MODULE().get_size() - - def to_bytes(self): - t = len(self.Modules).to_bytes(4, byteorder = 'little', signed = False) + def to_buffer(self, buffer): + buffer.write(len(self.Modules).to_bytes(4, byteorder = 'little', signed = False)) for module in self.Modules: - t += module.to_bytes() - return t + t += module.to_buffer(buffer) @staticmethod def parse(buff): diff --git a/minidump/streams/SystemInfoStream.py b/minidump/streams/SystemInfoStream.py index 05da4d3..27a426e 100644 --- a/minidump/streams/SystemInfoStream.py +++ b/minidump/streams/SystemInfoStream.py @@ -88,48 +88,40 @@ def __init__(self): #for wrtier self.CSDVersion = None - def get_size(self): - # here we cannot tell upfront what the size will be :( - return len(self.to_bytes()) - - def to_bytes(self, data_buffer = None): - t = self.ProcessorArchitecture.value.to_bytes(2, byteorder = 'little', signed = False) - t += self.ProcessorLevel.to_bytes(2, byteorder = 'little', signed = False) - t += self.ProcessorRevision.to_bytes(2, byteorder = 'little', signed = False) - #missing filed here? - t += self.NumberOfProcessors.to_bytes(1, byteorder = 'little', signed = False) - t += self.ProductType.value.to_bytes(1, byteorder = 'little', signed = False) - t += self.MajorVersion.to_bytes(4, byteorder = 'little', signed = False) - t += self.MinorVersion.to_bytes(4, byteorder = 'little', signed = False) - t += self.BuildNumber.to_bytes(4, byteorder = 'little', signed = False) - t += self.PlatformId.to_bytes(4, byteorder = 'little', signed = False) - if data_buffer is None: - t += self.CSDVersionRva.to_bytes(4, byteorder = 'little', signed = False) - else: - pos = data_buffer.tell() - data_buffer.write(100*b'\x00') - self.CSDVersionRva = data_buffer.tell() - data_buffer.write(self.CSDVersion.encode('ascii') + b'\x00') - pos_end = data_buffer.tell() - data_buffer.seek(pos,0) - t += self.CSDVersionRva.to_bytes(4, byteorder = 'little', signed = False) + def to_buffer(self, buffer): + buffer.write(self.ProcessorArchitecture.value.to_bytes(2, byteorder = 'little', signed = False)) + buffer.write(self.ProcessorLevel.to_bytes(2, byteorder = 'little', signed = False)) + buffer.write(self.ProcessorRevision.to_bytes(2, byteorder = 'little', signed = False)) #missing filed here? - t += self.SuiteMask.to_bytes(2, byteorder = 'little', signed = False) - t += self.Reserved2.to_bytes(2, byteorder = 'little', signed = False) + buffer.write(self.NumberOfProcessors.to_bytes(1, byteorder = 'little', signed = False)) + buffer.write(self.ProductType.value.to_bytes(1, byteorder = 'little', signed = False)) + buffer.write(self.MajorVersion.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.MinorVersion.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.BuildNumber.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.PlatformId.to_bytes(4, byteorder = 'little', signed = False)) + + rva_1_pos = buffer.tell() + buffer.write(b'\x00'*4) #we set it to zeroes, then come back later to give the correct RVA + + buffer.write(self.SuiteMask.to_bytes(2, byteorder = 'little', signed = False)) + buffer.write(self.Reserved2.to_bytes(2, byteorder = 'little', signed = False)) if self.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL: for vid in self.VendorId: - t += vid.to_bytes(4, byteorder = 'little', signed = False) - t += self.VersionInformation.value.to_bytes(4, byteorder = 'little', signed = False) - t += self.FeatureInformation.value.to_bytes(4, byteorder = 'little', signed = False) - t += self.AMDExtendedCpuFeatures.value.to_bytes(4, byteorder = 'little', signed = False) + buffer.write(vid.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.VersionInformation.value.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.FeatureInformation.value.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.AMDExtendedCpuFeatures.value.to_bytes(4, byteorder = 'little', signed = False)) else: for pf in self.ProcessorFeatures: - t += pf.to_bytes(8, byteorder = 'little', signed = False) - - if data_buffer is None: - return t - else: - data_buffer.write(t) + buffer.write(pf.to_bytes(8, byteorder = 'little', signed = False)) + + #adding actual data, and writing the correct RVA for rva_1_pos + rva1 = buffer.tell() + buffer.seek(rva_1_pos, 0) + buffer.write(rva1.to_bytes(4, byteorder = 'little', signed = False)) + buffer.seek(rva1, 0) + buffer.write(MINIDUMP_STRING(self.CSDVersion).to_bytes()) + @staticmethod def parse(buff): diff --git a/minidump/writer.py b/minidump/writer.py index b5fcb8e..84bfae5 100644 --- a/minidump/writer.py +++ b/minidump/writer.py @@ -24,22 +24,22 @@ def __init__(self): def setup(self): pass - def get_sysinfo(self): + def get_sysinfo(self, databuffer): pass - def get_modules(self): + def get_modules(self, databuffer): pass - def get_sections(self): + def get_sections(self, databuffer): pass - def get_memory(self): + def get_memory(self, buffer): pass - def get_threads(self): + def get_threads(self, databuffer): pass - def get_exceptions(self): + def get_exceptions(self, databuffer): pass class LiveSystemReader(MinidumpSystemReader): @@ -49,15 +49,23 @@ def __init__(self, pid): self.process_handle = None self.sysinfo = None self.meminfolist = None + self.streamtypes = [MINIDUMP_STREAM_TYPE.SystemInfoStream, MINIDUMP_STREAM_TYPE.ModuleListStream, MINIDUMP_STREAM_TYPE.MemoryInfoListStream, MINIDUMP_STREAM_TYPE.Memory64ListStream] self.setup() + def get_available_directories(self): + for st in self.streamtypes: + memdir = MINIDUMP_DIRECTORY() + memdir.Location = 0 #location is not needed, only the type + memdir.StreamType = st + yield memdir + def open_process(self): self.process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, self.pid) def setup(self): self.open_process() - def get_sysinfo(self): + def get_sysinfo(self, databuffer): #https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo sysinfo_raw = GetSystemInfo() version_raw = GetVersionExW() @@ -88,13 +96,13 @@ def get_sysinfo(self): sysinfo.AMDExtendedCpuFeatures = 0 else: sysinfo.ProcessorFeatures = [0,0] - - self.sysinfo_raw = sysinfo_raw - - return sysinfo + + self.sysinfo_raw = sysinfo_raw #other functions will be using data from sysinfo so we need to store it! + sysinfo.to_buffer(databuffer) + - def get_modules(self): + def get_modules(self, databuffer): #https://docs.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process #https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules # @@ -119,12 +127,10 @@ def get_modules(self): module_list.Modules.append(mmod) - return module_list + module_list.to_buffer(databuffer) - def get_sections(self): + def get_sections(self, databuffer): #https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualqueryex - if self.sysinfo_raw is None: - self.get_sysinfo() meminfolist = MINIDUMP_MEMORY_INFO_LIST() i = self.sysinfo_raw.lpMinimumApplicationAddress while i < self.sysinfo_raw.lpMaximumApplicationAddress: @@ -152,15 +158,15 @@ def get_sections(self): i += mi_raw.RegionSize self.meminfolist = meminfolist - return meminfolist + meminfolist.to_buffer(databuffer) - def get_threads(self): + def get_threads(self, databuffer): pass - def get_exceptions(self): + def get_exceptions(self, databuffer): pass - def get_memory(self): + def get_memory(self, buffer): read_flags = [AllocationProtect.PAGE_EXECUTE_READ, AllocationProtect.PAGE_EXECUTE_READWRITE, AllocationProtect.PAGE_READONLY, @@ -180,136 +186,140 @@ def get_memory(self): memlist.MemoryRanges.append(memdesc) return memlist - - -class MinidumpWriter: - def __init__(self,sysreader): - self.sysreader = sysreader - self.output_file = None - - self.streams = {} #stream type -> list of stream objects - - self.header = None - self.directory_list = [] - self.directory_rva = 28 - self.header_size = None - - self.header_buffer = io.BytesIO() - self.data_buffer = io.BytesIO() - - def prepare_header(self): - self.header = MinidumpHeader() - self.header.Version = 1 - self.header.ImplementationVersion = 1 - self.header.NumberOfStreams = len(self.streams) +1 # +1 is fot he memory info stream - self.header.StreamDirectoryRva = self.directory_rva - #self.header.CheckSum = None - #self.header.Reserved = None - #self.header.TimeDateStamp = None - self.header.Flags = MINIDUMP_TYPE.MiniDumpWithFullMemory - self.header_buffer.write(self.header.to_bytes()) - - def prepare_directory(self): - curr_pos = self.header_size - for streamtype in self.streams: - self.streams[streamtype].to_bytes(self.data_buffer) - directory = MINIDUMP_DIRECTORY() - directory.StreamType = streamtype - directory.Location = curr_pos - self.header_buffer.write(directory.to_bytes()) - - def finalize_header(self): - # currently only using the 32 bit MINIDUMP_LOCATION_DESCRIPTOR, this is because we expect that the header - # and any data in the header (including all streams data except memory stream) will not be bigger than 4GB - # memory stream is a special case, as it cvan be longer than 4GB but the RVA to the beginning of the memory stream - # is not expected to be bigger than 4G max. - # if this becomes the case then this all will fail :) - header_size = 28 - header_size += len(self.streams) * 8 #this is for the dictionary itself, not the streams - for stream in self.streams: - header_size += self.streams[stream].get_size() - - header_size += 10 * 1024 #allocating 10k for the memory info - - self.prepare_header() - self.prepare_directory() - - - - - - def create_streams(self): - sysinfo = self.sysreader.get_sysinfo() - self.streams[MINIDUMP_STREAM_TYPE.SystemInfoStream] = sysinfo - - print(str(sysinfo)) - moduleinfo = self.sysreader.get_modules() - self.streams[MINIDUMP_STREAM_TYPE.ModuleListStream] = moduleinfo - - sections = self.sysreader.get_sections() - self.streams[MINIDUMP_STREAM_TYPE.MemoryInfoListStream] = sections - - self.finalize_header() - - memory = self.sysreader.get_memory() - - - #def get_total_streams_cnt(self): - # total = 0 - # for t in self.streams: - # total += len(t) - # return total - - - - - - #def construct_directory(self): - # - # total_streams = self.get_total_streams_cnt() - # - # for stype in self.streams: - # for stream in self.streams[stype]: - # - # stream - # - # loc = MINIDUMP_LOCATION_DESCRIPTOR() - # loc.DataSize = 0 - # loc.Rva = 0 - # directory = MINIDUMP_DIRECTORY() - # directory.StreamType = stream - # self.directory.append() - - - def write_header(self): - hdr_pos = self.hdr_buff.tell() - self.hdr_buff.seek(0,0) - self.hdr_buff.write(self.construct_header()) - self.hdr_buff.seek(hdr_pos, 0) - return - - - def construct_directory(self): - self.sysreader.get_sysinfo(self.hdr_buff, self.data_buff) - self.stream_cnt += 1 - #modules - #self.sysreader.get_modules(self.hdr_buff, self.data_buff) - #self.stream_cnt += 1 - - #write header - self.write_header() - - - #append datastream for memory, with correct rva - - #dump memory - - def run(self): - self.create_streams() - - +# +# +#class MinidumpWriter: +# def __init__(self,sysreader): +# self.sysreader = sysreader +# self.output_file = None +# +# self.streams = {} #stream type -> list of stream objects +# +# self.header = None +# self.directory_list = [] +# self.directory_rva = 28 +# self.header_size = None +# +# self.header_buffer = io.BytesIO() +# self.data_buffer = io.BytesIO() +# +# def prepare_header(self): +# self.header = MinidumpHeader() +# self.header.Version = 1 +# self.header.ImplementationVersion = 1 +# self.header.NumberOfStreams = len(self.streams) +1 # +1 is fot he memory info stream +# self.header.StreamDirectoryRva = self.directory_rva +# #self.header.CheckSum = None +# #self.header.Reserved = None +# #self.header.TimeDateStamp = None +# self.header.Flags = MINIDUMP_TYPE.MiniDumpWithFullMemory +# self.header_buffer.write(self.header.to_bytes()) +# +# def prepare_directory(self): +# curr_pos = self.header_size +# for streamtype in self.streams: +# self.streams[streamtype].to_bytes(self.data_buffer) +# directory = MINIDUMP_DIRECTORY() +# directory.StreamType = streamtype +# directory.Location = curr_pos +# self.header_buffer.write(directory.to_bytes()) +# +# def finalize_header(self): +# # currently only using the 32 bit MINIDUMP_LOCATION_DESCRIPTOR, this is because we expect that the header +# # and any data in the header (including all streams data except memory stream) will not be bigger than 4GB +# # memory stream is a special case, as it cvan be longer than 4GB but the RVA to the beginning of the memory stream +# # is not expected to be bigger than 4G max. +# # if this becomes the case then this all will fail :) +# header_size = 28 +# header_size += len(self.streams) * 8 #this is for the dictionary itself, not the streams +# for stream in self.streams: +# header_size += self.streams[stream].get_size() +# +# header_size += 10 * 1024 #allocating 10k for the memory info +# +# self.prepare_header() +# self.prepare_directory() +# +# +# +# +# +# def create_streams(self): +# sysinfo = self.sysreader.get_sysinfo() +# self.streams[MINIDUMP_STREAM_TYPE.SystemInfoStream] = sysinfo +# +# print(str(sysinfo)) +# moduleinfo = self.sysreader.get_modules() +# self.streams[MINIDUMP_STREAM_TYPE.ModuleListStream] = moduleinfo +# +# sections = self.sysreader.get_sections() +# self.streams[MINIDUMP_STREAM_TYPE.MemoryInfoListStream] = sections +# +# self.finalize_header() +# +# memory = self.sysreader.get_memory() +# +# +# #def get_total_streams_cnt(self): +# # total = 0 +# # for t in self.streams: +# # total += len(t) +# # return total +# +# +# +# +# +# #def construct_directory(self): +# # +# # total_streams = self.get_total_streams_cnt() +# # +# # for stype in self.streams: +# # for stream in self.streams[stype]: +# # +# # stream +# # +# # loc = MINIDUMP_LOCATION_DESCRIPTOR() +# # loc.DataSize = 0 +# # loc.Rva = 0 +# # directory = MINIDUMP_DIRECTORY() +# # directory.StreamType = stream +# # self.directory.append() +# +# +# def write_header(self): +# hdr_pos = self.hdr_buff.tell() +# self.hdr_buff.seek(0,0) +# self.hdr_buff.write(self.construct_header()) +# self.hdr_buff.seek(hdr_pos, 0) +# return +# +# +# def construct_directory(self): +# self.sysreader.get_sysinfo(self.hdr_buff, self.data_buff) +# self.stream_cnt += 1 +# #modules +# #self.sysreader.get_modules(self.hdr_buff, self.data_buff) +# #self.stream_cnt += 1 +# +# #write header +# self.write_header() +# +# +# #append datastream for memory, with correct rva +# +# #dump memory +# +# def run(self): +# self.create_streams() +# +# if __name__ == '__main__': + from minidump.minidumpfile import MinidumpFile pid = 9600 sysreader = LiveSystemReader(pid) - writer = MinidumpWriter(sysreader) - writer.run() + with open('test.dmp', 'wb') as f: + mf = MinidumpFile() + mf.writer = sysreader + mf.to_buffer(f) + From 6564a315f84612b274c5bec1b19d82b4f15e6f1b Mon Sep 17 00:00:00 2001 From: skelsec Date: Fri, 10 Jan 2020 03:01:52 +0100 Subject: [PATCH 2/7] working prototype for writer --- .gitignore | 3 + minidump/common_structs.py | 1 + minidump/directory.py | 86 ++++++- minidump/header.py | 8 +- minidump/minidumpfile.py | 73 ++++-- minidump/streams/HandleDataStream.py | 17 +- minidump/streams/Memory64ListStream.py | 22 +- minidump/streams/MemoryInfoListStream.py | 16 +- minidump/streams/ModuleListStream.py | 47 ++-- minidump/streams/SystemInfoStream.py | 29 ++- minidump/streams/ThreadInfoListStream.py | 14 +- minidump/streams/ThreadListStream.py | 7 +- minidump/streams/UnloadedModuleListStream.py | 13 +- minidump/utils/winapi/kernel32.py | 247 ++++++++++++++++++- minidump/utils/winapi/version.py | 1 + minidump/writer.py | 123 +++++++-- 16 files changed, 586 insertions(+), 121 deletions(-) diff --git a/.gitignore b/.gitignore index 894a44c..71bc2a9 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,6 @@ venv.bak/ # mypy .mypy_cache/ +*.exe +*.DMP +*.dmp diff --git a/minidump/common_structs.py b/minidump/common_structs.py index 39516ec..b677b81 100644 --- a/minidump/common_structs.py +++ b/minidump/common_structs.py @@ -56,6 +56,7 @@ def __init__(self, data = None, encoding = 'utf-16-le'): if data is not None: self.Buffer = data.encode(self.encoding) self.Length = len(self.Buffer) + def to_bytes(self): t = self.Length.to_bytes(4, byteorder = 'little', signed = False) diff --git a/minidump/directory.py b/minidump/directory.py index 4a8e790..07301f4 100644 --- a/minidump/directory.py +++ b/minidump/directory.py @@ -2,6 +2,8 @@ from minidump.constants import MINIDUMP_STREAM_TYPE from minidump.common_structs import MINIDUMP_LOCATION_DESCRIPTOR +import io + class MINIDUMP_DIRECTORY: def __init__(self): self.StreamType = None @@ -42,4 +44,86 @@ def parse(buff): def __str__(self): t = 'StreamType: %s %s' % (self.StreamType, self.Location) - return t \ No newline at end of file + return t + + +class DirectoryBuffer: + def __init__(self, offset = 0): + self.offset = offset + self.buffer = io.BytesIO() + self.databuffer = io.BytesIO() + + self.rvas = [] #ptr_position_in_buffer, data_pos_in_databuffer + self.lds = [] # pos, size + + def write_rva(self, data): + """ + Stores the data in databuffer and returns an RVA position relative to buffer's start + """ + #pos = self.buffer.tell() + self.databuffer.tell() + self.offset + #self.databuffer.write(data) + #self.buffer.write(pos.to_bytes(4, byteorder = 'little', signed = False)) + + data_pos = self.databuffer.tell() + self.databuffer.write(data) + ptr_pos = self.buffer.tell() + self.buffer.write(b'\x00' * 4) + self.rvas.append((ptr_pos, data_pos)) + return + + def write_ld(self, data): + #""" + #writes a location descriptor to the buffer and the actual data to the databuffer + #""" + + #pos = self.buffer.tell() + self.databuffer.tell() + self.offset + #ld = MINIDUMP_LOCATION_DESCRIPTOR(len(data), pos) + #self.databuffer.write(data) + #self.buffer.write(ld.to_bytes()) + + data_pos = self.databuffer.tell() + self.databuffer.write(data) + ptr_pos = self.buffer.tell() + self.buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(0,0).to_bytes()) + self.lds.append((ptr_pos, data_pos, len(data))) + + return + + def write_data(self, data): + return self.databuffer.write(data) + + def write(self, data): + return self.buffer.write(data) + + def tell(self): + return self.buffer.tell() + + def seek(self, pos, whence): + print('Seek is not advised!') + return self.buffer.seek(pos, whence) + + def read(self, count = 1): + return self.buffer.read(count) + + def finalize(self): + self.databuffer.seek(0,0) + buffer_end = self.buffer.tell() + self.offset + for ptr_pos, data_pos in self.rvas: + final_pos = data_pos + buffer_end - 0x10 # TODO! figure out the offset? + self.buffer.seek(ptr_pos) + self.buffer.write(final_pos.to_bytes(4, byteorder ='little', signed= False)) + + for ptr_pos, data_pos, data_size in self.lds: + final_pos = data_pos + buffer_end - 0x10 # TODO! figure out the offset? + self.buffer.seek(ptr_pos) + self.buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(data_size, final_pos).to_bytes()) + + self.buffer.write(self.databuffer.read()) + self.databuffer = None + self.buffer.seek(0,0) + return self.buffer.read() + + + + + \ No newline at end of file diff --git a/minidump/header.py b/minidump/header.py index 6346537..230da78 100644 --- a/minidump/header.py +++ b/minidump/header.py @@ -4,7 +4,7 @@ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680378(v=vs.85).aspx class MinidumpHeader: def __init__(self): - self.Signature = 'PMDM' + self.Signature = 'MDMP' self.Version = None self.ImplementationVersion = None self.NumberOfStreams = None @@ -14,12 +14,6 @@ def __init__(self): self.TimeDateStamp = 0 self.Flags = None - def to_buffer(self, buffer): - self.StreamDirectoryRva = buffer.tell() + 16 # stream directory starts right after the end of the header - buffer.write(self.to_bytes()) - # no need to seek here, the directories will be serialized by the file object - - def to_bytes(self): t = self.Signature.encode('ascii') t += self.Version.to_bytes(2, byteorder = 'little', signed = False) diff --git a/minidump/minidumpfile.py b/minidump/minidumpfile.py index 4db6626..715e0ed 100644 --- a/minidump/minidumpfile.py +++ b/minidump/minidumpfile.py @@ -15,7 +15,7 @@ from minidump.streams import * from minidump.common_structs import * from minidump.constants import MINIDUMP_STREAM_TYPE -from minidump.directory import MINIDUMP_DIRECTORY +from minidump.directory import MINIDUMP_DIRECTORY, DirectoryBuffer from minidump.constants import MINIDUMP_TYPE class MinidumpFile: @@ -52,47 +52,84 @@ def to_buffer(self, buffer): mem_present = False self.header = MinidumpHeader() - self.header.Version = 1 - self.header.ImplementationVersion = 1 - self.header.NumberOfStreams = len([self.writer.get_available_directories]) - self.header.Flags = MINIDUMP_TYPE.MiniDumpWithFullMemory + self.header.Version = 42899 + self.header.ImplementationVersion = 41146 + self.header.NumberOfStreams = len(self.writer.get_available_directories()) + self.header.Flags = MINIDUMP_TYPE.MiniDumpNormal + self.header.StreamDirectoryRva = buffer.tell() + 32 hdr_bytes = self.header.to_bytes() buffer.write(hdr_bytes) databuffer.write(b'\x00' * len(hdr_bytes)) - for directory in self.writer.get_available_directories: - databuffer.write(b'\x00' * 8) + for directory in self.writer.get_available_directories(): + databuffer.write(b'\x00' * 12) - for directory in self.writer.get_available_directories: - directory.Location = databuffer.tell() + databuff_trim = databuffer.tell() + input(hex(databuff_trim)) + offset = databuff_trim + for directory in self.writer.get_available_directories(): + directory.Location = MINIDUMP_LOCATION_DESCRIPTOR() + directory.Location.Rva = databuffer.tell() + db = DirectoryBuffer(offset = offset) if directory.StreamType == MINIDUMP_STREAM_TYPE.SystemInfoStream: - self.writer.get_sysinfo(databuffer) + self.writer.get_sysinfo(db) elif directory.StreamType == MINIDUMP_STREAM_TYPE.ModuleListStream: - self.writer.get_modules(databuffer) + self.writer.get_modules(db) elif directory.StreamType == MINIDUMP_STREAM_TYPE.MemoryInfoListStream: - self.writer.get_sections(databuffer) + self.writer.get_sections(db) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.UnloadedModuleListStream: + self.writer.get_unloaded_modules(db) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.HandleDataStream: + self.writer.get_handle_data(db) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.ThreadInfoListStream: + self.writer.get_threadinfo(db) + elif directory.StreamType == MINIDUMP_STREAM_TYPE.ThreadListStream: + self.writer.get_threads(db) elif directory.StreamType == MINIDUMP_STREAM_TYPE.Memory64ListStream: mem64_present = True continue #skipping this! elif directory.StreamType == MINIDUMP_STREAM_TYPE.MemoryListStream: mem_present = True continue #skipping this! - + #else: + # #raise Exception('Unknown directory type here!') + + buffsize = db.buffer.tell() + databuffer.write(db.finalize()) + #directory.Location.DataSize = + directory.Location.DataSize = buffsize + offset += databuffer.tell() - directory.Location.Rva + #input(hex(directory.Location.DataSize)) directory.to_buffer(buffer) if mem64_present is True: # if memory is present, we add one more directory entry to the directory list, and finalize the header + end_pos = buffer.tell() #this is stored to update the actual size of the memory stream after dumping! + memdir = MINIDUMP_DIRECTORY() - memdir.Location = databuffer.tell() + memdir.Location = MINIDUMP_LOCATION_DESCRIPTOR() + memdir.Location.Rva = databuffer.tell() memdir.StreamType = MINIDUMP_STREAM_TYPE.Memory64ListStream + memdir.Location.DataSize = 0 #marking it as zero now. will need to update this later! #databuffer.tell() - directory.Location.Rva memdir.to_buffer(buffer) - databuffer.seek(0,0) + + + + databuffer.seek(databuff_trim,0) buffer.write(databuffer.read()) - self.writer.get_memory(buffer) # here we use the merged buffer (or the actual file) because memory to dump might be huge + datasize = self.writer.get_memory(buffer) # here we use the merged buffer (or the actual file) because memory to dump might be huge + + buffer.seek(end_pos + 4) #skipping the type + buffer.write(datasize.to_bytes(4, byteorder = 'little', signed = False)) + + print(datasize) return + + elif mem_present is True: raise Exception('Not yet implemented!') + @staticmethod def parse(filename): @@ -130,6 +167,7 @@ def get_reader(self): def _parse(self): self.__parse_header() + print(self.header) self.__parse_directories() def __parse_header(self): @@ -146,8 +184,9 @@ def __parse_header(self): logging.debug('Found Unknown UserStream directory Type: %x' % (user_stream_type_value)) def __parse_directories(self): - + for dir in self.directories: + #print(dir.StreamType) if dir.StreamType == MINIDUMP_STREAM_TYPE.UnusedStream: logging.debug('Found UnusedStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize)) continue # Reserved. Do not use this enumeration value. diff --git a/minidump/streams/HandleDataStream.py b/minidump/streams/HandleDataStream.py index 061e141..19cb2ff 100644 --- a/minidump/streams/HandleDataStream.py +++ b/minidump/streams/HandleDataStream.py @@ -10,11 +10,17 @@ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680372(v=vs.85).aspx class MINIDUMP_HANDLE_DATA_STREAM: def __init__(self): - self.SizeOfHeader = None - self.SizeOfDescriptor = None + self.SizeOfHeader = 16 + self.SizeOfDescriptor = 40 self.NumberOfDescriptors = None - self.Reserved = None + self.Reserved = 0 + def to_buffer(self, buffer): + buffer.write(self.SizeOfHeader.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.SizeOfDescriptor.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.NumberOfDescriptors.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.Reserved.to_bytes(4, byteorder = 'little', signed = False)) + @staticmethod def parse(buff): mhds = MINIDUMP_HANDLE_DATA_STREAM() @@ -22,6 +28,11 @@ def parse(buff): mhds.SizeOfDescriptor = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mhds.NumberOfDescriptors = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mhds.Reserved = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) + #print(mhds.SizeOfHeader) + #print(mhds.SizeOfDescriptor) + #print(mhds.NumberOfDescriptors) + #print(mhds.Reserved) + #print() return mhds diff --git a/minidump/streams/Memory64ListStream.py b/minidump/streams/Memory64ListStream.py index 9d9415b..5083173 100644 --- a/minidump/streams/Memory64ListStream.py +++ b/minidump/streams/Memory64ListStream.py @@ -13,15 +13,11 @@ def __init__(self): self.BaseRva = None self.MemoryRanges = [] - def get_size(self): - return 8 + 8 + len(self.MemoryRanges) * MINIDUMP_MEMORY_DESCRIPTOR64().get_size() - - def to_bytes(self): - t = len(self.MemoryRanges).to_bytes(8, byteorder = 'little', signed = False) - t += self.BaseRva.to_bytes(8, byteorder = 'little', signed = False) + def to_buffer(self, buffer): + buffer.write(len(self.MemoryRanges).to_bytes(8, byteorder = 'little', signed = False)) + buffer.write(self.BaseRva.to_bytes(8, byteorder = 'little', signed = False)) for memrange in self.MemoryRanges: - t += memrange.to_bytes() - return t + memrange.to_buffer(buffer) @staticmethod def parse(buff): @@ -48,13 +44,9 @@ def __init__(self): self.StartOfMemoryRange = None self.DataSize = None - def get_size(self): - return 16 - - def to_bytes(self): - t = self.StartOfMemoryRange.to_bytes(8, byteorder = 'little', signed = False) - t += self.DataSize.to_bytes(8, byteorder = 'little', signed = False) - return t + def to_buffer(self, buffer): + buffer.write(self.StartOfMemoryRange.to_bytes(8, byteorder = 'little', signed = False)) + buffer.write(self.DataSize.to_bytes(8, byteorder = 'little', signed = False)) @staticmethod def parse(buff): diff --git a/minidump/streams/MemoryInfoListStream.py b/minidump/streams/MemoryInfoListStream.py index bb99017..799c4f0 100644 --- a/minidump/streams/MemoryInfoListStream.py +++ b/minidump/streams/MemoryInfoListStream.py @@ -8,7 +8,6 @@ from minidump.common_structs import * class AllocationProtect(enum.Enum): - NONE = 0 PAGE_EXECUTE = 0x10 #Enables execute access to the committed region of pages. An attempt to write to the committed region results in an access violation. #This flag is not supported by the CreateFileMapping function. @@ -123,9 +122,18 @@ def to_bytes(self): t += self.AllocationProtect.to_bytes(4, byteorder = 'little', signed = False) t += self.__alignment1.to_bytes(4, byteorder = 'little', signed = False) t += self.RegionSize.to_bytes(8, byteorder = 'little', signed = False) - t += self.State.value.to_bytes(4, byteorder = 'little', signed = False) - t += self.Protect.value.to_bytes(4, byteorder = 'little', signed = False) - t += self.Type.value.to_bytes(4, byteorder = 'little', signed = False) + if isinstance(self.State, int): + t += self.State.to_bytes(4, byteorder = 'little', signed = False) + else: + t += self.State.value.to_bytes(4, byteorder = 'little', signed = False) + if isinstance(self.Protect, int): + t += self.Protect.to_bytes(4, byteorder = 'little', signed = False) + else: + t += self.Protect.value.to_bytes(4, byteorder = 'little', signed = False) + if isinstance(self.Type, int): + t += self.Type.to_bytes(4, byteorder = 'little', signed = False) + else: + t += self.Type.value.to_bytes(4, byteorder = 'little', signed = False) t += self.__alignment2.to_bytes(4, byteorder = 'little', signed = False) return t diff --git a/minidump/streams/ModuleListStream.py b/minidump/streams/ModuleListStream.py index 541491d..b0d44c4 100644 --- a/minidump/streams/ModuleListStream.py +++ b/minidump/streams/ModuleListStream.py @@ -28,7 +28,11 @@ def parse(mod, buff): mm.size = mod.SizeOfImage mm.checksum = mod.CheckSum mm.timestamp = mod.TimeDateStamp - mm.name = MINIDUMP_STRING.get_from_rva(mod.ModuleNameRva, buff) + try: + mm.name = MINIDUMP_STRING.get_from_rva(mod.ModuleNameRva, buff) + except: + print('high: %s' % hex(mod.ModuleNameRva)) + raise mm.versioninfo = mod.VersionInfo mm.endaddress = mm.baseaddress + mm.size return mm @@ -141,40 +145,36 @@ def __init__(self): def to_buffer(self, buffer): #beware: MINIDUMP_LOCATION_DESCRIPTOR is used here regardless that sometimes data might be stored above 4GB. FIXME + pos1 = buffer.tell() buffer.write(self.BaseOfImage.to_bytes(8, byteorder = 'little', signed = False)) buffer.write(self.SizeOfImage.to_bytes(4, byteorder = 'little', signed = False)) buffer.write(self.CheckSum.to_bytes(4, byteorder = 'little', signed = False)) buffer.write(self.TimeDateStamp.to_bytes(4, byteorder = 'little', signed = False)) - rva_modname = buffer.tell() + 32 + VS_FIXEDFILEINFO.get_size() - data_modname = MINIDUMP_STRING(self.ModuleName).to_bytes() - buffer.write(rva_modname.to_bytes(4, byteorder = 'little', signed = False)) - buffer.write(self.VersionInfo.to_bytes()) - data_cvrecord = b'' + buffer.write_rva(MINIDUMP_STRING(self.ModuleName).to_bytes()) + + if self.VersionInfo is not None: + buffer.write(self.VersionInfo.to_bytes()) + else: + buffer.write(b'\x00' * VS_FIXEDFILEINFO.get_size()) if self.CvRecord is not None: - data_cvrecord = self.CvRecord.to_bytes() - buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(len(data_cvrecord), 24 + buffer.tell() + len(data_modname))) + buffer.write_ld(self.VersionInfo.to_bytes()) else: - buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(0, 0).to_bytes()) + buffer.write_ld(b'') - data_miscrecord = b'' if self.MiscRecord is not None: - data_miscrecord = self.MiscRecord.to_bytes() - buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(len(data_miscrecord), 16 + buffer.tell() + len(data_modname) + len(data_cvrecord))) + buffer.write_ld(self.MiscRecord.to_bytes()) else: - buffer.write(MINIDUMP_LOCATION_DESCRIPTOR(0, 0).to_bytes()) + buffer.write_ld(b'') buffer.write(self.Reserved0.to_bytes(8, byteorder = 'little', signed = False)) buffer.write(self.Reserved1.to_bytes(8, byteorder = 'little', signed = False)) - - #RVAs - buffer.write(data_modname) - if self.CvRecord is not None: - buffer.write(data_cvrecord) - if self.MiscRecord is not None: - buffer.write(data_miscrecord) - @staticmethod def parse(buff): + p1 = buff.tell() + #print('MINIDUMP_MODULE') + #print(hex(p1)) + #input(hexdump(buff.read(6*8+4*4))) + buff.seek(p1) mm = MINIDUMP_MODULE() mm.BaseOfImage = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) mm.SizeOfImage = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) @@ -186,6 +186,7 @@ def parse(buff): mm.MiscRecord = MINIDUMP_LOCATION_DESCRIPTOR.parse(buff) mm.Reserved0 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) mm.Reserved1 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) + #print('RVA: %s' % hex(mm.ModuleNameRva)) return mm def __str__(self): @@ -203,7 +204,7 @@ def __init__(self): def to_buffer(self, buffer): buffer.write(len(self.Modules).to_bytes(4, byteorder = 'little', signed = False)) for module in self.Modules: - t += module.to_buffer(buffer) + module.to_buffer(buffer) @staticmethod def parse(buff): @@ -222,6 +223,8 @@ def __init__(self): def parse(dir, buff): t = MinidumpModuleList() buff.seek(dir.Location.Rva) + input('ds %s' % dir.Location.DataSize) + input('rva %s' % hex(dir.Location.Rva)) chunk = io.BytesIO(buff.read(dir.Location.DataSize)) mtl = MINIDUMP_MODULE_LIST.parse(chunk) for mod in mtl.Modules: diff --git a/minidump/streams/SystemInfoStream.py b/minidump/streams/SystemInfoStream.py index 27a426e..097492a 100644 --- a/minidump/streams/SystemInfoStream.py +++ b/minidump/streams/SystemInfoStream.py @@ -68,7 +68,7 @@ def __init__(self): self.ProcessorArchitecture = None self.ProcessorLevel = None self.ProcessorRevision = None - self.Reserved0 = None + self.Reserved0 = 0 self.NumberOfProcessors = None self.ProductType = None self.MajorVersion = None @@ -89,10 +89,12 @@ def __init__(self): self.CSDVersion = None def to_buffer(self, buffer): + #buff => DirectoryBuffer + start = buffer.tell() buffer.write(self.ProcessorArchitecture.value.to_bytes(2, byteorder = 'little', signed = False)) buffer.write(self.ProcessorLevel.to_bytes(2, byteorder = 'little', signed = False)) buffer.write(self.ProcessorRevision.to_bytes(2, byteorder = 'little', signed = False)) - #missing filed here? + #buffer.write(self.Reserved0.to_bytes(2, byteorder = 'little', signed = False)) ### RESERVED 0 in parsing there is no such thing :/// ???? buffer.write(self.NumberOfProcessors.to_bytes(1, byteorder = 'little', signed = False)) buffer.write(self.ProductType.value.to_bytes(1, byteorder = 'little', signed = False)) buffer.write(self.MajorVersion.to_bytes(4, byteorder = 'little', signed = False)) @@ -100,11 +102,13 @@ def to_buffer(self, buffer): buffer.write(self.BuildNumber.to_bytes(4, byteorder = 'little', signed = False)) buffer.write(self.PlatformId.to_bytes(4, byteorder = 'little', signed = False)) - rva_1_pos = buffer.tell() - buffer.write(b'\x00'*4) #we set it to zeroes, then come back later to give the correct RVA - + if self.CSDVersion is not None: + buffer.write_rva(self.CSDVersion.to_bytes()) + else: + buffer.write(b'\x00'*4) buffer.write(self.SuiteMask.to_bytes(2, byteorder = 'little', signed = False)) buffer.write(self.Reserved2.to_bytes(2, byteorder = 'little', signed = False)) + #32 if self.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL: for vid in self.VendorId: buffer.write(vid.to_bytes(4, byteorder = 'little', signed = False)) @@ -115,12 +119,9 @@ def to_buffer(self, buffer): for pf in self.ProcessorFeatures: buffer.write(pf.to_bytes(8, byteorder = 'little', signed = False)) - #adding actual data, and writing the correct RVA for rva_1_pos - rva1 = buffer.tell() - buffer.seek(rva_1_pos, 0) - buffer.write(rva1.to_bytes(4, byteorder = 'little', signed = False)) - buffer.seek(rva1, 0) - buffer.write(MINIDUMP_STRING(self.CSDVersion).to_bytes()) + #FIXME this should never be happening if the parsing was totally correct :() + if buffer.tell() - start < 0x38: + buffer.write(b'\x00' * (0x38 - (buffer.tell() - start))) @staticmethod @@ -217,7 +218,10 @@ def parse(dir, buff): t = MinidumpSystemInfo() buff.seek(dir.Location.Rva) chunk = io.BytesIO(buff.read(dir.Location.DataSize)) + print(hexdump(chunk.read())) + chunk.seek(0,0) si = MINIDUMP_SYSTEM_INFO.parse(chunk) + print(str) t.ProcessorArchitecture = si.ProcessorArchitecture t.ProcessorLevel = si.ProcessorLevel t.ProcessorRevision = si.ProcessorRevision @@ -227,7 +231,8 @@ def parse(dir, buff): t.MinorVersion = si.MinorVersion t.BuildNumber = si.BuildNumber t.PlatformId = si.PlatformId - t.CSDVersion = MINIDUMP_STRING.get_from_rva(si.CSDVersionRva, buff) + if si.CSDVersionRva != 0: + t.CSDVersion = MINIDUMP_STRING.get_from_rva(si.CSDVersionRva, buff) t.SuiteMask = si.SuiteMask t.VendorId = si.VendorId t.VersionInformation = si.VersionInformation diff --git a/minidump/streams/ThreadInfoListStream.py b/minidump/streams/ThreadInfoListStream.py index 329bcb9..b6fd891 100644 --- a/minidump/streams/ThreadInfoListStream.py +++ b/minidump/streams/ThreadInfoListStream.py @@ -19,15 +19,14 @@ class DumpFlags(enum.Enum): # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680506(v=vs.85).aspx class MINIDUMP_THREAD_INFO_LIST: def __init__(self): - self.SizeOfHeader = None - self.SizeOfEntry = None + self.SizeOfHeader = 12 + self.SizeOfEntry = 64 self.NumberOfEntries = None - def to_bytes(self): - t = self.SizeOfHeader.value.to_bytes(4, byteorder = 'little', signed = False) - t += self.SizeOfEntry.to_bytes(4, byteorder = 'little', signed = False) - t += self.NumberOfEntries.to_bytes(4, byteorder = 'little', signed = False) - return t + def to_buffer(self, buffer): + buffer.write(self.SizeOfHeader.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.SizeOfEntry.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.NumberOfEntries.to_bytes(4, byteorder = 'little', signed = False)) @staticmethod def parse(buff): @@ -35,7 +34,6 @@ def parse(buff): mtil.SizeOfHeader = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mtil.SizeOfEntry = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mtil.NumberOfEntries = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) - return mtil diff --git a/minidump/streams/ThreadListStream.py b/minidump/streams/ThreadListStream.py index 69bd836..8b064f1 100644 --- a/minidump/streams/ThreadListStream.py +++ b/minidump/streams/ThreadListStream.py @@ -13,11 +13,10 @@ def __init__(self): self.NumberOfThreads = None self.Threads = [] - def to_bytes(self): - t = len(self.Threads).to_bytes(4, byteorder = 'little', signed = False) + def to_buffer(self, buffer): + buffer.write(len(self.Threads).to_bytes(4, byteorder = 'little', signed = False)) for th in self.Threads: - t += th.to_bytes() - return t + th.to_buffer(buffer) @staticmethod def parse(buff): diff --git a/minidump/streams/UnloadedModuleListStream.py b/minidump/streams/UnloadedModuleListStream.py index fed75a3..fe8eb0b 100644 --- a/minidump/streams/UnloadedModuleListStream.py +++ b/minidump/streams/UnloadedModuleListStream.py @@ -10,15 +10,14 @@ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680521(v=vs.85).aspx class MINIDUMP_UNLOADED_MODULE_LIST: def __init__(self): - self.SizeOfHeader = None - self.SizeOfEntry = None + self.SizeOfHeader = 12 + self.SizeOfEntry = 24 self.NumberOfEntries = None - def to_bytes(self): - t = self.SizeOfHeader.value.to_bytes(4, byteorder = 'little', signed = False) - t += self.SizeOfEntry.to_bytes(4, byteorder = 'little', signed = False) - t += self.NumberOfEntries.to_bytes(4, byteorder = 'little', signed = False) - return t + def to_buffer(self, buffer): + buffer.write(self.SizeOfHeader.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.SizeOfEntry.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.NumberOfEntries.to_bytes(4, byteorder = 'little', signed = False)) @staticmethod def parse(buff): diff --git a/minidump/utils/winapi/kernel32.py b/minidump/utils/winapi/kernel32.py index 8a955d9..7c25c2a 100644 --- a/minidump/utils/winapi/kernel32.py +++ b/minidump/utils/winapi/kernel32.py @@ -178,4 +178,249 @@ def ReadProcessMemory(hProcess, lpBaseAddress, nSize): success = _ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, byref(lpNumberOfBytesRead)) if not success and GetLastError() != ERROR_PARTIAL_COPY: raise ctypes.WinError() - return str(lpBuffer.raw)[:lpNumberOfBytesRead.value] \ No newline at end of file + #return str(lpBuffer.raw)[:lpNumberOfBytesRead.value] + return lpBuffer.raw[:lpNumberOfBytesRead.value] + + +#--- Toolhelp library defines and structures ---------------------------------- + +TH32CS_SNAPHEAPLIST = 0x00000001 +TH32CS_SNAPPROCESS = 0x00000002 +TH32CS_SNAPTHREAD = 0x00000004 +TH32CS_SNAPMODULE = 0x00000008 +TH32CS_INHERIT = 0x80000000 +TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE) + +# typedef struct tagTHREADENTRY32 { +# DWORD dwSize; +# DWORD cntUsage; +# DWORD th32ThreadID; +# DWORD th32OwnerProcessID; +# LONG tpBasePri; +# LONG tpDeltaPri; +# DWORD dwFlags; +# } THREADENTRY32, *PTHREADENTRY32; +class THREADENTRY32(Structure): + _fields_ = [ + ('dwSize', DWORD), + ('cntUsage', DWORD), + ('th32ThreadID', DWORD), + ('th32OwnerProcessID', DWORD), + ('tpBasePri', LONG), + ('tpDeltaPri', LONG), + ('dwFlags', DWORD), + ] +LPTHREADENTRY32 = POINTER(THREADENTRY32) + +# typedef struct tagPROCESSENTRY32 { +# DWORD dwSize; +# DWORD cntUsage; +# DWORD th32ProcessID; +# ULONG_PTR th32DefaultHeapID; +# DWORD th32ModuleID; +# DWORD cntThreads; +# DWORD th32ParentProcessID; +# LONG pcPriClassBase; +# DWORD dwFlags; +# TCHAR szExeFile[MAX_PATH]; +# } PROCESSENTRY32, *PPROCESSENTRY32; +class PROCESSENTRY32(Structure): + _fields_ = [ + ('dwSize', DWORD), + ('cntUsage', DWORD), + ('th32ProcessID', DWORD), + ('th32DefaultHeapID', ULONG_PTR), + ('th32ModuleID', DWORD), + ('cntThreads', DWORD), + ('th32ParentProcessID', DWORD), + ('pcPriClassBase', LONG), + ('dwFlags', DWORD), + ('szExeFile', TCHAR * 260), + ] +LPPROCESSENTRY32 = POINTER(PROCESSENTRY32) + +# typedef struct tagPROCESSENTRY32W { +# DWORD dwSize; +# DWORD cntUsage; +# DWORD th32ProcessID; +# ULONG_PTR th32DefaultHeapID; +# DWORD th32ModuleID; +# DWORD cntThreads; +# DWORD th32ParentProcessID; +# LONG pcPriClassBase; +# DWORD dwFlags; +# WCHAR szExeFile[MAX_PATH]; +# } PROCESSENTRY32W, *PPROCESSENTRY32W; +class PROCESSENTRY32W(Structure): + _fields_ = [ + ('dwSize', DWORD), + ('cntUsage', DWORD), + ('th32ProcessID', DWORD), + ('th32DefaultHeapID', ULONG_PTR), + ('th32ModuleID', DWORD), + ('cntThreads', DWORD), + ('th32ParentProcessID', DWORD), + ('pcPriClassBase', LONG), + ('dwFlags', DWORD), + ('szExeFile', WCHAR * 260), + ] +LPPROCESSENTRY32W = POINTER(PROCESSENTRY32W) + + + +# typedef struct tagMODULEENTRY32 { +# DWORD dwSize; +# DWORD th32ModuleID; +# DWORD th32ProcessID; +# DWORD GlblcntUsage; +# DWORD ProccntUsage; +# BYTE* modBaseAddr; +# DWORD modBaseSize; +# HMODULE hModule; +# TCHAR szModule[MAX_MODULE_NAME32 + 1]; +# TCHAR szExePath[MAX_PATH]; +# } MODULEENTRY32, *PMODULEENTRY32; +class MODULEENTRY32(Structure): + _fields_ = [ + ("dwSize", DWORD), + ("th32ModuleID", DWORD), + ("th32ProcessID", DWORD), + ("GlblcntUsage", DWORD), + ("ProccntUsage", DWORD), + ("modBaseAddr", LPVOID), # BYTE* + ("modBaseSize", DWORD), + ("hModule", HMODULE), + ("szModule", TCHAR * (MAX_MODULE_NAME32 + 1)), + ("szExePath", TCHAR * MAX_PATH), + ] +LPMODULEENTRY32 = POINTER(MODULEENTRY32) + +# typedef struct tagHEAPENTRY32 { +# SIZE_T dwSize; +# HANDLE hHandle; +# ULONG_PTR dwAddress; +# SIZE_T dwBlockSize; +# DWORD dwFlags; +# DWORD dwLockCount; +# DWORD dwResvd; +# DWORD th32ProcessID; +# ULONG_PTR th32HeapID; +# } HEAPENTRY32, +# *PHEAPENTRY32; +class HEAPENTRY32(Structure): + _fields_ = [ + ("dwSize", SIZE_T), + ("hHandle", HANDLE), + ("dwAddress", ULONG_PTR), + ("dwBlockSize", SIZE_T), + ("dwFlags", DWORD), + ("dwLockCount", DWORD), + ("dwResvd", DWORD), + ("th32ProcessID", DWORD), + ("th32HeapID", ULONG_PTR), +] +LPHEAPENTRY32 = POINTER(HEAPENTRY32) + +# typedef struct tagHEAPLIST32 { +# SIZE_T dwSize; +# DWORD th32ProcessID; +# ULONG_PTR th32HeapID; +# DWORD dwFlags; +# } HEAPLIST32, +# *PHEAPLIST32; +class HEAPLIST32(Structure): + _fields_ = [ + ("dwSize", SIZE_T), + ("th32ProcessID", DWORD), + ("th32HeapID", ULONG_PTR), + ("dwFlags", DWORD), +] +LPHEAPLIST32 = POINTER(HEAPLIST32) + +# __in DWORD dwFlags, +# __in DWORD th32ProcessID +# ); +def CreateToolhelp32Snapshot(dwFlags = TH32CS_SNAPALL, th32ProcessID = 0): + _CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot + _CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD] + _CreateToolhelp32Snapshot.restype = HANDLE + + hSnapshot = _CreateToolhelp32Snapshot(dwFlags, th32ProcessID) + if hSnapshot == INVALID_HANDLE_VALUE: + raise ctypes.WinError() + return hSnapshot + +# BOOL WINAPI Heap32ListFirst( +# __in HANDLE hSnapshot, +# __inout LPHEAPLIST32 lphl +# ); +def Heap32ListFirst(hSnapshot): + _Heap32ListFirst = windll.kernel32.Heap32ListFirst + _Heap32ListFirst.argtypes = [HANDLE, LPHEAPLIST32] + _Heap32ListFirst.restype = bool + + hl = HEAPLIST32() + hl.dwSize = sizeof(HEAPLIST32) + success = _Heap32ListFirst(hSnapshot, byref(hl)) + if not success: + if GetLastError() == ERROR_NO_MORE_FILES: + return None + raise ctypes.WinError() + return hl + +# BOOL WINAPI Heap32ListNext( +# __in HANDLE hSnapshot, +# __out LPHEAPLIST32 lphl +# ); +def Heap32ListNext(hSnapshot, hl = None): + _Heap32ListNext = windll.kernel32.Heap32ListNext + _Heap32ListNext.argtypes = [HANDLE, LPHEAPLIST32] + _Heap32ListNext.restype = bool + + if hl is None: + hl = HEAPLIST32() + hl.dwSize = sizeof(HEAPLIST32) + success = _Heap32ListNext(hSnapshot, byref(hl)) + if not success: + if GetLastError() == ERROR_NO_MORE_FILES: + return None + raise ctypes.WinError() + return hl + + +# BOOL WINAPI Thread32First( +# __in HANDLE hSnapshot, +# __inout LPTHREADENTRY32 lpte +# ); +def Thread32First(hSnapshot): + _Thread32First = windll.kernel32.Thread32First + _Thread32First.argtypes = [HANDLE, LPTHREADENTRY32] + _Thread32First.restype = bool + + te = THREADENTRY32() + te.dwSize = sizeof(THREADENTRY32) + success = _Thread32First(hSnapshot, byref(te)) + if not success: + if GetLastError() == ERROR_NO_MORE_FILES: + return None + raise ctypes.WinError() + return te + +# BOOL WINAPI Thread32Next( +# __in HANDLE hSnapshot, +# __out LPTHREADENTRY32 lpte +# ); +def Thread32Next(hSnapshot, te = None): + _Thread32Next = windll.kernel32.Thread32Next + _Thread32Next.argtypes = [HANDLE, LPTHREADENTRY32] + _Thread32Next.restype = bool + + if te is None: + te = THREADENTRY32() + te.dwSize = sizeof(THREADENTRY32) + success = _Thread32Next(hSnapshot, byref(te)) + if not success: + if GetLastError() == ERROR_NO_MORE_FILES: + return None + raise ctypes.WinError() + return te \ No newline at end of file diff --git a/minidump/utils/winapi/version.py b/minidump/utils/winapi/version.py index 91b693b..d3e05fd 100644 --- a/minidump/utils/winapi/version.py +++ b/minidump/utils/winapi/version.py @@ -109,4 +109,5 @@ def GetFileVersionInfoW(lptstrFilename): dwLen = _GetFileVersionInfoSizeW(lptstrFilename, None) lpData = ctypes.create_string_buffer(dwLen) # not a string! _GetFileVersionInfoW(lptstrFilename, 0, dwLen, byref(lpData)) + return lpData \ No newline at end of file diff --git a/minidump/writer.py b/minidump/writer.py index 84bfae5..10bf23a 100644 --- a/minidump/writer.py +++ b/minidump/writer.py @@ -1,9 +1,10 @@ from minidump.constants import MINIDUMP_STREAM_TYPE, MINIDUMP_TYPE from minidump.header import MinidumpHeader from minidump.common_structs import MINIDUMP_LOCATION_DESCRIPTOR +from minidump.utils.privileges import enable_debug_privilege from minidump.utils.winapi.version import GetSystemInfo, GetVersionExW -from minidump.utils.winapi.kernel32 import OpenProcess, PROCESS_ALL_ACCESS, VirtualQueryEx, ReadProcessMemory +from minidump.utils.winapi.kernel32 import OpenProcess, PROCESS_ALL_ACCESS, VirtualQueryEx, ReadProcessMemory, CreateToolhelp32Snapshot, Thread32First, Thread32Next from minidump.utils.winapi.psapi import EnumProcessModules, GetModuleInformation, GetModuleFileNameExW from minidump.utils.winapi.version import GetFileVersionInfoW from minidump.streams import MINIDUMP_SYSTEM_INFO, PROCESSOR_ARCHITECTURE, MINIDUMP_MODULE_LIST, \ @@ -12,10 +13,15 @@ MINIDUMP_MEMORY64_LIST, MINIDUMP_MEMORY_DESCRIPTOR64 from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE, PRODUCT_TYPE +from minidump.streams.UnloadedModuleListStream import MINIDUMP_UNLOADED_MODULE_LIST +from minidump.streams.HandleDataStream import MINIDUMP_HANDLE_DATA_STREAM +from minidump.streams.ThreadInfoListStream import MINIDUMP_THREAD_INFO_LIST +from minidump.streams.ThreadListStream import MINIDUMP_THREAD_LIST from minidump.directory import MINIDUMP_DIRECTORY import io + class MinidumpSystemReader: def __init__(self): @@ -47,23 +53,41 @@ def __init__(self, pid): MinidumpSystemReader.__init__(self) self.pid = pid self.process_handle = None + self.process_toolhelp_handle = None self.sysinfo = None self.meminfolist = None - self.streamtypes = [MINIDUMP_STREAM_TYPE.SystemInfoStream, MINIDUMP_STREAM_TYPE.ModuleListStream, MINIDUMP_STREAM_TYPE.MemoryInfoListStream, MINIDUMP_STREAM_TYPE.Memory64ListStream] + self.streamtypes = [ + MINIDUMP_STREAM_TYPE.SystemInfoStream, + MINIDUMP_STREAM_TYPE.ModuleListStream, + MINIDUMP_STREAM_TYPE.MemoryInfoListStream, + MINIDUMP_STREAM_TYPE.Memory64ListStream, + MINIDUMP_STREAM_TYPE.UnloadedModuleListStream, + MINIDUMP_STREAM_TYPE.HandleDataStream, + MINIDUMP_STREAM_TYPE.ThreadInfoListStream, + #MINIDUMP_STREAM_TYPE.SystemMemoryInfoStream, + MINIDUMP_STREAM_TYPE.ThreadListStream, + + ] self.setup() def get_available_directories(self): + sts = [] for st in self.streamtypes: memdir = MINIDUMP_DIRECTORY() - memdir.Location = 0 #location is not needed, only the type + memdir.Location = None #location is not needed, only the type memdir.StreamType = st - yield memdir + sts.append(memdir) + return sts def open_process(self): self.process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, self.pid) + def open_toolhelp(self): + self.process_toolhelp_handle = CreateToolhelp32Snapshot(th32ProcessID = self.pid) + def setup(self): self.open_process() + self.open_toolhelp() def get_sysinfo(self, databuffer): #https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo @@ -86,7 +110,7 @@ def get_sysinfo(self, databuffer): sysinfo.SuiteMask = version_raw.wSuiteMask #sysinfo.Reserved2 = None - sysinfo.CSDVersion = version_raw.szCSDVersion + sysinfo.CSDVersion = None #version_raw.szCSDVersion #below todo, keeping all zeroes for now.. if sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL: @@ -98,9 +122,26 @@ def get_sysinfo(self, databuffer): sysinfo.ProcessorFeatures = [0,0] self.sysinfo_raw = sysinfo_raw #other functions will be using data from sysinfo so we need to store it! + #p1 = databuffer.tell() sysinfo.to_buffer(databuffer) - + #input('Sysinfo size: %s' % (databuffer.tell() - p1)) + + def get_threadinfo(self, databuffer): + ti = MINIDUMP_THREAD_INFO_LIST() + ti.NumberOfEntries = 0 + ti.to_buffer(databuffer) + + def get_handle_data(self, databuffer): + hds = MINIDUMP_HANDLE_DATA_STREAM() + hds.NumberOfDescriptors = 0 + hds.to_buffer(databuffer) + def get_unloaded_modules(self, databuffer): + #TODO: implement this. problem: finding an api call that differentiates between loaded and unloaded modules + # currently returning empty list + umodlist = MINIDUMP_UNLOADED_MODULE_LIST() + umodlist.NumberOfEntries = 0 + umodlist.to_buffer(databuffer) def get_modules(self, databuffer): #https://docs.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process @@ -108,20 +149,27 @@ def get_modules(self, databuffer): # module_list = MINIDUMP_MODULE_LIST() for module in EnumProcessModules(self.process_handle): - print(module) + #print(module) modinfo = GetModuleInformation(self.process_handle,module) modname = GetModuleFileNameExW(self.process_handle,module) - fileversion_raw = GetFileVersionInfoW(modname) - fileversion = VS_FIXEDFILEINFO.from_bytes(fileversion_raw) - print(modname) + #print(modname) + try: + fileversion_raw = GetFileVersionInfoW(modname) + fileversion = VS_FIXEDFILEINFO.from_bytes(fileversion_raw.raw[8+(16*2):]) + ts = fileversion.dwFileDateMS << 32 + fileversion.dwFileDateLS + except Exception as e: + print("Failed to get fileversion! Reason: %s " % e) + fileversion = None + ts = 0 + mmod = MINIDUMP_MODULE() mmod.BaseOfImage = modinfo.lpBaseOfDll mmod.SizeOfImage = modinfo.SizeOfImage - mmod.TimeDateStamp = fileversion.dwFileDateMS << 32 + fileversion.dwFileDateLS + mmod.TimeDateStamp = ts mmod.ModuleNameRva = None mmod.VersionInfo = fileversion - mmod.CvRecord = 0 # TODO? - mmod.MiscRecord = 0 # TODO? + mmod.CvRecord = None # TODO? + mmod.MiscRecord = None # TODO? mmod.ModuleName = modname @@ -161,12 +209,28 @@ def get_sections(self, databuffer): meminfolist.to_buffer(databuffer) def get_threads(self, databuffer): - pass + tl = MINIDUMP_THREAD_LIST() + tl.to_buffer(databuffer) + + #te = Thread32First(self.process_toolhelp_handle) + #while te is not None: + # dwThreadId = te.th32ThreadID + # te = Thread32Next(process_toolhelp_handle) +# +# + # ('dwSize', DWORD), + #('cntUsage', DWORD), + #('th32ThreadID', DWORD), + #('th32OwnerProcessID', DWORD), + #('tpBasePri', LONG), + #('tpDeltaPri', LONG), + #('dwFlags', DWORD), def get_exceptions(self, databuffer): pass def get_memory(self, buffer): + sp = buffer.tell() read_flags = [AllocationProtect.PAGE_EXECUTE_READ, AllocationProtect.PAGE_EXECUTE_READWRITE, AllocationProtect.PAGE_READONLY, @@ -175,17 +239,33 @@ def get_memory(self, buffer): AllocationProtect.PAGE_WRITECOPY ] memlist = MINIDUMP_MEMORY64_LIST() + memlist.BaseRva = 0 # TODO: check if this is correct! for section in self.meminfolist.entries: if section.Protect in read_flags: memdesc = MINIDUMP_MEMORY_DESCRIPTOR64() memdesc.StartOfMemoryRange = section.BaseAddress memdesc.DataSize = section.RegionSize - print(section.Protect) - data = ReadProcessMemory(self.process_handle, section.BaseAddress, section.RegionSize) - input(data) + #print(section.Protect) + #data = ReadProcessMemory(self.process_handle, section.BaseAddress, section.RegionSize) + #input(data) memlist.MemoryRanges.append(memdesc) - return memlist + memlist_rva_placeholder_loc = buffer.tell() + 8 + memlist.to_buffer(buffer) + memlist_rva = buffer.tell() + + buffer.seek(memlist_rva_placeholder_loc,0) + buffer.write(memlist_rva.to_bytes(8, byteorder = 'little', signed = False)) + buffer.seek(memlist_rva, 0) + + + for section in self.meminfolist.entries: + if section.Protect in read_flags: + data = ReadProcessMemory(self.process_handle, section.BaseAddress, section.RegionSize) + if section.RegionSize > len(data): + data += b'\x00' * (section.RegionSize + len(data)) + buffer.write(data) + return buffer.tell() - sp #giving back the total size # # #class MinidumpWriter: @@ -315,10 +395,13 @@ def get_memory(self, buffer): # # if __name__ == '__main__': + import sys from minidump.minidumpfile import MinidumpFile - pid = 9600 + pid = int(sys.argv[1]) + print(pid) + enable_debug_privilege() sysreader = LiveSystemReader(pid) - with open('test.dmp', 'wb') as f: + with open('test_new.dmp', 'wb') as f: mf = MinidumpFile() mf.writer = sysreader mf.to_buffer(f) From 06237817b46f303ab2a4eda04d30f2dcc61720d8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Mar 2022 15:55:18 +0200 Subject: [PATCH 3/7] first comm --- minidump/common_structs.py | 6 +- minidump/header.py | 7 +- minidump/minidumpfile.py | 3 +- minidump/streams/MemoryListStream.py | 11 +- minidump/streams/SystemInfoStream.py | 6 +- minidump/streams/ThreadListStream.py | 9 +- minidump/writer.py | 152 +++------------------------ 7 files changed, 39 insertions(+), 155 deletions(-) diff --git a/minidump/common_structs.py b/minidump/common_structs.py index b677b81..8902eca 100644 --- a/minidump/common_structs.py +++ b/minidump/common_structs.py @@ -1,7 +1,7 @@ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680383(v=vs.85).aspx class MINIDUMP_LOCATION_DESCRIPTOR: - def __init__(self, DataSize = None, Rva = None): + def __init__(self, DataSize = 0, Rva = 0): self.DataSize = DataSize self.Rva = Rva @@ -26,8 +26,8 @@ def __str__(self): class MINIDUMP_LOCATION_DESCRIPTOR64: def __init__(self): - self.DataSize = None - self.Rva = None + self.DataSize = 0 + self.Rva = 0 def get_size(self): return 16 diff --git a/minidump/header.py b/minidump/header.py index 230da78..12a70db 100644 --- a/minidump/header.py +++ b/minidump/header.py @@ -21,9 +21,8 @@ def to_bytes(self): t += self.NumberOfStreams.to_bytes(4, byteorder = 'little', signed = False) t += self.StreamDirectoryRva.to_bytes(4, byteorder = 'little', signed = False) t += self.CheckSum.to_bytes(4, byteorder = 'little', signed = False) - t += self.Reserved.to_bytes(4, byteorder = 'little', signed = False) t += self.TimeDateStamp.to_bytes(4, byteorder = 'little', signed = False) - t += self.Flags.value.to_bytes(4, byteorder = 'little', signed = False) + t += self.Flags.value.to_bytes(8, byteorder = 'little', signed = False) return t @@ -38,10 +37,9 @@ def parse(buff): mh.NumberOfStreams = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mh.StreamDirectoryRva = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mh.CheckSum = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) - mh.Reserved = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) mh.TimeDateStamp = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) try: - mh.Flags = MINIDUMP_TYPE(int.from_bytes(buff.read(4), byteorder = 'little', signed = False)) + mh.Flags = MINIDUMP_TYPE(int.from_bytes(buff.read(8), byteorder = 'little', signed = False)) except Exception as e: raise MinidumpHeaderFlagsException('Could not parse header flags!') @@ -55,7 +53,6 @@ def __str__(self): t+= 'NumberOfStreams: %s\n' % self.NumberOfStreams t+= 'StreamDirectoryRva: %s\n' % self.StreamDirectoryRva t+= 'CheckSum: %s\n' % self.CheckSum - t+= 'Reserved: %s\n' % self.Reserved t+= 'TimeDateStamp: %s\n' % self.TimeDateStamp t+= 'Flags: %s\n' % self.Flags return t \ No newline at end of file diff --git a/minidump/minidumpfile.py b/minidump/minidumpfile.py index 715e0ed..1752be3 100644 --- a/minidump/minidumpfile.py +++ b/minidump/minidumpfile.py @@ -55,7 +55,7 @@ def to_buffer(self, buffer): self.header.Version = 42899 self.header.ImplementationVersion = 41146 self.header.NumberOfStreams = len(self.writer.get_available_directories()) - self.header.Flags = MINIDUMP_TYPE.MiniDumpNormal + self.header.Flags = 2 self.header.StreamDirectoryRva = buffer.tell() + 32 hdr_bytes = self.header.to_bytes() @@ -65,7 +65,6 @@ def to_buffer(self, buffer): databuffer.write(b'\x00' * 12) databuff_trim = databuffer.tell() - input(hex(databuff_trim)) offset = databuff_trim for directory in self.writer.get_available_directories(): directory.Location = MINIDUMP_LOCATION_DESCRIPTOR() diff --git a/minidump/streams/MemoryListStream.py b/minidump/streams/MemoryListStream.py index deec7dc..1c87ccd 100644 --- a/minidump/streams/MemoryListStream.py +++ b/minidump/streams/MemoryListStream.py @@ -39,13 +39,16 @@ class MINIDUMP_MEMORY_DESCRIPTOR: def __init__(self): self.StartOfMemoryRange = None self.MemoryLocation = None - + self.StartOfMemoryRange = 0 + self.MemoryLocation = MINIDUMP_LOCATION_DESCRIPTOR() + #we do not use MemoryLocation but immediately store its fields in this object for easy access - self.DataSize = None - self.Rva = None + self.DataSize = 0 + self.Rva = 0 def to_bytes(self): - t = self.StartOfMemoryRange.to_bytes(4, byteorder = 'little', signed = False) + t = self.StartOfMemoryRange.to_bytes(8, byteorder = 'little', signed = False) + self.MemoryLocation = MINIDUMP_LOCATION_DESCRIPTOR(self.DataSize, self.Rva) t += self.MemoryLocation.to_bytes() return t diff --git a/minidump/streams/SystemInfoStream.py b/minidump/streams/SystemInfoStream.py index 097492a..953a824 100644 --- a/minidump/streams/SystemInfoStream.py +++ b/minidump/streams/SystemInfoStream.py @@ -112,9 +112,9 @@ def to_buffer(self, buffer): if self.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL: for vid in self.VendorId: buffer.write(vid.to_bytes(4, byteorder = 'little', signed = False)) - buffer.write(self.VersionInformation.value.to_bytes(4, byteorder = 'little', signed = False)) - buffer.write(self.FeatureInformation.value.to_bytes(4, byteorder = 'little', signed = False)) - buffer.write(self.AMDExtendedCpuFeatures.value.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.VersionInformation.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.FeatureInformation.to_bytes(4, byteorder = 'little', signed = False)) + buffer.write(self.AMDExtendedCpuFeatures.to_bytes(4, byteorder = 'little', signed = False)) else: for pf in self.ProcessorFeatures: buffer.write(pf.to_bytes(8, byteorder = 'little', signed = False)) diff --git a/minidump/streams/ThreadListStream.py b/minidump/streams/ThreadListStream.py index 8b064f1..27ae019 100644 --- a/minidump/streams/ThreadListStream.py +++ b/minidump/streams/ThreadListStream.py @@ -34,11 +34,11 @@ def __init__(self): self.PriorityClass = None self.Priority = None self.Teb = None - self.Stack = None - self.ThreadContext = None + self.Stack = MINIDUMP_MEMORY_DESCRIPTOR() + self.ThreadContext = MINIDUMP_LOCATION_DESCRIPTOR() def to_bytes(self): - t = self.ThreadId.value.to_bytes(4, byteorder = 'little', signed = False) + t = self.ThreadId.to_bytes(4, byteorder = 'little', signed = False) t += self.SuspendCount.to_bytes(4, byteorder = 'little', signed = False) t += self.PriorityClass.to_bytes(4, byteorder = 'little', signed = False) t += self.Priority.to_bytes(4, byteorder = 'little', signed = False) @@ -47,6 +47,9 @@ def to_bytes(self): t += self.ThreadContext.to_bytes() return t + def to_buffer(self, buffer): + buffer.write(self.to_bytes()) + @staticmethod def parse(buff): mt = MINIDUMP_THREAD() diff --git a/minidump/writer.py b/minidump/writer.py index 10bf23a..0eadd88 100644 --- a/minidump/writer.py +++ b/minidump/writer.py @@ -10,13 +10,13 @@ from minidump.streams import MINIDUMP_SYSTEM_INFO, PROCESSOR_ARCHITECTURE, MINIDUMP_MODULE_LIST, \ MINIDUMP_MODULE, VS_FIXEDFILEINFO, MINIDUMP_MEMORY_INFO_LIST, MINIDUMP_MEMORY_INFO, \ AllocationProtect, MemoryType, MemoryState, \ - MINIDUMP_MEMORY64_LIST, MINIDUMP_MEMORY_DESCRIPTOR64 + MINIDUMP_MEMORY64_LIST, MINIDUMP_MEMORY_DESCRIPTOR64, MINIDUMP_MEMORY_DESCRIPTOR from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE, PRODUCT_TYPE from minidump.streams.UnloadedModuleListStream import MINIDUMP_UNLOADED_MODULE_LIST from minidump.streams.HandleDataStream import MINIDUMP_HANDLE_DATA_STREAM from minidump.streams.ThreadInfoListStream import MINIDUMP_THREAD_INFO_LIST -from minidump.streams.ThreadListStream import MINIDUMP_THREAD_LIST +from minidump.streams.ThreadListStream import MINIDUMP_THREAD_LIST, MINIDUMP_THREAD from minidump.directory import MINIDUMP_DIRECTORY @@ -210,8 +210,18 @@ def get_sections(self, databuffer): def get_threads(self, databuffer): tl = MINIDUMP_THREAD_LIST() - tl.to_buffer(databuffer) + for thread_number in range(2): + mt = MINIDUMP_THREAD() + mt.ThreadId = 4 * thread_number + mt.SuspendCount = 0 + mt.PriorityClass = 0 + mt.Priority = 40 + mt.Teb = 0 + + tl.Threads.append(mt) + tl.to_buffer(databuffer) + #te = Thread32First(self.process_toolhelp_handle) #while te is not None: # dwThreadId = te.th32ThreadID @@ -245,9 +255,6 @@ def get_memory(self, buffer): memdesc = MINIDUMP_MEMORY_DESCRIPTOR64() memdesc.StartOfMemoryRange = section.BaseAddress memdesc.DataSize = section.RegionSize - #print(section.Protect) - #data = ReadProcessMemory(self.process_handle, section.BaseAddress, section.RegionSize) - #input(data) memlist.MemoryRanges.append(memdesc) memlist_rva_placeholder_loc = buffer.tell() + 8 @@ -265,135 +272,10 @@ def get_memory(self, buffer): if section.RegionSize > len(data): data += b'\x00' * (section.RegionSize + len(data)) buffer.write(data) - return buffer.tell() - sp #giving back the total size -# -# -#class MinidumpWriter: -# def __init__(self,sysreader): -# self.sysreader = sysreader -# self.output_file = None -# -# self.streams = {} #stream type -> list of stream objects -# -# self.header = None -# self.directory_list = [] -# self.directory_rva = 28 -# self.header_size = None -# -# self.header_buffer = io.BytesIO() -# self.data_buffer = io.BytesIO() -# -# def prepare_header(self): -# self.header = MinidumpHeader() -# self.header.Version = 1 -# self.header.ImplementationVersion = 1 -# self.header.NumberOfStreams = len(self.streams) +1 # +1 is fot he memory info stream -# self.header.StreamDirectoryRva = self.directory_rva -# #self.header.CheckSum = None -# #self.header.Reserved = None -# #self.header.TimeDateStamp = None -# self.header.Flags = MINIDUMP_TYPE.MiniDumpWithFullMemory -# self.header_buffer.write(self.header.to_bytes()) -# -# def prepare_directory(self): -# curr_pos = self.header_size -# for streamtype in self.streams: -# self.streams[streamtype].to_bytes(self.data_buffer) -# directory = MINIDUMP_DIRECTORY() -# directory.StreamType = streamtype -# directory.Location = curr_pos -# self.header_buffer.write(directory.to_bytes()) -# -# def finalize_header(self): -# # currently only using the 32 bit MINIDUMP_LOCATION_DESCRIPTOR, this is because we expect that the header -# # and any data in the header (including all streams data except memory stream) will not be bigger than 4GB -# # memory stream is a special case, as it cvan be longer than 4GB but the RVA to the beginning of the memory stream -# # is not expected to be bigger than 4G max. -# # if this becomes the case then this all will fail :) -# header_size = 28 -# header_size += len(self.streams) * 8 #this is for the dictionary itself, not the streams -# for stream in self.streams: -# header_size += self.streams[stream].get_size() -# -# header_size += 10 * 1024 #allocating 10k for the memory info -# -# self.prepare_header() -# self.prepare_directory() -# -# -# -# -# -# def create_streams(self): -# sysinfo = self.sysreader.get_sysinfo() -# self.streams[MINIDUMP_STREAM_TYPE.SystemInfoStream] = sysinfo -# -# print(str(sysinfo)) -# moduleinfo = self.sysreader.get_modules() -# self.streams[MINIDUMP_STREAM_TYPE.ModuleListStream] = moduleinfo -# -# sections = self.sysreader.get_sections() -# self.streams[MINIDUMP_STREAM_TYPE.MemoryInfoListStream] = sections -# -# self.finalize_header() -# -# memory = self.sysreader.get_memory() -# -# -# #def get_total_streams_cnt(self): -# # total = 0 -# # for t in self.streams: -# # total += len(t) -# # return total -# -# -# -# -# -# #def construct_directory(self): -# # -# # total_streams = self.get_total_streams_cnt() -# # -# # for stype in self.streams: -# # for stream in self.streams[stype]: -# # -# # stream -# # -# # loc = MINIDUMP_LOCATION_DESCRIPTOR() -# # loc.DataSize = 0 -# # loc.Rva = 0 -# # directory = MINIDUMP_DIRECTORY() -# # directory.StreamType = stream -# # self.directory.append() -# -# -# def write_header(self): -# hdr_pos = self.hdr_buff.tell() -# self.hdr_buff.seek(0,0) -# self.hdr_buff.write(self.construct_header()) -# self.hdr_buff.seek(hdr_pos, 0) -# return -# -# -# def construct_directory(self): -# self.sysreader.get_sysinfo(self.hdr_buff, self.data_buff) -# self.stream_cnt += 1 -# #modules -# #self.sysreader.get_modules(self.hdr_buff, self.data_buff) -# #self.stream_cnt += 1 -# -# #write header -# self.write_header() -# -# -# #append datastream for memory, with correct rva -# -# #dump memory -# -# def run(self): -# self.create_streams() -# -# + + return (len(memlist.MemoryRanges) + 1) * 0x10 + + if __name__ == '__main__': import sys from minidump.minidumpfile import MinidumpFile From 64680e446c3e5b639cdb7c9d2e5a192e0a9bb34e Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Mar 2022 16:24:24 +0200 Subject: [PATCH 4/7] initial support for crash dump writing --- minidump/common_structs.py | 4 ++-- minidump/streams/SystemInfoStream.py | 4 ---- minidump/writer.py | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/minidump/common_structs.py b/minidump/common_structs.py index 2a295fd..fc5e840 100644 --- a/minidump/common_structs.py +++ b/minidump/common_structs.py @@ -63,8 +63,8 @@ def __init__(self, data = None, encoding = 'utf-16-le'): self.Buffer = None self.encoding = encoding if data is not None: - self.Buffer = data.encode(self.encoding) - self.Length = len(self.Buffer) + self.Buffer = data.encode(self.encoding) + b"\x00\x00" + self.Length = len(self.Buffer) + 2 def to_bytes(self): diff --git a/minidump/streams/SystemInfoStream.py b/minidump/streams/SystemInfoStream.py index ac67f83..7f8228f 100644 --- a/minidump/streams/SystemInfoStream.py +++ b/minidump/streams/SystemInfoStream.py @@ -120,10 +120,6 @@ def to_buffer(self, buffer): for pf in self.ProcessorFeatures: t += pf.to_bytes(8, byteorder = 'little', signed = False) - if data_buffer is None: - return t - else: - data_buffer.write(t) @staticmethod def parse(buff): diff --git a/minidump/writer.py b/minidump/writer.py index 0eadd88..88a0fcc 100644 --- a/minidump/writer.py +++ b/minidump/writer.py @@ -212,7 +212,7 @@ def get_threads(self, databuffer): tl = MINIDUMP_THREAD_LIST() for thread_number in range(2): mt = MINIDUMP_THREAD() - mt.ThreadId = 4 * thread_number + mt.ThreadId = 4 * (thread_number + 1) mt.SuspendCount = 0 mt.PriorityClass = 0 mt.Priority = 40 From f283b7069b05139aee899125af598ff8c1024300 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Mar 2022 16:36:24 +0200 Subject: [PATCH 5/7] minor changes --- minidump/writer.py | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/minidump/writer.py b/minidump/writer.py index 88a0fcc..da02c90 100644 --- a/minidump/writer.py +++ b/minidump/writer.py @@ -56,18 +56,19 @@ def __init__(self, pid): self.process_toolhelp_handle = None self.sysinfo = None self.meminfolist = None + + # TODO: implement more streams. The currently implemented streams are the 'bare minimum' for windbg self.streamtypes = [ MINIDUMP_STREAM_TYPE.SystemInfoStream, MINIDUMP_STREAM_TYPE.ModuleListStream, + MINIDUMP_STREAM_TYPE.ThreadListStream, + # MINIDUMP_STREAM_TYPE.ThreadInfoListStream, + # MINIDUMP_STREAM_TYPE.UnloadedModuleListStream, + # MINIDUMP_STREAM_TYPE.HandleDataStream, MINIDUMP_STREAM_TYPE.MemoryInfoListStream, MINIDUMP_STREAM_TYPE.Memory64ListStream, - MINIDUMP_STREAM_TYPE.UnloadedModuleListStream, - MINIDUMP_STREAM_TYPE.HandleDataStream, - MINIDUMP_STREAM_TYPE.ThreadInfoListStream, - #MINIDUMP_STREAM_TYPE.SystemMemoryInfoStream, - MINIDUMP_STREAM_TYPE.ThreadListStream, - ] + self.setup() def get_available_directories(self): @@ -216,31 +217,17 @@ def get_threads(self, databuffer): mt.SuspendCount = 0 mt.PriorityClass = 0 mt.Priority = 40 + # TODO: get thread teb + # TODO: get thread context so windbg's stack trace works mt.Teb = 0 - tl.Threads.append(mt) tl.to_buffer(databuffer) - - #te = Thread32First(self.process_toolhelp_handle) - #while te is not None: - # dwThreadId = te.th32ThreadID - # te = Thread32Next(process_toolhelp_handle) -# -# - # ('dwSize', DWORD), - #('cntUsage', DWORD), - #('th32ThreadID', DWORD), - #('th32OwnerProcessID', DWORD), - #('tpBasePri', LONG), - #('tpDeltaPri', LONG), - #('dwFlags', DWORD), def get_exceptions(self, databuffer): pass def get_memory(self, buffer): - sp = buffer.tell() read_flags = [AllocationProtect.PAGE_EXECUTE_READ, AllocationProtect.PAGE_EXECUTE_READWRITE, AllocationProtect.PAGE_READONLY, @@ -261,7 +248,7 @@ def get_memory(self, buffer): memlist.to_buffer(buffer) memlist_rva = buffer.tell() - buffer.seek(memlist_rva_placeholder_loc,0) + buffer.seek(memlist_rva_placeholder_loc, 0) buffer.write(memlist_rva.to_bytes(8, byteorder = 'little', signed = False)) buffer.seek(memlist_rva, 0) From 50709f5d99f9fb59bf59ca543ec11003a50fc7bf Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Mar 2022 16:50:14 +0200 Subject: [PATCH 6/7] change dump type flags --- minidump/minidumpfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minidump/minidumpfile.py b/minidump/minidumpfile.py index 3e5bfa5..037575a 100644 --- a/minidump/minidumpfile.py +++ b/minidump/minidumpfile.py @@ -55,7 +55,7 @@ def to_buffer(self, buffer): self.header.Version = 42899 self.header.ImplementationVersion = 41146 self.header.NumberOfStreams = len(self.writer.get_available_directories()) - self.header.Flags = 2 + self.header.Flags = MINIDUMP_TYPE.MiniDumpWithFullMemory | MINIDUMP_TYPE.MiniDumpIgnoreInaccessibleMemory | MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo self.header.StreamDirectoryRva = buffer.tell() + 32 hdr_bytes = self.header.to_bytes() From fa57078e465f2adde3651fa00a5a00f8d9f01717 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Mar 2022 16:52:05 +0200 Subject: [PATCH 7/7] I forgor --- minidump/minidumpfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minidump/minidumpfile.py b/minidump/minidumpfile.py index 037575a..243d5be 100644 --- a/minidump/minidumpfile.py +++ b/minidump/minidumpfile.py @@ -14,8 +14,8 @@ from minidump.minidumpreader import MinidumpFileReader from minidump.streams import * from minidump.common_structs import * -from minidump.constants import MINIDUMP_STREAM_TYPE -from minidump.directory import MINIDUMP_DIRECTORY +from minidump.constants import MINIDUMP_STREAM_TYPE, MINIDUMP_TYPE +from minidump.directory import MINIDUMP_DIRECTORY, DirectoryBuffer from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE class MinidumpFile: