#!/usr/bin/python2
# -*- coding: UTF-8 -*-
__doc__       = """
This tool automates the research of some artifacts for forensics purpose in
 memory dumps based upon Volatility for a series of common Windows applications.

It can also open multiple archive formats (it uses pyunpack). In case of an
 archive, the tool will extract all its files to a temporary directory and then
 try to open each file as a memory dump.
"""
__examples__  = [
    "memory.dmp",
    "my-dumps.tar.gz",
    "dumps.zip -a none -s all",
    "dump.raw -a 1,2,4 -f",
    "dump.mem -a 0,3,10,11 -s 0",
    "dump.raw -v --profile Win7SP1x86",
]
__print__ = "Print documentation:\n- stdout: pydoc {0}\n- html  : pydoc -w {0}"


from appmemdumper import *
from appmemdumper.__info__ import *
from tinyscript import *


def at_exit():
    # clean the extracted files' folder
    logger.debug("Removing temporary files...")
    try:
        shutil.rmtree(args.temp_dir)
    except OSError:
        pass
    try:                 # this occurs when another exception happened before,
        memdump.clean()  #  letting 'memdump' set to None
    except AttributeError:
        pass


def at_interrupt():
    logger.warning("Interrupted by user")


def list_of(dumpers):
    """ Parses a comma-separated list of ints matching selected dumpers. """
    def _wrapper(value):
        if value.lower() in ["", "[]", "none"]:
            return []
        try:
            l = list(range(len(dumpers))) if value == "all" else pos_ints(value)
            return [dumpers[i] for i in l]
        except:
            raise ValueError("{} could not be parsed".format(value))
    return _wrapper


if __name__ == '__main__':
    global memdump
    parser.add_argument("dump", type=file_exists, help="memory dump file path")
    dumpers = parser.add_argument_group("application/system dumpers")
    appl = '\n'.join(two_columns(["  [{}] {}".format(i, a) for i, a in \
                                  enumerate(APPDUMPERS)]))
    sysl = '\n'.join(two_columns(["  [{}] {}".format(i, g) for i, g in \
                                  enumerate(SYSDUMPERS)]))
    dumpers.add_argument("-a", dest="apps", default="all",
                         type=list_of(APPDUMPERS),
                         help="comma-separated list of integers designating "
                              "applications to be parsed\n Currently supported:"
                              " \n{}\n".format(appl))
    dumpers.add_argument("-s", dest="syst", default="none",
                         type=list_of(SYSDUMPERS),
                         help="comma-separated list of integers designating "
                              "system items to be parsed\n Currently supported:"
                              " \n{}\n".format(sysl))
    vol = parser.add_argument_group("volatility options")
    vol.add_argument("-f", "--force", dest="force", action="store_true",
                     help="force profile search, do not use cached profile")
    vol.add_argument("-p", "--plugins-dir", dest="plugins", type=folder_exists,
                     help="path to custom plugins")
    vol.add_argument("--profile", help="force Volatility profile",
                     note="has the precedence on -f/--force")
    others = parser.add_argument_group("output options")
    others.add_argument("-d", "--dump-dir", dest="dump_dir", default="files",
                        help="dump directory")
    others.add_argument("-t", "--temp-dir", dest="temp_dir", default=".temp",
                        help="temporary directory for decompressed images")
    others.add_argument("-u", "--update", action="store_true",
                        help="update previous dump directories")
    initialize()
    if len(args.apps) == 0 and len(args.syst) == 0:
        logger.warning("No dumper selected")
        sys.exit(0)
    sys.argv = sys.argv[:1]  # remove arguments for avoiding passing them to the
                             #  Volatility API (e.g. -p will cause a clash with
                             #  the PID option of Volatility)
    for l in ['pyunpack', 'easyprocess', 'volatility.debug']:
        logging.getLogger(l).setLevel(51)
    # running the main stuff
    _, args.dump = decompress(args.dump, args.temp_dir)
    from_cache = not args.force
    for dump in args.dump:
        if not os.path.isfile(dump):
            continue
        dump_dir = os.path.join(args.dump_dir, os.path.basename(dump))
        try:
            memdump = VolatilityMemDump(dump, args.apps, args.syst, dump_dir,
                                        args.plugins, args.profile, from_cache)
            memdump.dump(args.update)
        except ValueError as e:
            logger.critical(e)
        # if archive, assume that every other dump has the same profile as
        #  the first one in order to spare time
        from_cache = True
