Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for writing minidumps #30

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ venv.bak/

# mypy
.mypy_cache/
*.exe
*.DMP
*.dmp
63 changes: 37 additions & 26 deletions minidump/common_structs.py
Original file line number Diff line number Diff line change
@@ -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 = 0, Rva = 0):
self.DataSize = DataSize
self.Rva = Rva

def get_size(self):
return 8
Expand Down Expand Up @@ -35,8 +35,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
Expand All @@ -58,10 +58,20 @@ 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) + b"\x00\x00"
self.Length = len(self.Buffer) + 2


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()
Expand All @@ -83,6 +93,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')

@staticmethod
Expand Down Expand Up @@ -272,42 +283,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"""
Expand Down
92 changes: 91 additions & 1 deletion minidump/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@
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
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()
Expand Down Expand Up @@ -55,4 +63,86 @@ async def aparse(buff):

def __str__(self):
t = 'StreamType: %s %s' % (self.StreamType, self.Location)
return t
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()





9 changes: 3 additions & 6 deletions minidump/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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
Expand All @@ -22,9 +22,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

Expand All @@ -39,10 +38,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!')

Expand All @@ -62,7 +60,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
Loading