You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
psrGROUP/env/Lib/site-packages/haystack/memory_dumper.py

160 lines
5.2 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Dumps a process memory _memory_handler to a haystack dump format."""
import logging
import argparse
import shutil
import sys
import tempfile
import os
from haystack import dbg
from haystack.mappings.process import make_process_memory_handler
__author__ = "Loic Jaquemet"
__copyright__ = "Copyright (C) 2012 Loic Jaquemet"
__email__ = "loic.jaquemet+python@gmail.com"
__license__ = "GPL"
__maintainer__ = "Loic Jaquemet"
__status__ = "Production"
log = logging.getLogger('dumper')
class MemoryDumper:
"""
Dumps a process memory maps to a tgz
"""
ARCHIVE_TYPES = ["dir", "tar", "gztar"]
def __init__(self, pid, dest):
self._pid = pid
self._dest = os.path.normpath(dest)
self.dbg = None
self._memory_handler = None
def make_mappings(self):
"""Connect the debugguer to the process and gets the memory mappings
metadata."""
self.dbg = dbg.get_debugger(self._pid)
self._memory_handler = make_process_memory_handler(self.dbg.get_process())
log.debug('Memory Mappings read. Dropping ptrace on pid.')
return
def dump(self, dest=None):
"""Dumps the source memory mapping to the target dump place."""
if dest is not None:
self._dest = os.path.normpath(dest)
if os.path.isfile(self._dest):
raise TypeError('target is a file. You asked for a directory dump. '
'Please delete the file.')
if not os.access(self._dest, os.X_OK | os.F_OK):
os.mkdir(self._dest)
self._dump_all_mappings(self._dest)
self._free_process()
return
def _dump_all_mappings_winapp(self, destdir):
# TODO TEST
# winappdbg
self.index = open(os.path.join(destdir, 'mappings'), 'w+')
# test dump only the heap
err = 0
memory_maps = self.dbg.get_process().generate_memory_snaphost()
for mbi in memory_maps:
# TODO
try:
self._dump_mapping(mbi, destdir)
except IOError as e:
err += 1
log.warning(e)
pass # no se how to read windows
log.debug('%d mapping in error, destdir: %s', err, destdir)
self.index.close()
return
def _dump_all_mappings(self, destdir):
"""Iterates on all _memory_handler and dumps them to file."""
self.index = open(os.path.join(destdir, 'mappings'), 'w+')
err = 0
for m in self._memory_handler:
try:
self._dump_mapping(m, destdir)
except IOError as e:
err += 1
log.warning(e)
pass
log.debug('%d mapping in error, destdir: %s', err, destdir)
self.index.close()
return
def _free_process(self):
"""continue() the process."""
self.dbg.quit()
return
def _dump_mapping(self, m, tmpdir):
"""Dump one mapping to one file in one tmpdir."""
my_utils = self._memory_handler.get_target_platform().get_target_ctypes_utils()
if m.permissions[0] != 'r':
log.debug('Ignore read protected mapping %s', m)
return
elif m.pathname in ['[vdso]', '[vsyscall]', '[vvar]']:
log.debug('Ignore system mapping %s', m)
return
# make filename
# We don't really care about the filename but we need to be coherent.
mname = b'%s-%s' % (my_utils.formatAddress(m.start), my_utils.formatAddress(m.end))
mmap_fname = os.path.join(tmpdir, mname)
# dumping the memorymap
log.debug('Dump %s', m)
with open(mmap_fname, 'wb') as mmap_fout:
try:
mmap_fout.write(m.mmap().get_byte_buffer())
except Exception as e:
raise IOError(e)
# dump all the metadata
start = my_utils.formatAddress(m.start)
end = my_utils.formatAddress(m.end)
perms = m.permissions
offset = '0x%0.8x' % m.offset
device = '%0.2x:%0.2x' % (m.major_device, m.minor_device)
inode = '%0.7d' % m.inode
text = ' '.join([start, end, perms, offset, device, inode, str(m.pathname)])
self.index.write('%s\n' % text)
return
def dump(pid, outfile):
"""Dumps a process memory to Haystack dump format."""
dumper = MemoryDumper(pid, outfile)
dumper.make_mappings()
dumper.dump()
log.info('Process %d memory dumped to folder %s', pid, outfile)
return outfile
def _dump(opt):
"""Dumps a process memory _memory_handler to Haystack dump format."""
return dump(opt.pid, opt.dumpname)
def argparser():
dump_parser = argparse.ArgumentParser(prog='memory_dumper',
description="dump a pid's memory to file.")
dump_parser.add_argument('pid', type=int, action='store',
help='Target PID.')
dump_parser.add_argument('dumpname', action='store', help='The dump name.')
dump_parser.set_defaults(func=_dump)
return dump_parser
def main():
logging.basicConfig(level=logging.DEBUG)
parser = argparser()
opts = parser.parse_args(sys.argv[1:])
opts.func(opts)