Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
skelsec committed May 23, 2018
1 parent 6af558a commit ed3e8f6
Show file tree
Hide file tree
Showing 31 changed files with 2,655 additions and 0 deletions.
68 changes: 68 additions & 0 deletions minidump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
#
# Author:
# Tamas Jos (@skelsec)
#

import logging
from minidump.minidumpfile import MinidumpFile

if __name__ == '__main__':
import argparse

parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz -currently only minidump-')
parser.add_argument('minidumpfile', help='path to the minidump file of lsass.exe')
parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('--modules', action='store_true', help='List modules')
parser.add_argument('--threads', action='store_true', help='List threads')
parser.add_argument('--memory', action='store_true', help='List memory')
parser.add_argument('--sysinfo', action='store_true', help='Show sysinfo')
parser.add_argument('--comments', action='store_true', help='Show comments')
parser.add_argument('--handles', action='store_true', help='List handles')
parser.add_argument('--misc', action='store_true', help='Show misc info')
parser.add_argument('--all', action='store_true', help='Show all info')

args = parser.parse_args()
if args.verbose == 0:
logging.basicConfig(level=logging.INFO)
elif args.verbose == 1:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=1)

mf = MinidumpFile.parse(args.minidumpfile)
reader = mf.get_reader()

if args.all or args.threads:
if mf.threads is not None:
print(str(mf.threads))
if mf.threads_ex is not None:
print(str(mf.threads_ex))
if mf.thread_info is not None:
print(str(mf.thread_info))
if args.all or args.modules:
if mf.modules is not None:
print(str(mf.modules))
if mf.unloaded_modules is not None:
print(str(mf.unloaded_modules))
if args.all or args.memory:
if mf.memory_segments is not None:
print(str(mf.memory_segments))
if mf.memory_segments_64 is not None:
print(str(mf.memory_segments_64))
if mf.memory_info is not None:
print(str(mf.memory_info))
if args.all or args.sysinfo:
if mf.sysinfo is not None:
print(str(mf.sysinfo))
if args.all or args.comments:
if mf.comment_a is not None:
print(str(mf.comment_a))
if mf.comment_w is not None:
print(str(mf.comment_w))
if args.all or args.handles:
if mf.handles is not None:
print(str(mf.handles))
if args.all or args.misc:
if mf.misc_info is not None:
print(str(mf.misc_info))
Empty file added minidump/__init__.py
Empty file.
170 changes: 170 additions & 0 deletions minidump/common_structs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@

# 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

@staticmethod
def parse(buff):
mld = MINIDUMP_LOCATION_DESCRIPTOR()
mld.DataSize = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
mld.Rva = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
return mld

def __str__(self):
t = 'Size: %s File offset: %s' % (self.DataSize, self.Rva)
return t

class MINIDUMP_LOCATION_DESCRIPTOR64:
def __init__(self):
self.DataSize = None
self.Rva = None

@staticmethod
def parse(buff):
mld = MINIDUMP_LOCATION_DESCRIPTOR64()
mld.DataSize = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
mld.Rva = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
return mld

def __str__(self):
t = 'Size: %s File offset: %s' % (self.DataSize, self.Rva)
return t

class MINIDUMP_STRING:
def __init__(self):
self.Length = None
self.Buffer = None

@staticmethod
def parse(buff):
ms = MINIDUMP_STRING()
ms.Length = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
ms.Buffer = buff.read(ms.Length)
return ms

@staticmethod
def get_from_rva(rva, buff):
pos = buff.tell()
buff.seek(rva, 0)
ms = MINIDUMP_STRING.parse(buff)
buff.seek(pos, 0)
return ms.Buffer.decode('utf-16-le')

class MinidumpMemorySegment:
def __init__(self):
self.start_virtual_address = None
self.size = None
self.end_virtual_address = None
self.start_file_address = None

def parse_mini(memory_decriptor, buff):
"""
memory_descriptor: MINIDUMP_MEMORY_DESCRIPTOR
buff: file_handle
"""
mms = MinidumpMemorySegment()
mms.start_virtual_address = memory_decriptor.StartOfMemoryRange
mms.size = memory_decriptor.Memory.DataSize
mms.start_file_address = memory_decriptor.Memory.Rva
mms.end_virtual_address = mms.start_virtual_address + mms.size
return mms

def parse_full(memory_decriptor, buff, rva):
mms = MinidumpMemorySegment()
mms.start_virtual_address = memory_decriptor.StartOfMemoryRange
mms.size = memory_decriptor.DataSize
mms.start_file_address = rva
mms.end_virtual_address = mms.start_virtual_address + mms.size
return mms


def inrange(self, virt_addr):
if virt_addr >= self.start_virtual_address and virt_addr < self.end_virtual_address:
return True
return False
def read(self, virtual_address, size, file_handler):
if virtual_address > self.end_virtual_address or virtual_address < self.start_virtual_address:
raise Exception('Reading from wrong segment!')

if virtual_address+size > self.end_virtual_address:
raise Exception('Read would cross boundaries!')

pos = file_handler.tell()
offset = virtual_address - self.start_virtual_address
file_handler.seek(self.start_file_address + offset, 0)
data = file_handler.read(size)
file_handler.seek(pos, 0)
return data

def search(self, pattern, file_handler):
if len(pattern) > self.size:
return []
pos = file_handler.tell()
file_handler.seek(self.start_file_address, 0)
data = file_handler.read(self.size)
file_handler.seek(pos, 0)
fl = []
offset = 0
while len(data) > len(pattern):
marker = data.find(pattern)
if marker == -1:
return fl
fl.append(marker + offset + self.start_virtual_address)
data = data[marker+1:]
offset = marker + 1

return fl

def __str__(self):
t = 'VA Start: %s, RVA: %s, Size: %s' % (hex(self.start_virtual_address), hex(self.start_file_address), hex(self.size))
return t



def hexdump( src, length=16, sep='.', start = 0):
'''
@brief Return {src} in hex dump.
@param[in] length {Int} Nb Bytes by row.
@param[in] sep {Char} For the text part, {sep} will be used for non ASCII char.
@return {Str} The hexdump
@note Full support for python2 and python3 !
'''
result = [];

# Python3 support
try:
xrange(0,1);
except NameError:
xrange = range;

for i in xrange(0, len(src), length):
subSrc = src[i:i+length];
hexa = '';
isMiddle = False;
for h in xrange(0,len(subSrc)):
if h == length/2:
hexa += ' ';
h = subSrc[h];
if not isinstance(h, int):
h = ord(h);
h = hex(h).replace('0x','');
if len(h) == 1:
h = '0'+h;
hexa += h+' ';
hexa = hexa.strip(' ');
text = '';
for c in subSrc:
if not isinstance(c, int):
c = ord(c);
if 0x20 <= c < 0x7F:
text += chr(c);
else:
text += sep;
if start == 0:
result.append(('%08X: %-'+str(length*(2+1)+1)+'s |%s|') % (i, hexa, text));
else:
result.append(('%08X+%08X: %-'+str(length*(2+1)+1)+'s |%s|') % (start, i, hexa, text));
return '\n'.join(result);
Loading

0 comments on commit ed3e8f6

Please sign in to comment.