예제 #1
0
def new_process(params):
    '''
    Monitor process creation in order to start tracing the first process.
    '''
    from DeviareDbParser import DbConnector
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import mw_monitor_start_monitoring_process

    pid = params["pid"]
    pgd = params["pgd"]
    name = params["name"]

    main_proc = mwmon.data.procs[0]
    if main_proc.get_proc_name() is not None and (
            main_proc.get_proc_name() in name
            or name in main_proc.get_proc_name()):
        mwmon.printer("Starting monitoring process %s" % name)
        main_proc.set_pgd(pgd)
        main_proc.set_pid(pid)
        if mwmon.api_tracer and not mwmon.api_tracer_light_mode:
            mwmon.printer("Loading API tracer database...")
            # Initialize API doc database. We need to initialize it in this thread (callback),
            # because sqlite limits db access to 1 thread, and the rest of callbacks should be
            # running on this same thread.
            mwmon.db = DbConnector(mwmon.api_database_path)

        mw_monitor_start_monitoring_process(main_proc, insert_proc=False)

        mwmon.cm.rm_callback("vmi_new_proc")
예제 #2
0
def new_process(params):
    '''
    Monitor process creation in order to start tracing the first process.
    '''
    from DeviareDbParser import DbConnector
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import mw_monitor_start_monitoring_process

    pid = params["pid"]
    pgd = params["pgd"]
    name = params["name"]

    main_proc = mwmon.data.procs[0]
    if main_proc.get_proc_name() is not None and (main_proc.get_proc_name() in name or name in main_proc.get_proc_name()):
        mwmon.printer("Starting monitoring process %s" % name)
        main_proc.set_pgd(pgd)
        main_proc.set_pid(pid)
        if mwmon.api_tracer and not mwmon.api_tracer_light_mode:
            mwmon.printer("Loading API tracer database...")
            # Initialize API doc database. We need to initialize it in this thread (callback),
            # because sqlite limits db access to 1 thread, and the rest of callbacks should be
            # running on this same thread.
            mwmon.db = DbConnector(mwmon.api_database_path)

        mw_monitor_start_monitoring_process(main_proc, insert_proc=False)

        mwmon.cm.rm_callback("vmi_new_proc")
예제 #3
0
def apitracer_start_monitoring_process(proc):
    from mw_monitor_classes import mwmon
    from api import CallbackManager

    mwmon.printer("Initializing API tracer...")

    """
    # E8 - call, E9,EA,EB - jmp
    mwmon.cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(
        opcodes, db=mwmon.db, proc=proc), name="opcode1_%x" % (proc.pid), start_opcode=0xE8, end_opcode=0xEB)
    mwmon.cm.add_trigger("opcode1_%x" %
                         (proc.get_pid()), "triggers/trigger_opcode_user_only.so")
    mwmon.cm.set_trigger_var("opcode1_%x" %
                             (proc.get_pid()), "cr3", proc.get_pgd())
    """
    # FF - call and jmp
    mwmon.cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(
        opcodes, db=mwmon.db, proc=proc), name="opcode2_%x" % (proc.pid), start_opcode=0xFF, end_opcode=0xFF)
    mwmon.cm.add_trigger("opcode2_%x" %
                         (proc.get_pid()), "triggers/trigger_opcode_user_only.so")
    mwmon.cm.set_trigger_var("opcode2_%x" %
                             (proc.get_pid()), "cr3", proc.get_pgd())
    """
    # 9A - call
    mwmon.cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(
        opcodes, db=mwmon.db, proc=proc), name="opcode3_%x" % (proc.pid), start_opcode=0x9A, end_opcode=0x9A)
    mwmon.cm.add_trigger("opcode3_%x" %
                         (proc.get_pid()), "triggers/trigger_opcode_user_only.so")
    mwmon.cm.set_trigger_var("opcode3_%x" %
                             (proc.get_pid()), "cr3", proc.get_pgd())
    """
    """
예제 #4
0
def mw_monitor_start_monitoring_process(new_proc, insert_proc=True):
    '''
        This function sets up all the callbacks and structures
        necessary to monitor a process.

        :param new_proc: The process to start monitoring as a instance of Process
        :type new_proc: Process

        :param insert_proc: (Optional) Whether to insert the process in the list of
                            monitored processes (internal to this script).
        :type insert_proc: bool
    '''
    from coverage import block_executed
    from api_tracer import apitracer_start_monitoring_process
    from dumper import dumper_start_monitoring_process
    from api import CallbackManager
    import api

    # Insert the process, if necessary
    if insert_proc:
        mwmon.data.procs.append(new_proc)
    # Start monitoring the process
    api.start_monitoring_process(new_proc.get_pgd())
    mwmon.printer("Started monitoring process with PGD %x and name %s" %
                  (new_proc.get_pgd(), new_proc.get_proc_name()))

    # coverage module
    # Create a callback and trigger for each process
    if mwmon.coverage and (mwmon.coverage_procs is None
                           or new_proc.proc_name in mwmon.coverage_procs):
        mwmon.cm.add_callback(CallbackManager.BLOCK_BEGIN_CB,
                              functools.partial(block_executed, proc=new_proc),
                              name="block_begin_coverage_%d" %
                              new_proc.proc_num)
        mwmon.cm.add_trigger("block_begin_coverage_%d" % new_proc.proc_num,
                             "triggers/trigger_block_user_only_coverage.so")
        mwmon.cm.set_trigger_var(
            "block_begin_coverage_%d" % (new_proc.proc_num), "cr3",
            new_proc.get_pgd())
        mwmon.cm.set_trigger_var(
            "block_begin_coverage_%d" % (new_proc.proc_num), "proc_num",
            new_proc.proc_num)
        # Output file name, with pid
        mwmon.cm.set_trigger_var(
            "block_begin_coverage_%d" % (new_proc.proc_num), "log_name",
            mwmon.coverage_log_name + ".%x" % (new_proc.pid))

    # api tracer module
    if mwmon.api_tracer and (mwmon.api_tracer_procs is None
                             or new_proc.proc_name in mwmon.api_tracer_procs):
        apitracer_start_monitoring_process(new_proc)

    # dumper module
    if mwmon.dumper:
        dumper_start_monitoring_process(new_proc)

    mwmon.cm.add_callback(CallbackManager.CONTEXTCHANGE_CB,
                          functools.partial(context_change, new_proc,
                                            new_proc.get_proc_name()),
                          name="context_change_%x" % new_proc.get_pgd())
예제 #5
0
def dump_command(line):
    '''
    Dump process memory with its modules and rest of VADS. Specify process name,
    pid or cr3 (in hex).
    '''
    from mw_monitor_classes import mwmon
    from utils import find_procs

    if line == "":
        dump()
    else:
        param = line.split()[0].strip()
        found = find_procs(param)
        if len(found) == 0:
            mwmon.printer("Process %s not found" % param)
        elif len(found) == 1 or (len(found) == 2
                                 and found[0][1] == found[1][1]):
            if found[0][0] == 0:
                # kernel process
                pass
            else:
                pid, pgd, pname = found[0]
                dump(pid=pid)
        else:
            mwmon.printer("Please specify a valid (and unique) process")
예제 #6
0
def apitracer_start_monitoring_process(proc):
    from mw_monitor_classes import mwmon
    from api import CallbackManager

    mwmon.printer("Initializing API tracer...")
    """
    # E8 - call, E9,EA,EB - jmp
    mwmon.cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(
        opcodes, db=mwmon.db, proc=proc), name="opcode1_%x" % (proc.pid), start_opcode=0xE8, end_opcode=0xEB)
    mwmon.cm.add_trigger("opcode1_%x" %
                         (proc.get_pid()), "triggers/trigger_opcode_user_only.so")
    mwmon.cm.set_trigger_var("opcode1_%x" %
                             (proc.get_pid()), "cr3", proc.get_pgd())
    """
    # FF - call and jmp
    mwmon.cm.add_callback(CallbackManager.OPCODE_RANGE_CB,
                          functools.partial(opcodes, db=mwmon.db, proc=proc),
                          name="opcode2_%x" % (proc.pid),
                          start_opcode=0xFF,
                          end_opcode=0xFF)
    mwmon.cm.add_trigger("opcode2_%x" % (proc.get_pid()),
                         "triggers/trigger_opcode_user_only.so")
    mwmon.cm.set_trigger_var("opcode2_%x" % (proc.get_pid()), "cr3",
                             proc.get_pgd())
    """
    # 9A - call
    mwmon.cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(
        opcodes, db=mwmon.db, proc=proc), name="opcode3_%x" % (proc.pid), start_opcode=0x9A, end_opcode=0x9A)
    mwmon.cm.add_trigger("opcode3_%x" %
                         (proc.get_pid()), "triggers/trigger_opcode_user_only.so")
    mwmon.cm.set_trigger_var("opcode3_%x" %
                             (proc.get_pid()), "cr3", proc.get_pgd())
    """
    """
예제 #7
0
def interproc_basic_stats():
    from mw_monitor_classes import mwmon
    try:
        for proc in mwmon.data.procs:
            proc.print_stats(mwmon.interproc_basic_stats_name)
    except Exception:
        traceback.print_exc()
        mwmon.printer(traceback.print_stack())
예제 #8
0
def remove_process(pid, pgd, name):
    from mw_monitor_classes import mwmon
    from api import unload_module

    for proc in mwmon.data.procs:
        if proc.get_pid() == pid and proc.has_exited() is False:
            proc.set_exited()
            mwmon.printer("Process %s (%x) exited" % (name, pid))
예제 #9
0
def interproc_basic_stats():
    from mw_monitor_classes import mwmon
    try:
        for proc in mwmon.data.procs:
            proc.print_stats(mwmon.interproc_basic_stats_name)
    except Exception:
        traceback.print_exc()
        mwmon.printer(traceback.print_stack())
예제 #10
0
def serialize_interproc():
    from mw_monitor_classes import mwmon
    try:
        f_out = open(mwmon.interproc_bin_log_name, "w")
        pickle.dump(mwmon.data, f_out)
        f_out.close()
    except Exception:
        traceback.print_exc()
        mwmon.printer(traceback.print_stack())
예제 #11
0
def serialize_interproc():
    from mw_monitor_classes import mwmon
    try:
        f_out = open(mwmon.interproc_bin_log_name, "w")
        pickle.dump(mwmon.data, f_out)
        f_out.close()
    except Exception:
        traceback.print_exc()
        mwmon.printer(traceback.print_stack())
예제 #12
0
def ntmapviewofsection_ret(params,
                           pid,
                           callback_name,
                           mapped_sec,
                           mapping_proc,
                           base_p, size_p,
                           offset_p,
                           proc,
                           update_vads):
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import SectionMap
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    pgd = api.get_running_process(cpu_index)

    # First, remove callback
    mwmon.cm.rm_callback(callback_name)

    if base_p != 0:
        base = dereference_target_long(base_p, pgd)
    else:
        base = 0

    if size_p != 0:
        size = dereference_target_long(size_p, pgd)
    else:
        size = 0

    # Offset is always 8 bytes
    if offset_p != 0:
        try:
            offset = struct.unpack("Q", api.r_va(pgd, offset_p, 8))[0]
        except:
            offset = 0
            mwmon.printer("Could not dereference offset in NtMapViewOfSection return, in interproc.py")
    else:
        offset = 0

    mapping_proc.section_maps.append(
        SectionMap(mapped_sec, base, size, offset))

    if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
        f = mwmon.interproc_text_log_handle
        if TARGET_LONG_SIZE == 4:
            f.write("[PID: %x] NtMapViewOfSection: Base: %08x Size: %08x Offset: %08x / Section: %s\n" %
                    (pid, base, size, offset, mapped_sec.backing_file))
        elif TARGET_LONG_SIZE == 8:
            f.write("[PID: %x] NtMapViewOfSection: Base: %16x Size: %16x Offset: %08x / Section: %s\n" %
                    (pid, base, size, offset, mapped_sec.backing_file))

    if update_vads:
        proc.update_vads()
예제 #13
0
    def update_symbols(self):
        import api
        from api import CallbackManager
        global mods_pending_symbol_resolution

        if self.unpickled:
            return

        syms = api.get_symbol_list()

        # Check if we can remove the module from the list of modules with
        # pending symbol resolution
        for mod in api.get_module_list(self.get_pgd()):
            if mod["symbols_resolved"] and mod["name"] in mods_pending_symbol_resolution.get(self.get_pgd(), []):
                del mods_pending_symbol_resolution[self.get_pgd()][mod["name"]]

        for d in syms:
            mod = d["mod"]
            fun = d["name"]
            addr = d["addr"]

            pos = bisect.bisect_left(self.symbols, Symbol("", "", addr))
            if pos >= 0 and pos < len(self.symbols) and self.symbols[pos].get_addr() == addr:
                continue
            if mod in self.modules:
                for pair in self.modules[mod]:
                    # Update the (include/exclude addresses in apitracer)
                    if mwmon.include_apis is not None:
                        if (mod.lower(), fun.lower()) in mwmon.include_apis:
                            if (pair[0] + addr) not in mwmon.include_apis_addrs:
                                mwmon.include_apis_addrs.append(pair[0] + addr)

                    if mwmon.exclude_apis is not None:
                        if (mod.lower(), fun.lower()) in mwmon.exclude_apis:
                            if (pair[0] + addr) not in mwmon.exclude_apis_addrs:
                                mwmon.exclude_apis_addrs.append(pair[0] + addr)

                    bisect.insort(
                        self.symbols, Symbol(mod, fun, pair[0] + addr))
                    if mwmon.interproc or mwmon.api_tracer or mwmon.dumper:
                        # Add breakpoint if necessary
                        if (mod, fun) in self.breakpoints and self.breakpoints[(mod, fun)] is None:
                            f_callback = self.bp_funcs[(mod, fun)][0]
                            update_vads = self.bp_funcs[(mod, fun)][1]
                            callback = functools.partial(
                                f_callback, pid=self.pid, proc=self, update_vads=update_vads)
                            bp = mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                                                       callback,
                                                       name="api_bp_%x_%s" % (self.pid, fun),
                                                       addr=pair[0] + addr,
                                                       pgd=self.pgd)
                            self.breakpoints[(mod, fun)] = (bp, pair[0] + addr)
                            mwmon.printer("Adding breakpoint at %s:%s %x:%x from process with PID %x" %
                                          (mod, fun, pair[0] + addr, self.pgd, self.pid))
예제 #14
0
    def update_symbols(self):
        import api
        from api import CallbackManager
        global mods_pending_symbol_resolution

        if self.unpickled:
            return

        syms = api.get_symbol_list()

        # Check if we can remove the module from the list of modules with
        # pending symbol resolution
        for mod in api.get_module_list(self.get_pgd()):
            if mod["symbols_resolved"] and mod["name"] in mods_pending_symbol_resolution.get(self.get_pgd(), []):
                del mods_pending_symbol_resolution[self.get_pgd()][mod["name"]]

        for d in syms:
            mod = d["mod"]
            fun = d["name"]
            addr = d["addr"]

            pos = bisect.bisect_left(self.symbols, Symbol("", "", addr))
            if pos >= 0 and pos < len(self.symbols) and self.symbols[pos].get_addr() == addr:
                continue
            if mod in self.modules:
                for pair in self.modules[mod]:
                    # Update the (include/exclude addresses in apitracer)
                    if mwmon.include_apis is not None:
                        if (mod.lower(), fun.lower()) in mwmon.include_apis:
                            if (pair[0] + addr) not in mwmon.include_apis_addrs:
                                mwmon.include_apis_addrs.append(pair[0] + addr)

                    if mwmon.exclude_apis is not None:
                        if (mod.lower(), fun.lower()) in mwmon.exclude_apis:
                            if (pair[0] + addr) not in mwmon.exclude_apis_addrs:
                                mwmon.exclude_apis_addrs.append(pair[0] + addr)

                    bisect.insort(
                        self.symbols, Symbol(mod, fun, pair[0] + addr))
                    if mwmon.interproc or mwmon.api_tracer or mwmon.dumper:
                        # Add breakpoint if necessary
                        if (mod, fun) in self.breakpoints and self.breakpoints[(mod, fun)] is None:
                            f_callback = self.bp_funcs[(mod, fun)][0]
                            update_vads = self.bp_funcs[(mod, fun)][1]
                            callback = functools.partial(
                                f_callback, pid=self.pid, proc=self, update_vads=update_vads)
                            bp = mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                                                       callback,
                                                       name="api_bp_%x_%s" % (self.pid, fun),
                                                       addr=pair[0] + addr,
                                                       pgd=self.pgd)
                            self.breakpoints[(mod, fun)] = (bp, pair[0] + addr)
                            mwmon.printer("Adding breakpoint at %s:%s %x:%x from process with PID %x" %
                                          (mod, fun, pair[0] + addr, self.pgd, self.pid))
예제 #15
0
def ntmapviewofsection_ret(params, pid, callback_name, mapped_sec,
                           mapping_proc, base_p, size_p, offset_p, proc,
                           update_vads):
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import SectionMap
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    pgd = api.get_running_process(cpu_index)

    # First, remove callback
    mwmon.cm.rm_callback(callback_name)

    if base_p != 0:
        base = dereference_target_long(base_p, pgd)
    else:
        base = 0

    if size_p != 0:
        size = dereference_target_long(size_p, pgd)
    else:
        size = 0

    # Offset is always 8 bytes
    if offset_p != 0:
        try:
            offset = struct.unpack("Q", api.r_va(pgd, offset_p, 8))[0]
        except:
            offset = 0
            mwmon.printer(
                "Could not dereference offset in NtMapViewOfSection return, in interproc.py"
            )
    else:
        offset = 0

    mapping_proc.section_maps.append(SectionMap(mapped_sec, base, size,
                                                offset))

    if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
        f = mwmon.interproc_text_log_handle
        if TARGET_LONG_SIZE == 4:
            f.write(
                "[PID: %x] NtMapViewOfSection: Base: %08x Size: %08x Offset: %08x / Section: %s\n"
                % (pid, base, size, offset, mapped_sec.backing_file))
        elif TARGET_LONG_SIZE == 8:
            f.write(
                "[PID: %x] NtMapViewOfSection: Base: %16x Size: %16x Offset: %08x / Section: %s\n"
                % (pid, base, size, offset, mapped_sec.backing_file))

    if update_vads:
        proc.update_vads()
예제 #16
0
def remove_process(params):
    from mw_monitor_classes import mwmon
    from api import unload_module

    pid = params["pid"]
    pgd = params["pgd"]
    name = params["name"]

    for proc in mwmon.data.procs:
        if proc.get_pid() == pid and proc.has_exited() is False:
            proc.set_exited()
            mwmon.printer("Process %s (%x) exited" % (name, pid))
예제 #17
0
def mw_monitor_start_monitoring_process(new_proc, insert_proc=True):
    '''
        This function sets up all the callbacks and structures
        necessary to monitor a process.

        :param new_proc: The process to start monitoring as a instance of Process
        :type new_proc: Process

        :param insert_proc: (Optional) Whether to insert the process in the list of
                            monitored processes (internal to this script).
        :type insert_proc: bool
    '''
    from coverage import block_executed
    from api_tracer import apitracer_start_monitoring_process
    from dumper import dumper_start_monitoring_process
    from api import CallbackManager
    import api

    # Insert the process, if necessary
    if insert_proc:
        mwmon.data.procs.append(new_proc)
    # Start monitoring the process
    api.start_monitoring_process(new_proc.get_pgd())
    mwmon.printer("Started monitoring process with PGD %x and name %s" %
                  (new_proc.get_pgd(),
                   new_proc.get_proc_name()))

    # coverage module
    # Create a callback and trigger for each process
    if mwmon.coverage and (mwmon.coverage_procs is None or new_proc.proc_name in mwmon.coverage_procs):
        mwmon.cm.add_callback(CallbackManager.BLOCK_BEGIN_CB, functools.partial(
            block_executed, proc=new_proc), name="block_begin_coverage_%d" % new_proc.proc_num)
        mwmon.cm.add_trigger("block_begin_coverage_%d" %
                             new_proc.proc_num, "triggers/trigger_block_user_only_coverage.so")
        mwmon.cm.set_trigger_var("block_begin_coverage_%d" %
                                 (new_proc.proc_num), "cr3", new_proc.get_pgd())
        mwmon.cm.set_trigger_var("block_begin_coverage_%d" %
                                 (new_proc.proc_num), "proc_num", new_proc.proc_num)
        # Output file name, with pid
        mwmon.cm.set_trigger_var("block_begin_coverage_%d" %
                                 (new_proc.proc_num), "log_name", mwmon.coverage_log_name + ".%x" % (new_proc.pid))

    # api tracer module
    if mwmon.api_tracer and (mwmon.api_tracer_procs is None or new_proc.proc_name in mwmon.api_tracer_procs):
        apitracer_start_monitoring_process(new_proc)

    # dumper module
    if mwmon.dumper:
        dumper_start_monitoring_process(new_proc)        

    mwmon.cm.add_callback(CallbackManager.CONTEXTCHANGE_CB,
                          functools.partial(context_change, new_proc, new_proc.get_proc_name()),
                          name="context_change_%x" % new_proc.get_pgd())
예제 #18
0
def remove_process(params):
    from mw_monitor_classes import mwmon
    from api import unload_module

    pid = params["pid"]
    pgd = params["pgd"]
    name = params["name"]

    for proc in mwmon.data.procs:
        if proc.get_pid() == pid and proc.has_exited() is False:
            proc.set_exited()
            mwmon.printer("Process %s (%x) exited" % (name, pid))
예제 #19
0
def context_change(new_proc, target_mod_name, params):
    '''Callback triggered for every context change'''
    global ntdll_breakpoint
    from mw_monitor_classes import mwmon
    from api import BP
    import api
    from api import CallbackManager
    from functools import partial

    old_pgd = params["old_pgd"]
    new_pgd = params["new_pgd"]

    if new_proc.get_pgd() == new_pgd:
        ep = find_ep(new_proc, target_mod_name)
        if ep is not None:
            mwmon.printer("The entry point for %s is %x\n" %
                          (target_mod_name, ep))
            mwmon.cm.rm_callback("context_change_%x" % new_proc.get_pgd())

            try:
                # Load modules and symbols for the process
                mods = api.get_module_list(new_proc.get_pgd())
                if mods is not None:
                    for m in mods:
                        name = m["name"]
                        base = m["base"]
                        size = m["size"]
                        new_proc.set_module(name, base, size)
                        # NTDLL is a special case, and we set a breakpoint
                        # on the code of the main module to trigger the symbol resolution
                        # as soon as we execute one instruction in its
                        # region
                        if target_mod_name in name:
                            ntdll_breakpoint[new_proc.get_pgd()] = BP(
                                base,
                                new_proc.get_pgd(),
                                size=size,
                                func=partial(ntdll_breakpoint_func, new_proc),
                                new_style=True)
                            ntdll_breakpoint[new_proc.get_pgd()].enable()

            except ValueError as e:
                # The process has not been created yet, so we need to
                # wait for symbol resolution
                pass

            # Callback for each module loaded
            mwmon.cm.add_callback(CallbackManager.LOADMODULE_CB,
                                  module_loaded,
                                  pgd=new_proc.get_pgd(),
                                  name=("load_module_%x" % new_proc.get_pgd()))
예제 #20
0
def context_change(new_proc, target_mod_name, params):
    '''Callback triggered for every context change'''
    global ntdll_breakpoint
    from mw_monitor_classes import mwmon
    from api import BP
    import api
    from api import CallbackManager
    from functools import partial

    old_pgd = params["old_pgd"]
    new_pgd = params["new_pgd"]

    if new_proc.get_pgd() == new_pgd:
        ep = find_ep(new_proc, target_mod_name)
        if ep is not None:
            mwmon.printer("The entry point for %s is %x\n" % (target_mod_name, ep))
            mwmon.cm.rm_callback("context_change_%x" % new_proc.get_pgd())

            try:
                # Load modules and symbols for the process
                mods = api.get_module_list(new_proc.get_pgd())
                if mods is not None:
                    for m in mods:
                        name = m["name"]
                        base = m["base"]
                        size = m["size"]
                        new_proc.set_module(name, base, size)
                        # NTDLL is a special case, and we set a breakpoint
                        # on the code of the main module to trigger the symbol resolution
                        # as soon as we execute one instruction in its 
                        # region
                        if target_mod_name in name:
                            ntdll_breakpoint[new_proc.get_pgd()] = BP(base, 
                                                  new_proc.get_pgd(), 
                                                  size = size,
                                                  func = partial(ntdll_breakpoint_func, new_proc),
                                                  new_style = True)
                            ntdll_breakpoint[new_proc.get_pgd()].enable()

            except ValueError as e:
                # The process has not been created yet, so we need to 
                # wait for symbol resolution
                pass

            # Callback for each module loaded
            mwmon.cm.add_callback(CallbackManager.LOADMODULE_CB, 
                                  module_loaded, 
                                  pgd = new_proc.get_pgd(), 
                                  name = ("load_module_%x" % new_proc.get_pgd()))
예제 #21
0
def opcodes_ret(addr_from, addr_to, data, callback_name, argument_parser, mod,
                fun, proc, cpu_index, cpu):
    from mw_monitor_classes import mwmon
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    try:
        mwmon.cm.rm_callback(callback_name)
        if TARGET_LONG_SIZE == 4:
            argument_parser.update_return(cpu.EAX)
        elif TARGET_LONG_SIZE == 8:
            argument_parser.update_return(cpu.RAX)
        data.out_args = [arg for arg in argument_parser.get_out_args()]
        data.ret = argument_parser.get_ret()
        proc.add_call(addr_from, addr_to, data)
    except Exception as e:
        mwmon.printer("Exception: %s" % str(e))
    finally:
        return
예제 #22
0
def ntdll_breakpoint_func(proc, cpu_index, cpu):
    ''' 
        Breakpoint for the first instruction executed in the main module
    '''
    global ntdll_breakpoint
    from mw_monitor_classes import mwmon
    import api

    ntdll_breakpoint[proc.get_pgd()].disable()
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    if TARGET_LONG_SIZE == 4:
        mwmon.printer("Executed first instruction for pgd %x at %x" %
                      (cpu.CR3, cpu.EIP))
    elif TARGET_LONG_SIZE == 8:
        mwmon.printer("Executed first instruction for pgd %x at %x" %
                      (cpu.CR3, cpu.RIP))

    proc.update_symbols()
예제 #23
0
def ntdll_breakpoint_func(proc, params):
    ''' 
        Breakpoint for the first instruction executed in the main module
    '''
    global ntdll_breakpoint
    from mw_monitor_classes import mwmon
    import api

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    ntdll_breakpoint[proc.get_pgd()].disable()
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    if TARGET_LONG_SIZE == 4:
        mwmon.printer("Executed first instruction for pgd %x at %x" % (cpu.CR3, cpu.EIP))
    elif TARGET_LONG_SIZE == 8:
        mwmon.printer("Executed first instruction for pgd %x at %x" % (cpu.CR3, cpu.RIP))

    proc.update_symbols()
예제 #24
0
def opcodes_ret(addr_from, addr_to, data, callback_name, argument_parser, mod, fun, proc, params):
    from mw_monitor_classes import mwmon
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    try:
        mwmon.cm.rm_callback(callback_name)
        if TARGET_LONG_SIZE == 4:
            argument_parser.update_return(cpu.EAX)
        elif TARGET_LONG_SIZE == 8:
            argument_parser.update_return(cpu.RAX)
        data.out_args = [arg for arg in argument_parser.get_out_args()]
        data.ret = argument_parser.get_ret()
        proc.add_call(addr_from, addr_to, data)
    except Exception as e:
        mwmon.printer("Exception: %s" % str(e))
    finally:
        return
예제 #25
0
 def __str__(self):
     from mw_monitor_classes import mwmon
     import api
     TARGET_LONG_SIZE = api.get_os_bits() / 8
     try:
         outstr = ""
         if TARGET_LONG_SIZE == 4:
             outstr += ("\n\n\n[0x%08x] --> [%s:%s] --> [0x%08x]\n" %
                        (self.pc, self.mod, self.fun, self.ret_addr))
         elif TARGET_LONG_SIZE == 8:
             outstr += ("\n\n\n[0x%016x] --> [%s:%s] --> [0x%016x]\n" %
                        (self.pc, self.mod, self.fun, self.ret_addr))
         args = sorted(self.in_args + self.out_args)
         for arg in args:
             if arg.is_output_arg():
                 try:
                     outstr += ("[OUT] %s: %s\n" %
                                (arg.get_arg_name(), arg.__str__()))
                 except Exception as e:
                     outstr += ("[OUT] %s: Unable to process: %s\n" %
                                (arg.get_arg_name(), str(e)))
             else:
                 try:
                     outstr += ("[IN ] %s: %s\n" %
                                (arg.get_arg_name(), arg.__str__()))
                 except Exception as e:
                     outstr += ("[IN] %s: Unable to process : %s\n" %
                                (arg.get_arg_name(), str(e)))
         if self.ret is not None and self.ret is not "":
             try:
                 outstr += ("[RET] %s: %s\n" %
                            (self.ret.get_arg_name(), self.ret.__str__()))
             except Exception as e:
                 outstr += ("[RET] %s: Unable to process: %s\n" %
                            (self.ret.get_arg_name(), str(e)))
         return outstr
     except Exception as e:
         mwmon.printer(traceback.print_exc())
         mwmon.printer(str(e))
예제 #26
0
def find_ep(proc, proc_name):
    '''Given an address space and a process name, uses pefile module
       to get its entry point
    '''
    import api
    import pefile
    from mw_monitor_classes import mwmon

    try:
        for m in api.get_module_list(proc.get_pgd()):
            name = m["name"]
            base = m["base"]
            # size = m["size"]
            if name == proc_name:
                    pe_data = api.r_va(proc.get_pgd(), base, 0x1000)
                    pe = pefile.PE(data=pe_data)
                    ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
                    return (base + ep)
    except Exception as e:
        mwmon.printer("Unable to run pefile on loaded module %s (%s)" % (proc_name, str(e)))
        pass
    return None
예제 #27
0
 def __str__(self):
     from mw_monitor_classes import mwmon
     import api
     TARGET_LONG_SIZE = api.get_os_bits() / 8
     try:
         outstr = ""
         if TARGET_LONG_SIZE == 4:
             outstr += ("\n\n\n[0x%08x] --> [%s:%s] --> [0x%08x]\n" %
                        (self.pc, self.mod, self.fun, self.ret_addr))
         elif TARGET_LONG_SIZE == 8:
             outstr += ("\n\n\n[0x%016x] --> [%s:%s] --> [0x%016x]\n" %
                        (self.pc, self.mod, self.fun, self.ret_addr))
         args = sorted(self.in_args + self.out_args)
         for arg in args:
             if arg.is_output_arg():
                 try:
                     outstr += ("[OUT] %s: %s\n" %
                                (arg.get_arg_name(), arg.__str__()))
                 except Exception as e:
                     outstr += (
                         "[OUT] %s: Unable to process: %s\n" % (arg.get_arg_name(), str(e)))
             else:
                 try:
                     outstr += ("[IN ] %s: %s\n" %
                                (arg.get_arg_name(), arg.__str__()))
                 except Exception as e:
                     outstr += (
                         "[IN] %s: Unable to process : %s\n" % (arg.get_arg_name(), str(e)))
         if self.ret is not None and self.ret is not "":
             try:
                 outstr += ("[RET] %s: %s\n" %
                            (self.ret.get_arg_name(), self.ret.__str__()))
             except Exception as e:
                 outstr += ("[RET] %s: Unable to process: %s\n" %
                            (self.ret.get_arg_name(), str(e)))
         return outstr
     except Exception as e:
         mwmon.printer(traceback.print_exc())
         mwmon.printer(str(e))
예제 #28
0
def find_ep(proc, proc_name):
    '''Given an address space and a process name, uses pefile module
       to get its entry point
    '''
    import api
    import pefile
    from mw_monitor_classes import mwmon

    try:
        for m in api.get_module_list(proc.get_pgd()):
            name = m["name"]
            base = m["base"]
            # size = m["size"]
            if proc_name in name:
                    pe_data = api.r_va(proc.get_pgd(), base, 0x1000)
                    pe = pefile.PE(data=pe_data)
                    ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
                    return (base + ep)
    except Exception as e:
        mwmon.printer("Unable to run pefile on loaded module %s (%s)" % (proc_name, str(e)))
        pass
    return None
예제 #29
0
def dump_command(line):
    '''
    Dump process memory with its modules and rest of VADS. Specify process name,
    pid or cr3 (in hex).
    '''
    from mw_monitor_classes import mwmon
    from utils import find_procs

    if line == "":
        dump()
    else:
        param = line.split()[0].strip()
        found = find_procs(param)
        if len(found) == 0:
            mwmon.printer("Process %s not found" % param)
        elif len(found) == 1 or (len(found) == 2 and found[0][1] == found[1][1]):
            if found[0][0] == 0:
                # kernel process
                pass
            else:
                pid, pgd, pname = found[0]
                dump(pid=pid)
        else:
            mwmon.printer("Please specify a valid (and unique) process")
예제 #30
0
def log_calls():
    from mw_monitor_classes import mwmon
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    f_out = open(mwmon.api_tracer_text_log_name, "w")
    try:
        for proc in mwmon.data.procs:
            if mwmon.api_tracer_procs is None or proc.proc_name in mwmon.api_tracer_procs:
                f_out.write("Process (PID: %x) %s\n" %
                            (proc.pid, proc.proc_name))
                for vad in proc.vads:
                    if len(vad.get_calls()) > 0:
                        if TARGET_LONG_SIZE == 4:
                            f_out.write("\n\nVAD [%08x - %08x]\n\n" %
                                        (vad.start, vad.size))
                        elif TARGET_LONG_SIZE == 8:
                            f_out.write("\n\nVAD [%016x - %016x]\n\n" %
                                        (vad.start, vad.size))
                        for data in vad.get_calls():
                            f_out.write("%s" % data[2].__str__())
                if len(proc.other_calls) > 0:
                    f_out.write("\n\n OTHER CALLS...\n\n")
                    for call in proc.other_calls:
                        f_out.write("%s" % data[2].__str__())
        if f_out is not None:
            f_out.close()
    except Exception as e:
        mwmon.printer(str(e))
        mwmon.printer(traceback.print_exc())

    # Output ordered calls
    f_out = open(mwmon.api_tracer_text_log_name + ".ordered", "w")
    try:
        for proc in mwmon.data.procs:
            if mwmon.api_tracer_procs is None or proc.proc_name in mwmon.api_tracer_procs:
                f_out.write("Process (PID: %x) %s\n" %
                            (proc.pid, proc.proc_name))
                for data in proc.all_calls:
                    f_out.write("%s" % data[2].__str__())
        if f_out is not None:
            f_out.close()
    except Exception as e:
        mwmon.printer(str(e))
        mwmon.printer(traceback.print_exc())
예제 #31
0
def log_calls():
    from mw_monitor_classes import mwmon
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    f_out = open(mwmon.api_tracer_text_log_name, "w")
    try:
        for proc in mwmon.data.procs:
            if mwmon.api_tracer_procs is None or proc.proc_name in mwmon.api_tracer_procs:
                f_out.write("Process (PID: %x) %s\n" %
                            (proc.pid, proc.proc_name))
                for vad in proc.vads:
                    if len(vad.get_calls()) > 0:
                        if TARGET_LONG_SIZE == 4:
                            f_out.write(
                                "\n\nVAD [%08x - %08x]\n\n" % (vad.start, vad.size))
                        elif TARGET_LONG_SIZE == 8:
                            f_out.write(
                                "\n\nVAD [%016x - %016x]\n\n" % (vad.start, vad.size))
                        for data in vad.get_calls():
                            f_out.write("%s" % data[2].__str__())
                if len(proc.other_calls) > 0:
                    f_out.write("\n\n OTHER CALLS...\n\n")
                    for call in proc.other_calls:
                        f_out.write("%s" % data[2].__str__())
        if f_out is not None:
            f_out.close()
    except Exception as e:
        mwmon.printer(str(e))
        mwmon.printer(traceback.print_exc())

    # Output ordered calls
    f_out = open(mwmon.api_tracer_text_log_name + ".ordered", "w")
    try:
        for proc in mwmon.data.procs:
            if mwmon.api_tracer_procs is None or proc.proc_name in mwmon.api_tracer_procs:
                f_out.write("Process (PID: %x) %s\n" %
                            (proc.pid, proc.proc_name))
                for data in proc.all_calls:
                    f_out.write("%s" % data[2].__str__())
        if f_out is not None:
            f_out.close()
    except Exception as e:
        mwmon.printer(str(e))
        mwmon.printer(traceback.print_exc())
예제 #32
0
def clean():
    '''
    Clean up everything. At least you need to place this
    clean() call to the callback manager, that will
    unregister all the registered callbacks.
    '''
    from mw_monitor_logging import log_calls
    from mw_monitor_classes import mwmon
    # import tarfile

    mwmon.printer("Cleaning module")
    mwmon.cm.clean()
    mwmon.printer("Cleaned module")

    out_bundle = tarfile.open(mwmon.output_bundle_name, "w:gz")
    if os.path.isfile('mw_monitor_run.json'):
        out_bundle.add('mw_monitor_run.json')

    if mwmon.api_tracer and mwmon.api_tracer_text_log:
        mwmon.printer("Writing text call log")
        log_calls()
        if os.path.isfile(mwmon.api_tracer_text_log_name):
            out_bundle.add(mwmon.api_tracer_text_log_name)

    if mwmon.api_tracer and mwmon.api_tracer_bin_log:
        mwmon.printer("Writing binary call log")
        serialize_calls()
        if os.path.isfile(mwmon.api_tracer_bin_log_name):
            out_bundle.add(mwmon.api_tracer_bin_log_name)

    if mwmon.interproc and mwmon.interproc_bin_log:
        mwmon.printer("Writing interproc bin log")
        serialize_interproc()
        if os.path.isfile(mwmon.interproc_bin_log_name):
            out_bundle.add(mwmon.interproc_bin_log_name)

    if mwmon.interproc and mwmon.interproc_basic_stats:
        mwmon.printer("Writing interproc text log")
        interproc_basic_stats()
        if os.path.isfile(mwmon.interproc_basic_stats_name):
            out_bundle.add(mwmon.interproc_basic_stats_name)

    if mwmon.interproc_text_log_handle is not None:
        mwmon.interproc_text_log_handle.close()
        mwmon.interproc_text_log_handle = None
        if os.path.isfile(mwmon.interproc_text_log_name):
            out_bundle.add(mwmon.interproc_text_log_name)

    if mwmon.coverage:
        mwmon.printer("Processing coverage")
        log_coverage()
        if os.path.isfile(mwmon.coverage_log_name):
            out_bundle.add(mwmon.coverage_log_name)
        if os.path.isfile(mwmon.coverage_text_name):
            out_bundle.add(mwmon.coverage_text_name)

    if mwmon.dumper:
        mwmon.printer("Adding dumped memory")
        # Add the dumped stuff.
        if os.path.isdir(mwmon.dumper_path):
            mwmon.printer(mwmon.dumper_path)
            out_bundle.add(mwmon.dumper_path)

    # Remove the temporary extracted files
    if os.path.isdir(mwmon.extracted_files_path):
        shutil.rmtree(mwmon.extracted_files_path)

    out_bundle.close()

    mwmon.printer("Module unloaded")
예제 #33
0
def opcodes(params, db, proc):
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import is_in_pending_resolution
    from api import CallbackManager
    import api
    from DeviareDbParser import ArgumentParser

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]
    pc = params["cur_pc"]
    next_pc = params["next_pc"]

    # First, check if the next_pc is located in a module with
    # pending symbol resolution, and update symbols
    # accordingly
    if is_in_pending_resolution(proc.get_pgd(), next_pc):
        proc.update_symbols()

    try:
        # Locate nearest lower symbol
        sym = proc.locate_nearest_symbol(next_pc)
        if sym is None:
            return
        mod = sym.get_mod()
        fun = sym.get_fun()
        real_api_addr = sym.get_addr()
        # Reduce FP's by checking that the origin EIP is not also within the
        # same module (code reuse inside the dll)
        if mod in proc.modules:
            for (base, size) in proc.modules[mod]:
                if pc >= base and pc <= (base + size):
                    return

        # First shortcut: check if it is an excluded api/module, or included:
        if mwmon.exclude_apis_addrs is not None and len(mwmon.exclude_apis_addrs) > 0:
            if real_api_addr in mwmon.exclude_apis_addrs:
                return

        if mwmon.exclude_modules_addrs is not None and len(mwmon.exclude_modules_addrs) > 0:
            for (base, size) in mwmon.exclude_modules_addrs:
                if real_api_addr >= base and real_api_addr < (base + size):
                    return

        # Origin modules
        if mwmon.exclude_origin_modules_addrs is not None and len(mwmon.exclude_origin_modules_addrs) > 0:
            # pc is the originating pc
            for (base, size) in mwmon.exclude_origin_modules_addrs:
                if pc >= base and pc < (base + size):
                    return

        if mwmon.include_apis_addrs is not None and len(mwmon.include_apis_addrs) > 0:
            if real_api_addr not in mwmon.include_apis_addrs:
                return

        if proc.in_mod_boundaries(real_api_addr):

            pgd = api.get_running_process(cpu_index)

            # Set callback on return address
            if TARGET_LONG_SIZE == 4:
                try:
                    ret_addr_val = api.r_va(pgd, cpu.ESP, 4)
                    ret_addr = struct.unpack("<I", ret_addr_val)[0]
                except:
                    ret_addr = 0
                    mwmon.printer("Could not read return address on API tracer")
            elif TARGET_LONG_SIZE == 8:
                try:
                    ret_addr_val = api.r_va(pgd, cpu.RSP, 8)
                    ret_addr = struct.unpack("<Q", ret_addr_val)[0]
                except:
                    ret_addr = 0 
                    mwmon.printer("Could not read return address on API tracer")

            if mwmon.api_tracer_light_mode:
                if real_api_addr == next_pc:
                    if TARGET_LONG_SIZE == 4:
                        proc.add_call(pc, real_api_addr, "[PID: %x] %08x --> %s:%s(%08x) --> %08x\n" % (
                            proc.pid, pc, mod, fun, real_api_addr, ret_addr))
                    elif TARGET_LONG_SIZE == 8:
                        proc.add_call(pc, real_api_addr, "[PID: %x] %016x --> %s:%s(%016x) --> %016x\n" % (
                            proc.pid, pc, mod, fun, real_api_addr, ret_addr))
                else:
                    if TARGET_LONG_SIZE == 4:
                        proc.add_call(pc, real_api_addr, "[PID: %x] %08x --> %s:%s(+%x)(%08x) --> %08x\n" % (
                            proc.pid, pc, mod, fun, (next_pc - real_api_addr), next_pc, ret_addr))
                    elif TARGET_LONG_SIZE == 8:
                        proc.add_call(pc, real_api_addr, "[PID: %x] %016x --> %s:%s(+%x)(%016x) --> %016x\n" % (
                            proc.pid, pc, mod, fun, (next_pc - real_api_addr), next_pc, ret_addr))
                return

            data = APICallData()
            data.pc = pc
            data.mod = mod
            data.fun = fun
            data.ret_addr = ret_addr

            if TARGET_LONG_SIZE == 4:
                argument_parser = ArgumentParser(db, cpu, cpu.ESP, mod, fun)
            elif TARGET_LONG_SIZE == 8:
                argument_parser = ArgumentParser(db, cpu, cpu.RSP, mod, fun)

            if not argument_parser.in_db():
                return

            data.in_args = [arg for arg in argument_parser.get_in_args()]

            # If return address could not be read, we skip the callback
            if ret_addr != 0:
                callback_name = "ret_bp_%d" % mwmon.bp_counter

                callback = functools.partial(opcodes_ret,
                                             pc,
                                             real_api_addr,
                                             data,
                                             callback_name,
                                             argument_parser,
                                             mod,
                                             fun,
                                             proc)

                mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                                      callback,
                                      name=callback_name,
                                      addr=data.ret_addr,
                                      pgd=pgd)

                mwmon.bp_counter += 1

    except Exception as e:
        mwmon.printer(str(e))
        traceback.print_exc()
    finally:
        return
예제 #34
0
def dump(params,
         pid=None,
         proc=None,
         update_vads=None,
         from_addr=None,
         callback_name=None,
         terminate_process=False):
    '''
    Dump the process, modules, vads...
    '''
    import volatility.constants as constants
    import volatility.exceptions as exceptions
    import volatility.obj as obj
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from utils import get_addr_space
    import api

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    mwmon.printer("Dumping process...")

    proc_hdl = None
    if terminate_process:
        if TARGET_LONG_SIZE == 4:
            # ret, proc_hdl, exit_status
            try:
                _, proc_hdl, _ = struct.unpack(
                    "<III", api.r_va(api.get_running_process(cpu_index), cpu.ESP, 4 * 3))
            except:
                proc_hdl = 0
                mwmon.printer("Could not dereference process handle in dumper.py")
        elif TARGET_LONG_SIZE == 8:
            # We don't need the return address
            # ret = struct.unpack("<Q",
            # api.r_va(api.get_running_process(cpu_index), cpu.ESP, 8))[0]
            proc_hdl = cpu.RCX
            # We don't need the exit status
            # exit_status = cpu.RDX

        # It seems there are usually 2 calls, when a process terminates itself.
        # First, ZwTerminateProcess is called with 0 as proc_hdl, and afterwards
        # -1.
        if proc_hdl == 0:
            return

    if callback_name is not None:
        # First, remove callback
        mwmon.cm.rm_callback(callback_name)

    # Check if we have been called from the right from_addr
    if from_addr is not None:
        if TARGET_LONG_SIZE == 4:
            try:
                buff = api.r_va(api.get_running_process(cpu_index), cpu.ESP, 4)
                ret_addr = struct.unpack("<I", buff)[0]
            except:
                ret_addr = 0
                mwmon.printer("Could not dereference return address on dumper.py")
        elif TARGET_LONG_SIZE == 8:
            try:
                buff = api.r_va(api.get_running_process(cpu_index), cpu.RSP, 8)
                ret_addr = struct.unpack("<Q", buff)[0]
            except:
                ret_addr = 0
                mwmon.printer("Could not dereference return address on dumper.py")
        if from_addr != ret_addr:
            return

    # We have been called from the right point, now, dump.
    path = mwmon.dumper_path
    # Dump a file with the VAD info, etc, and a filename for each dumped file,
    # so that we can import feed IDA with it
    try:
        # Dump main executable.
        addr_space = get_addr_space()

        # If 1 handle is specified, get the pid for that handle instead
        # of the calling PID.
        if proc_hdl is not None:
            if (TARGET_LONG_SIZE == 4 and proc_hdl == 0xFFFFFFFF) or \
               (TARGET_LONG_SIZE == 8 and proc_hdl == 0xFFFFFFFFFFFFFFFF):
                # If the handle is 0xFFFFFFFF, then the process is the caller.
                pass
            else:
                eprocs = [t for t in tasks.pslist(
                    addr_space) if t.UniqueProcessId == pid]
                proc_obj = None
                # Search handle table for the new created process
                for task in eprocs:
                    if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
                        for handle in task.ObjectTable.handles():
                            if handle.is_valid() and handle.HandleValue == proc_hdl and handle.get_object_type() == "Process":
                                proc_obj = handle.dereference_as("_EPROCESS")
                                break
                        break
                if proc_obj is not None:
                    # If we found the handle to which it referred, update the
                    # corresponding pid
                    pid = int(proc_obj.UniqueProcessId)
                else:
                    return

        # Case when no PID is specified, just dump everything
        if pid is None:
            pids = [p.pid for p in mwmon.data.procs]
        # When one pid is specified, dump that PID
        else:
            pids = [pid]

        eprocs = [t for t in tasks.pslist(
            addr_space) if t.UniqueProcessId in pids]
        for task in eprocs:
            mwmon.printer("Dumping process %x" % (task.UniqueProcessId))
            # Code adapted from procdump (volatility)
            task_space = task.get_process_address_space()
            if task_space is None:
                mwmon.printer("Error: Cannot acquire process AS")
                return
            elif task.Peb is None:
                # we must use m() here, because any other attempt to
                # reference task.Peb will try to instantiate the _PEB
                mwmon.printer(
                    "Error: PEB at {0:#x} is unavailable (possibly due to paging)".format(task.m('Peb')))
                return
            elif task_space.vtop(task.Peb.ImageBaseAddress) is None:
                mwmon.printer(
                    "Error: ImageBaseAddress at {0:#x} is unavailable" +
                    "(possibly due to paging)".format(task.Peb.ImageBaseAddress))
                return
            else:
                mwmon.printer(
                    "Dumping executable for %x" % (task.UniqueProcessId))
                dump_file = os.path.join(
                    path, "executable.%x.exe" % (task.UniqueProcessId))
                of = open(dump_file, 'wb')
                pe_file = obj.Object(
                    "_IMAGE_DOS_HEADER", offset=task.Peb.ImageBaseAddress, vm=task_space)
                try:
                    for offset, code in pe_file.get_image(unsafe=True,
                                                          memory=False,
                                                          fix=True):
                        of.seek(offset)
                        of.write(code)
                except ValueError, ve:
                    mwmon.printer("Error: {0}".format(ve))
                    return
                except exceptions.SanityCheckException, ve:
                    mwmon.printer("Error: {0} Try -u/--unsafe".format(ve))
                    return
예제 #35
0
def ntwritevirtualmemory(params, pid, proc, update_vads, reverse=False):
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import Injection
    import api
    from utils import get_addr_space

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    # _In_ HANDLE     ProcessHandle,
    # _In_ PVOID  BaseAddress,
    # _In_ PVOID  Buffer,
    # _In_ SIZE_T     NumberOfBytesToWrite,
    # _Out_opt_ PSIZE_T   NumberOfBytesWritten

    pgd = api.get_running_process(cpu_index)

    # Read the parameters
    ret_addr, proc_hdl, remote_addr, local_addr, size, size_out = read_parameters(
        cpu, 5)

    local_proc = None
    # Get local proc
    for proc in mwmon.data.procs:
        if proc.pid == pid:
            local_proc = proc
            break
    # Try to get remote process from list of monitored processes
    remote_proc = None
    # Load volatility address space
    addr_space = get_addr_space(pgd)

    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]

    # Initialize proc_obj, that will point to the eprocess of the new created
    # process
    proc_obj = None

    # Search handle table for the new created process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid(
                ) and handle.HandleValue == proc_hdl and handle.get_object_type(
                ) == "Process":
                    proc_obj = handle.dereference_as("_EPROCESS")
                    break
            break
    if proc_obj is not None:
        for proc in mwmon.data.procs:
            if proc.pid == proc_obj.UniqueProcessId:
                remote_proc = proc
                break
    else:
        # Sometimes we get calls to this function over non-proc handles (e.g. type "Desktop")
        return
    if remote_proc is None:
        mwmon.printer(
            "[!]  Could not obtain remote proc, or it is not being monitored")
        return
    elif local_proc is None:
        mwmon.printer(
            "[!]  Could not obtain local proc, or it is not being monitored")
        return
    else:
        if reverse:
            data = None
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write(
                        "[PID: %x] NtReadVirtualMemory: PID: %x - Addr: %08x <-- PID: %x Addr: %08x / Size: %08x\n"
                        % (pid, local_proc.pid, local_addr, remote_proc.pid,
                           remote_addr, size))
                elif TARGET_LONG_SIZE == 8:
                    f.write(
                        "[PID: %x] NtReadVirtualMemory: PID: %x - Addr: %16x <-- PID: %x Addr: %16x / Size: %16x\n"
                        % (pid, local_proc.pid, local_addr, remote_proc.pid,
                           remote_addr, size))
        else:
            data = None
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write(
                        "[PID: %x] NtWriteVirtualMemory: PID: %x - Addr: %08x --> PID: %x Addr: %08x / Size: %08x\n"
                        % (pid, local_proc.pid, local_addr, remote_proc.pid,
                           remote_addr, size))
                elif TARGET_LONG_SIZE == 8:
                    f.write(
                        "[PID: %x] NtWriteVirtualMemory: PID: %x - Addr: %16x --> PID: %x Addr: %16x / Size: %16x\n"
                        % (pid, local_proc.pid, local_addr, remote_proc.pid,
                           remote_addr, size))

        inj = Injection(remote_proc, remote_addr, local_proc, local_addr, size,
                        data, reverse)
        local_proc.add_injection(inj)

    if update_vads:
        proc.update_vads()
예제 #36
0
def clean():
    '''
    Clean up everything. At least you need to place this
    clean() call to the callback manager, that will
    unregister all the registered callbacks.
    '''
    from mw_monitor_logging import log_calls
    from mw_monitor_classes import mwmon
    # import tarfile

    mwmon.printer("Cleaning module")
    mwmon.cm.clean()
    mwmon.printer("Cleaned module")

    out_bundle = tarfile.open(mwmon.output_bundle_name,"w:gz")
    if os.path.isfile('mw_monitor_run.json'):
       out_bundle.add('mw_monitor_run.json')

    if mwmon.api_tracer and mwmon.api_tracer_text_log:
        mwmon.printer("Writing text call log")
        log_calls()
        if os.path.isfile(mwmon.api_tracer_text_log_name):
            out_bundle.add(mwmon.api_tracer_text_log_name)

    if mwmon.api_tracer and mwmon.api_tracer_bin_log:
        mwmon.printer("Writing binary call log")
        serialize_calls()
        if os.path.isfile(mwmon.api_tracer_bin_log_name):
            out_bundle.add(mwmon.api_tracer_bin_log_name)

    if mwmon.interproc and mwmon.interproc_bin_log:
        mwmon.printer("Writing interproc bin log")
        serialize_interproc()
        if os.path.isfile(mwmon.interproc_bin_log_name):
            out_bundle.add(mwmon.interproc_bin_log_name)

    if mwmon.interproc and mwmon.interproc_basic_stats:
        mwmon.printer("Writing interproc text log")
        interproc_basic_stats()
        if os.path.isfile(mwmon.interproc_basic_stats_name):
            out_bundle.add(mwmon.interproc_basic_stats_name)

    if mwmon.interproc_text_log_handle is not None:
        mwmon.interproc_text_log_handle.close()
        mwmon.interproc_text_log_handle = None
        if os.path.isfile(mwmon.interproc_text_log_name):
            out_bundle.add(mwmon.interproc_text_log_name)

    if mwmon.coverage:
        mwmon.printer("Processing coverage")
        log_coverage()
        if os.path.isfile(mwmon.coverage_log_name):
            out_bundle.add(mwmon.coverage_log_name)
        if os.path.isfile(mwmon.coverage_text_name):
            out_bundle.add(mwmon.coverage_text_name)

    if mwmon.dumper:
        mwmon.printer("Adding dumped memory")
        # Add the dumped stuff.
        if os.path.isdir(mwmon.dumper_path):
            mwmon.printer(mwmon.dumper_path)
            out_bundle.add(mwmon.dumper_path)

    # Remove the temporary extracted files
    if os.path.isdir(mwmon.extracted_files_path):
        shutil.rmtree(mwmon.extracted_files_path)

    out_bundle.close()

    mwmon.printer("Module unloaded")
예제 #37
0
    def __init__(self, pgd, section_object):
        import api

        TARGET_LONG_SIZE = api.get_os_bits() / 8

        # Volatility object representing the section
        self.section_object = section_object

        # Volatility lacks the vtype for _SECTION, which
        # has scarce documentation:

        # http://forum.sysinternals.com/section-object_topic24975.html

        #   These structures seem to remain consistent
        #   across different Windows versions.

        #   typedef struct _MMADDRESS_NODE {
        #   union {
        #       LONG_PTR Balance : 2;
        #       struct _MMADDRESS_NODE *Parent;
        #   } u1;
        #   struct _MMADDRESS_NODE *LeftChild;
        #   struct _MMADDRESS_NODE *RightChild;
        #   ULONG_PTR StartingVpn;
        #   ULONG_PTR EndingVpn;
        #   } MMADDRESS_NODE, *PMMADDRESS_NODE;

        #   typedef struct _SECTION {
        #    MMADDRESS_NODE Address;
        #    PSEGMENT Segment;
        #    LARGE_INTEGER SizeOfSection;
        #    union {
        #     ULONG LongFlags;
        #     MMSECTION_FLAGS Flags;
        #    } u;
        #    MM_PROTECTION_MASK InitialPageProtection;
        #    } SECTION, *PSECTION;

        # As we can see, Volatility has instead a _SECTION_OBJECT
        # vtype, which, consistently across Windows versions,
        # has at the beginning of the structure, 5 pointers, just
        # like the MMADDRESS_NODE for _SECTION. Therefore, the Segment
        # field seems to be at the same offset on both structures:
        # _SECTION and _SECTION_OBJECT, both for 32 and 64 bits.

        # Flags are located after Segment (PSEGMENT) + LARGE_INTEGER (64 bits independently of arch)
        # --> The offset should be the size of 6 pointers + size of LARGE_INTEGER

        # Flags are always 4 bytes

        # Compute FileBacked and  CopyOnWrite
        try:
            self.flags = struct.unpack(
                "I", api.r_va(pgd, self.section_object.obj_offset + ((TARGET_LONG_SIZE * 6) + 8), 0x4))[0]
        except:
            self.flags = 0x00000000
            mwmon.printer("Could not read flags in Section __init__")
        self.cow = ((self.flags & 0x00000800) != 0)
        self.file_backed = ((self.flags & 0x00000080) != 0)

        self.backing_file = None

        # If so, get corresponding file.
        if self.file_backed:
            # Dereference as _SEGMENT, that is different from _SEGMENT_OBJECT
            # This is because the volatility profile lacks the _SECTION object,
            # and instead has the _SECTION_OBJECT. Since the Segment field
            # of _SECTION and _SECTION_OBJECT are aligned, we can just dereference
            # that offset. Nevertheless, _SECTION_OBJECT has a _SEGMENT_OBJECT type
            # Segment, while _SECTION has a _SEGMENT type Segment...

            # http://forum.sysinternals.com/section-object_topic24975.html

            self.segment = self.section_object.Segment.dereference_as(
                "_SEGMENT")
            file_obj = self.segment.ControlArea.FilePointer


            from volatility.plugins.overlays.windows.windows import _FILE_OBJECT
            from volatility.obj import Pointer

            # on winxp file_obj is volatility.obj.Pointer with .target being _FILE_OBJECT
            if not (type(file_obj) is Pointer and type(file_obj.dereference()) is _FILE_OBJECT):
                from volatility.plugins.overlays.windows.windows import _EX_FAST_REF
                if type(file_obj) is _EX_FAST_REF:
                    # on newer volatility profiles, FilePointer is _EX_FAST_REF, needs deref
                    file_obj = file_obj.dereference_as("_FILE_OBJECT")
                else:
                    raise TypeError("The type for self.segment.ControlArea.FilePointer in Section" + \
                                    "class does not match _FILE_OBJECT or _EX_FAST_REF: %r (type %r)" % (file_obj, type(file_obj)))

            for fi in mwmon.data.files:
                if fi.file_name == str(file_obj.FileName):
                    self.backing_file = fi
                    break

            # If we have still not recorded the file, add it to files record
            if self.backing_file is None:
                self.backing_file = File(str(file_obj.FileName))
                mwmon.data.files.append(self.backing_file)
        
        self.unpickled = False
        self.offset = self.section_object.obj_offset
예제 #38
0
def ntopenprocessret(params, pid, callback_name, proc_hdl_p, proc, update_vads):
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import mw_monitor_start_monitoring_process
    from mw_monitor_classes import Process
    from api import get_running_process
    from utils import get_addr_space
    import api

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    pgd = get_running_process(cpu_index)

    # First, remove callback
    mwmon.cm.rm_callback(callback_name)

    # Do not continue if EAX/RAX returns and invalid return code.
    if read_return_parameter(cpu) != 0:
        return

    # Load volatility address space
    addr_space = get_addr_space(pgd)

    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]

    # Initialize proc_obj, that will point to the eprocess of the new created
    # process
    proc_obj = None

    # Dereference the output argument containing the hdl of the newly created
    # process
    proc_hdl = dereference_target_long(proc_hdl_p, pgd)

    # Search handle table for the new created process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid() and handle.HandleValue == proc_hdl and handle.get_object_type() == "Process":
                    proc_obj = handle.dereference_as("_EPROCESS")
                    break
            break

    if proc_obj is not None:
        if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
            f = mwmon.interproc_text_log_handle
            f.write("[PID: %x] NtOpenProcess: %s - PID: %x - CR3: %x\n" %
                    (pid,
                     str(proc_obj.ImageFileName),
                     int(proc_obj.UniqueProcessId),
                     int(proc_obj.Pcb.DirectoryTableBase.v())))

        # Check if we are already monitoring the process
        for proc in mwmon.data.procs:
            if proc.pid == int(proc_obj.UniqueProcessId):
                return
        new_proc = Process(str(proc_obj.ImageFileName))
        new_proc.set_pid(int(proc_obj.UniqueProcessId))
        new_proc.set_pgd(int(proc_obj.Pcb.DirectoryTableBase.v()))
        mw_monitor_start_monitoring_process(new_proc)
    else:
        if TARGET_LONG_SIZE == 4: 
            mwmon.printer("Error while trying to retrieve EPROCESS for handle %x, PID %x, EAX: %x" % (proc_hdl, pid, cpu.EAX))
        elif TARGET_LONG_SIZE == 8:
            mwmon.printer("Error while trying to retrieve EPROCESS for handle %x, PID %x, EAX: %x" % (proc_hdl, pid, cpu.RAX))

    if update_vads:
        proc.update_vads()

    return
예제 #39
0
    def __init__(self, pgd, section_object):
        import api

        TARGET_LONG_SIZE = api.get_os_bits() / 8

        # Volatility object representing the section
        self.section_object = section_object

        # Volatility lacks the vtype for _SECTION, which
        # has scarce documentation:

        # http://forum.sysinternals.com/section-object_topic24975.html

        #   These structures seem to remain consistent
        #   across different Windows versions.

        #   typedef struct _MMADDRESS_NODE {
        #   union {
        #       LONG_PTR Balance : 2;
        #       struct _MMADDRESS_NODE *Parent;
        #   } u1;
        #   struct _MMADDRESS_NODE *LeftChild;
        #   struct _MMADDRESS_NODE *RightChild;
        #   ULONG_PTR StartingVpn;
        #   ULONG_PTR EndingVpn;
        #   } MMADDRESS_NODE, *PMMADDRESS_NODE;

        #   typedef struct _SECTION {
        #    MMADDRESS_NODE Address;
        #    PSEGMENT Segment;
        #    LARGE_INTEGER SizeOfSection;
        #    union {
        #     ULONG LongFlags;
        #     MMSECTION_FLAGS Flags;
        #    } u;
        #    MM_PROTECTION_MASK InitialPageProtection;
        #    } SECTION, *PSECTION;

        # As we can see, Volatility has instead a _SECTION_OBJECT
        # vtype, which, consistently across Windows versions,
        # has at the beginning of the structure, 5 pointers, just
        # like the MMADDRESS_NODE for _SECTION. Therefore, the Segment
        # field seems to be at the same offset on both structures:
        # _SECTION and _SECTION_OBJECT, both for 32 and 64 bits.

        # Flags are located after Segment (PSEGMENT) + LARGE_INTEGER (64 bits independently of arch)
        # --> The offset should be the size of 6 pointers + size of LARGE_INTEGER

        # Flags are always 4 bytes

        # Compute FileBacked and  CopyOnWrite
        try:
            self.flags = struct.unpack(
                "I",
                api.r_va(
                    pgd, self.section_object.obj_offset +
                    ((TARGET_LONG_SIZE * 6) + 8), 0x4))[0]
        except:
            self.flags = 0x00000000
            mwmon.printer("Could not read flags in Section __init__")
        self.cow = ((self.flags & 0x00000800) != 0)
        self.file_backed = ((self.flags & 0x00000080) != 0)

        self.backing_file = None

        # If so, get corresponding file.
        if self.file_backed:
            # Dereference as _SEGMENT, that is different from _SEGMENT_OBJECT
            # This is because the volatility profile lacks the _SECTION object,
            # and instead has the _SECTION_OBJECT. Since the Segment field
            # of _SECTION and _SECTION_OBJECT are aligned, we can just dereference
            # that offset. Nevertheless, _SECTION_OBJECT has a _SEGMENT_OBJECT type
            # Segment, while _SECTION has a _SEGMENT type Segment...

            # http://forum.sysinternals.com/section-object_topic24975.html

            self.segment = self.section_object.Segment.dereference_as(
                "_SEGMENT")
            file_obj = self.segment.ControlArea.FilePointer

            from volatility.plugins.overlays.windows.windows import _FILE_OBJECT
            if type(file_obj) is not _FILE_OBJECT:
                from volatility.plugins.overlays.windows.windows import _EX_FAST_REF
                if type(file_obj) is _EX_FAST_REF:
                    # on newer volatility profiles, FilePointer is _EX_FAST_REF, needs deref
                    file_obj = file_obj.dereference_as("_FILE_OBJECT")
                else:
                    raise TypeError("The type for self.segment.ControlArea.FilePointer in Section" + \
                                    "class does not match _FILE_OBJECT or _EX_FAST_REF")

            for fi in mwmon.data.files:
                if fi.file_name == str(file_obj.FileName):
                    self.backing_file = fi
                    break

            # If we have still not recorded the file, add it to files record
            if self.backing_file is None:
                self.backing_file = File(str(file_obj.FileName))
                mwmon.data.files.append(self.backing_file)

        self.unpickled = False
        self.offset = self.section_object.obj_offset
예제 #40
0
    def generate_arg(self,
                     arg_name,
                     arg_typ,
                     arg_class,
                     is_out,
                     addr=None,
                     val=None,
                     arg_num=0):

        # Return None for null pointers
        if addr is not None and addr == 0:
            return None

        # TODO - FIX - We need to correct the arg_class, because it seems that
        # the db has some errors
        while arg_class >= 65536:
            arg_class -= 65536

        if arg_class == NKT_DBOBJCLASS_Fundamental:
            return BasicArgument(arg_name,
                                 arg_typ,
                                 pgd=self.pgd,
                                 addr=addr,
                                 val=val,
                                 is_out=is_out,
                                 arg_num=arg_num)

        elif arg_class == NKT_DBOBJCLASS_Struct:
            self.c.execute(
                "select Id,Name,Size,Align,Flags from Structs where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                new_struct = Struct(arg_name,
                                    res[0],
                                    res[1],
                                    res[2],
                                    res[3],
                                    res[4],
                                    pgd=self.pgd,
                                    addr=addr,
                                    val=val,
                                    is_out=is_out,
                                    arg_num=arg_num)

                if addr is not None:
                    self.c.execute("select StructId,Id,Name,Offset,Bits,Flags,TypeId,TypeClass" +
                                   " from StructsMembers where StructId = %d order by Id ASC" % (arg_typ))
                    sub_fields = self.c.fetchall()
                    for sub_field in sub_fields:

                        # Skip incorrect blank fields in the db
                        if sub_field[3] == 0 and sub_field[1] > 1:
                            continue

                        offset = sub_field[3] / 8

                        new_struct.add_field(offset,
                                             self.generate_arg(sub_field[2],
                                                               sub_field[6],
                                                               sub_field[7],
                                                               is_out, addr=addr +
                                                               offset,
                                                               val=None,
                                                               arg_num=arg_num))
                else:
                    mwmon.printer("Unsupported type: A struct has been returned as" +
                                  "return value (EAX/RAX), or as register parameter (RCX/RDX/R8/R9).")
                return new_struct
            else:
                return None

        elif arg_class == NKT_DBOBJCLASS_Union:
            self.c.execute(
                "select Id,Name,Size,Align,Flags from Unions where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                new_union = Union(arg_name,
                                  res[0],
                                  res[1],
                                  res[2],
                                  res[3],
                                  res[4],
                                  pgd=self.pgd,
                                  addr=addr,
                                  val=val,
                                  is_out=is_out,
                                  arg_num=arg_num)

                if addr is not None:
                    self.c.execute("select UnionId,Id,Name,Offset,Bits,Flags,TypeId,TypeClass " +
                                   "from UnionsMembers where UnionId =  %d order by Id ASC" % (arg_typ))
                    sub_fields = self.c.fetchall()
                    for sub_field in sub_fields:
                        offset = sub_field[3] / 8
                        new_union.add_field(offset,
                                            self.generate_arg(sub_field[2],
                                                              sub_field[6],
                                                              sub_field[7],
                                                              is_out,
                                                              addr=addr +
                                                              offset,
                                                              arg_num=arg_num))
                else:
                    mwmon.printer("Unsupported type: A union has been returned as" +
                                  "return value (EAX/RAX), or as register parameter (RCX/RDX/R8/R9).")

                return new_union
            else:
                return None

        elif arg_class == NKT_DBOBJCLASS_Typedef:
            self.c.execute(
                "select Id,Name,TypeId,TypeClass from TypeDefs where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                t = Typedef(arg_name,
                            res[0],
                            self.generate_arg(res[1],
                                              res[2],
                                              res[3],
                                              is_out,
                                              addr=addr,
                                              val=val,
                                              arg_num=arg_num),
                            addr=addr,
                            val=val,
                            is_out=is_out,
                            arg_num=arg_num)
                return t
            else:
                return None

        elif arg_class == NKT_DBOBJCLASS_Array:
            self.c.execute(
                "select Id,Max,Size,Align,TypeId,TypeClass from Arrays where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                the_arr = Array(arg_name,
                                res[0],
                                res[1],
                                res[2],
                                res[3],
                                pgd=self.pgd,
                                addr=addr,
                                val=val,
                                is_out=is_out,
                                arg_num=arg_num)

                if addr is not None:
                    size_of_element = res[2] / res[1]
                    for i in range(0, res[1]):
                        the_arr.add_field(i,
                                          self.generate_arg("",
                                                            res[4],
                                                            res[5],
                                                            is_out,
                                                            addr=addr,
                                                            val=None,
                                                            arg_num=arg_num))
                        addr += size_of_element
                else:
                    mwmon.printer("Unsupported type: An array has been returned as" +
                                  "return value (EAX/RAX), or as register parameter (RCX/RDX/R8/R9).")
                return the_arr
            else:
                return None

        elif arg_class == NKT_DBOBJCLASS_Pointer:
            self.c.execute(
                "select Id,Size,Align,TypeId,TypeClass from Pointers where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                # We let the Pointer class determine if it should dereference
                # or not the pointer
                the_pointer = Pointer(arg_name,
                                      arg_typ,
                                      res[1],
                                      res[2],
                                      res[3],
                                      res[4],
                                      is_out,
                                      pgd=self.pgd,
                                      addr=addr,
                                      val=val,
                                      arg_num=arg_num)
                return the_pointer

            else:
                return None

        elif arg_class == NKT_DBOBJCLASS_Reference:
            self.c.execute(
                "select Id,Size,Align,TypeId,TypeClass from XReferences where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                if addr is not None:
                    # Dereference pointer. we can safely dereference it when the argument parser is created (function
                    # call) because the address will not change when the
                    # function returns if it is an output parameter.
                    deref_addr = struct.unpack("<I", read(self.pgd, addr, 4))[0] if res[
                        1] == 32 else struct.unpack("<Q", read(self.pgd, addr, 8))[0]
                else:
                    deref_addr = val
                the_pointer = Reference(arg_name,
                                        arg_typ,
                                        res[1],
                                        res[2],
                                        self.generate_arg("",
                                                          res[3],
                                                          res[4],
                                                          is_out,
                                                          addr=deref_addr,
                                                          arg_num=arg_num),
                                        pgd=self.pgd,
                                        addr=addr,
                                        val=val,
                                        arg_num=arg_num)
                return the_pointer
            else:
                return None

        elif arg_class == NKT_DBOBJCLASS_Enumeration:
            self.c.execute(
                "select Id,Name,Size,Align from Enumerations where Id = %d" % (arg_typ))
            res = self.c.fetchone()
            if res is not None:
                return Enumeration(arg_name,
                                   res[0],
                                   None,
                                   res[2],
                                   pgd=self.pgd,
                                   addr=addr,
                                   val=val,
                                   is_out=is_out,
                                   arg_num=arg_num)
            else:
                return None
예제 #41
0
def dumper_start_monitoring_process(new_proc):
    '''
    Initialization function called for every new process created.
    '''
    from mw_monitor_classes import mwmon
    from api import CallbackManager

    if mwmon.dumper_onexit:
        dll, fun = ("ntdll.dll", "ZwTerminateProcess")
        # Add a bp to the list of symbol based breakpoints for the process
        new_proc.breakpoints[(dll, fun)] = None
        new_proc.bp_funcs[(dll,
                           fun)] = (functools.partial(dump,
                                                      terminate_process=True),
                                    False)
        mwmon.printer("Deferring dumper breakpoint at %s:%s (PGD: %x)" %
                      (dll, fun, new_proc.get_pgd()))

    dump_at = mwmon.dumper_dumpat
    if dump_at is not None:
        # Possible formats for dump_at:
        # 0x00400000
        # user32.dll!CharNextW
        # user32.dll!CharNextW!0x00400000
        terms = dump_at.split("!")
        if len(terms) == 1:
            try:
                addr = int(terms[0], 16)
            except Exception as e:
                mwmon.printer(
                    "Dumper - dump_at: Wrong address value, must specify an hex number"
                )
                return
            cb_name = "dumper_bp_%x_%x" % (new_proc.get_pid(), addr)
            bp = mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                                       functools.partial(
                                           dump,
                                           pid=new_proc.pid,
                                           callback_name=cb_name),
                                       name=cb_name,
                                       addr=addr,
                                       pgd=new_proc.get_pgd())
            breakpoints.append(bp)
            mwmon.printer("Adding dumper breakpoint at %x (CR3: %x)" %
                          (addr, new_proc.get_pgd()))
        elif len(terms) == 2:
            dll = terms[0]
            fun = terms[1]
            # Add a bp to the list of symbol based breakpoints for the process
            if (dll, fun) in new_proc.breakpoints:
                mwmon.printer(
                    "Cannot set dump callback on standard function %s" % fun)
                return
            new_proc.breakpoints[(dll, fun)] = None
            new_proc.bp_funcs[(dll, fun)] = (dump, False)
            mwmon.printer("Deferring dumper breakpoint at %s:%s (PGD: %x)" %
                          (dll, fun, new_proc.get_pgd()))
        elif len(terms) == 3:
            dll = terms[0]
            fun = terms[1]
            try:
                from_addr = int(terms[2], 16)
            except Exception as e:
                mwmon.printer(
                    "Dumper - dump_at: Wrong address value, must specify an hex number : %s"
                    % str(e))
                return
            # Add a bp to the list of symbol based breakpoints for the process
            if (dll, fun) in new_proc.breakpoints:
                mwmon.printer(
                    "Cannot set dump callback on standard function %s" % fun)
                return
            new_proc.breakpoints[(dll, fun)] = None
            new_proc.bp_funcs[(dll,
                               fun)] = (functools.partial(dump,
                                                          from_addr=from_addr),
                                        False)
            mwmon.printer(
                "Deferring dumper breakpoint at %s:%s from %x (PGD: %x)" %
                (dll, fun, from_addr, new_proc.get_pgd()))
        else:
            mwmon.printer(
                "Incorrect format for dumper dump_at parameter. No hook will be created"
            )
예제 #42
0
def dump(params,
         pid=None,
         proc=None,
         update_vads=None,
         from_addr=None,
         callback_name=None,
         terminate_process=False):
    '''
    Dump the process, modules, vads...
    '''
    import volatility.constants as constants
    import volatility.exceptions as exceptions
    import volatility.obj as obj
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from utils import get_addr_space
    import api

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    mwmon.printer("Dumping process...")

    proc_hdl = None
    if terminate_process:
        if TARGET_LONG_SIZE == 4:
            # ret, proc_hdl, exit_status
            try:
                _, proc_hdl, _ = struct.unpack(
                    "<III",
                    api.r_va(api.get_running_process(cpu_index), cpu.ESP,
                             4 * 3))
            except:
                proc_hdl = 0
                mwmon.printer(
                    "Could not dereference process handle in dumper.py")
        elif TARGET_LONG_SIZE == 8:
            # We don't need the return address
            # ret = struct.unpack("<Q",
            # api.r_va(api.get_running_process(cpu_index), cpu.ESP, 8))[0]
            proc_hdl = cpu.RCX
            # We don't need the exit status
            # exit_status = cpu.RDX

        # It seems there are usually 2 calls, when a process terminates itself.
        # First, ZwTerminateProcess is called with 0 as proc_hdl, and afterwards
        # -1.
        if proc_hdl == 0:
            return

    if callback_name is not None:
        # First, remove callback
        mwmon.cm.rm_callback(callback_name)

    # Check if we have been called from the right from_addr
    if from_addr is not None:
        if TARGET_LONG_SIZE == 4:
            try:
                buff = api.r_va(api.get_running_process(cpu_index), cpu.ESP, 4)
                ret_addr = struct.unpack("<I", buff)[0]
            except:
                ret_addr = 0
                mwmon.printer(
                    "Could not dereference return address on dumper.py")
        elif TARGET_LONG_SIZE == 8:
            try:
                buff = api.r_va(api.get_running_process(cpu_index), cpu.RSP, 8)
                ret_addr = struct.unpack("<Q", buff)[0]
            except:
                ret_addr = 0
                mwmon.printer(
                    "Could not dereference return address on dumper.py")
        if from_addr != ret_addr:
            return

    # We have been called from the right point, now, dump.
    path = mwmon.dumper_path
    # Dump a file with the VAD info, etc, and a filename for each dumped file,
    # so that we can import feed IDA with it
    try:
        # Dump main executable.
        addr_space = get_addr_space()

        # If 1 handle is specified, get the pid for that handle instead
        # of the calling PID.
        if proc_hdl is not None:
            if (TARGET_LONG_SIZE == 4 and proc_hdl == 0xFFFFFFFF) or \
               (TARGET_LONG_SIZE == 8 and proc_hdl == 0xFFFFFFFFFFFFFFFF):
                # If the handle is 0xFFFFFFFF, then the process is the caller.
                pass
            else:
                eprocs = [
                    t for t in tasks.pslist(addr_space)
                    if t.UniqueProcessId == pid
                ]
                proc_obj = None
                # Search handle table for the new created process
                for task in eprocs:
                    if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
                        for handle in task.ObjectTable.handles():
                            if handle.is_valid(
                            ) and handle.HandleValue == proc_hdl and handle.get_object_type(
                            ) == "Process":
                                proc_obj = handle.dereference_as("_EPROCESS")
                                break
                        break
                if proc_obj is not None:
                    # If we found the handle to which it referred, update the
                    # corresponding pid
                    pid = int(proc_obj.UniqueProcessId)
                else:
                    return

        # Case when no PID is specified, just dump everything
        if pid is None:
            pids = [p.pid for p in mwmon.data.procs]
        # When one pid is specified, dump that PID
        else:
            pids = [pid]

        eprocs = [
            t for t in tasks.pslist(addr_space) if t.UniqueProcessId in pids
        ]
        for task in eprocs:
            mwmon.printer("Dumping process %x" % (task.UniqueProcessId))
            # Code adapted from procdump (volatility)
            task_space = task.get_process_address_space()
            if task_space is None:
                mwmon.printer("Error: Cannot acquire process AS")
                return
            elif task.Peb is None:
                # we must use m() here, because any other attempt to
                # reference task.Peb will try to instantiate the _PEB
                mwmon.printer(
                    "Error: PEB at {0:#x} is unavailable (possibly due to paging)"
                    .format(task.m('Peb')))
                return
            elif task_space.vtop(task.Peb.ImageBaseAddress) is None:
                mwmon.printer(
                    "Error: ImageBaseAddress at {0:#x} is unavailable" +
                    "(possibly due to paging)".format(
                        task.Peb.ImageBaseAddress))
                return
            else:
                mwmon.printer("Dumping executable for %x" %
                              (task.UniqueProcessId))
                dump_file = os.path.join(
                    path, "executable.%x.exe" % (task.UniqueProcessId))
                of = open(dump_file, 'wb')
                pe_file = obj.Object("_IMAGE_DOS_HEADER",
                                     offset=task.Peb.ImageBaseAddress,
                                     vm=task_space)
                try:
                    for offset, code in pe_file.get_image(unsafe=True,
                                                          memory=False,
                                                          fix=True):
                        of.seek(offset)
                        of.write(code)
                except ValueError, ve:
                    mwmon.printer("Error: {0}".format(ve))
                    return
                except exceptions.SanityCheckException, ve:
                    mwmon.printer("Error: {0} Try -u/--unsafe".format(ve))
                    return
예제 #43
0
def dumper_start_monitoring_process(new_proc):
    '''
    Initialization function called for every new process created.
    '''
    from mw_monitor_classes import mwmon
    from api import CallbackManager

    if mwmon.dumper_onexit:
        dll, fun = ("ntdll.dll", "ZwTerminateProcess")
        # Add a bp to the list of symbol based breakpoints for the process
        new_proc.breakpoints[(dll, fun)] = None
        new_proc.bp_funcs[(dll, fun)] = (
            functools.partial(dump, terminate_process=True), False)
        mwmon.printer("Deferring dumper breakpoint at %s:%s (PGD: %x)" %
                      (dll, fun, new_proc.get_pgd()))

    dump_at = mwmon.dumper_dumpat
    if dump_at is not None:
        # Possible formats for dump_at:
        # 0x00400000
        # user32.dll!CharNextW
        # user32.dll!CharNextW!0x00400000
        terms = dump_at.split("!")
        if len(terms) == 1:
            try:
                addr = int(terms[0], 16)
            except Exception as e:
                mwmon.printer(
                    "Dumper - dump_at: Wrong address value, must specify an hex number")
                return
            cb_name = "dumper_bp_%x_%x" % (new_proc.get_pid(), addr)
            bp = mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB, functools.partial(
                dump, pid=new_proc.pid, callback_name=cb_name), name=cb_name, addr=addr, pgd=new_proc.get_pgd())
            breakpoints.append(bp)
            mwmon.printer("Adding dumper breakpoint at %x (CR3: %x)" %
                          (addr, new_proc.get_pgd()))
        elif len(terms) == 2:
            dll = terms[0]
            fun = terms[1]
            # Add a bp to the list of symbol based breakpoints for the process
            if (dll, fun) in new_proc.breakpoints:
                mwmon.printer(
                    "Cannot set dump callback on standard function %s" % fun)
                return
            new_proc.breakpoints[(dll, fun)] = None
            new_proc.bp_funcs[(dll, fun)] = (dump, False)
            mwmon.printer("Deferring dumper breakpoint at %s:%s (PGD: %x)" %
                          (dll, fun, new_proc.get_pgd()))
        elif len(terms) == 3:
            dll = terms[0]
            fun = terms[1]
            try:
                from_addr = int(terms[2], 16)
            except Exception as e:
                mwmon.printer(
                    "Dumper - dump_at: Wrong address value, must specify an hex number : %s" % str(e))
                return
            # Add a bp to the list of symbol based breakpoints for the process
            if (dll, fun) in new_proc.breakpoints:
                mwmon.printer(
                    "Cannot set dump callback on standard function %s" % fun)
                return
            new_proc.breakpoints[(dll, fun)] = None
            new_proc.bp_funcs[(dll, fun)] = (
                functools.partial(dump, from_addr=from_addr), False)
            mwmon.printer("Deferring dumper breakpoint at %s:%s from %x (PGD: %x)" % (dll,
                                                                                      fun, from_addr,
                                                                                      new_proc.get_pgd()))
        else:
            mwmon.printer("Incorrect format for dumper dump_at parameter. No hook will be created")
예제 #44
0
def ntmapviewofsection(params, pid, proc, update_vads):
    import volatility.obj as obj
    import volatility.win32.tasks as tasks
    import volatility.plugins.overlays.windows.windows as windows
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import Section
    from utils import get_addr_space
    import api
    from api import CallbackManager
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    # IN HANDLE               SectionHandle,
    # IN HANDLE               ProcessHandle,
    # IN OUT PVOID            *BaseAddress OPTIONAL,
    # IN ULONG                ZeroBits OPTIONAL,
    # IN ULONG                CommitSize,
    # IN OUT PLARGE_INTEGER   SectionOffset OPTIONAL,
    # IN OUT PULONG           ViewSize,
    # IN                      InheritDisposition,
    # IN ULONG                AllocationType OPTIONAL,
    # IN ULONG                Protect

    pgd = api.get_running_process(cpu_index)

    # Read the parameters
    ret_addr, section_handle, proc_handle, base_p, arg_3, arg_4, offset_p, size_p = read_parameters(
        cpu, 7)

    # Load volatility address space
    addr_space = get_addr_space(pgd)

    class _SECTION_OBJECT(obj.CType, windows.ExecutiveObjectMixin):

        def is_valid(self):
            return obj.CType.is_valid(self)

    addr_space.profile.object_classes.update(
        {'_SECTION_OBJECT': _SECTION_OBJECT})
    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]
    # Initialize proc_obj, that will point to the object of the referenced
    # process, and section_obj, idem
    proc_obj = None
    section_obj = None
    mapping_proc = None
    if (TARGET_LONG_SIZE == 4 and proc_handle == 0xffffffff) or (TARGET_LONG_SIZE == 8 and proc_handle == 0xffffffffffffffff):
        for proc in mwmon.data.procs:
            if proc.pid == pid:
                mapping_proc = proc
                break
    # Search handle table for the caller process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid():
                    if not mapping_proc and not proc_obj and \
                       handle.HandleValue == proc_handle and \
                       handle.get_object_type() == "Process":
                        proc_obj = handle.dereference_as("_EPROCESS")
                    elif handle.HandleValue == section_handle and handle.get_object_type() == "Section":
                        # We dereference the object as _SECTION_OBJECT, although it is not a _SECTION_OBJECT but a
                        # _SECTION, that is not present in the volatility overlay:
                        # http://forum.sysinternals.com/section-object_topic24975.html
                        # For a better reference see the comments on the Section class
                        # in mw_monitor_classes.py
                        section_obj = handle.dereference_as("_SECTION_OBJECT")
                if (proc_obj or mapping_proc) and section_obj:
                    break
            break
    # proc_obj represents the process over which the section is mapped
    # section_object represents the section being mapped.
    if (proc_obj is not None or mapping_proc is not None) and section_obj is not None:
        mapped_sec = None
        if not mapping_proc:
            for proc in mwmon.data.procs:
                if proc.pid == proc_obj.UniqueProcessId:
                    mapping_proc = proc
                    break
        if mapping_proc is None:
            mwmon.printer("[!] The mapping process is not being monitored," +
                          " a handle was obtained with an API different from " +
                          "OpenProcess or CreateProcess")
            return
        for sec in mwmon.data.sections:
            if sec.get_offset() == section_obj.obj_offset:
                mapped_sec = sec
                break

        # If the section was not in our list, we create an entry
        if mapped_sec is None:
            mapped_sec = Section(pgd, section_obj)
            mwmon.data.sections.append(mapped_sec)

        # Record the actual map once we return back from the call and we can
        # dereference output parameters
        callback_name = mwmon.cm.generate_callback_name("mapviewofsection_ret")
        # Arguments to callback: the callback name, so that it can unset it,
        # the process handle variable, and the section handle

        callback_function = functools.partial(ntmapviewofsection_ret,
                                              pid=pid,
                                              callback_name=callback_name,
                                              mapping_proc=mapping_proc,
                                              mapped_sec=mapped_sec,
                                              base_p=base_p,
                                              size_p=size_p,
                                              offset_p=offset_p,
                                              proc=proc,
                                              update_vads=update_vads)

        mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                              callback_function,
                              name=callback_name,
                              addr=ret_addr,
                              pgd=pgd)
예제 #45
0
                    of.close()

                # Dump every dll.
                mods = dict(
                    (mod.DllBase.v(), mod) for mod in task.get_load_modules())

                # List of covered_ranges contains all the address ranges already dumped, to avoid
                # dumping them as vads.
                covered_ranges = [task.Peb.ImageBaseAddress]

                for mod in mods.values():
                    mod_base = mod.DllBase.v()
                    mod_name = mod.BaseDllName
                    if not task_space.is_valid_address(mod_base):
                        mwmon.printer(
                            "Error: DllBase is unavailable (possibly due to paging)"
                        )
                        continue
                    else:
                        mwmon.printer("Dumping module %s for %x" %
                                      (mod_name, task.UniqueProcessId))
                        dump_file = os.path.join(
                            path, "module.{0:x}.{1:x}.dll".format(
                                task.UniqueProcessId, mod_base))
                        of = open(dump_file, 'wb')
                        pe_file = obj.Object("_IMAGE_DOS_HEADER",
                                             offset=mod_base,
                                             vm=task_space)
                        covered_ranges.append(mod_base)
                        try:
                            for offset, code in pe_file.get_image(unsafe=True,
예제 #46
0
def ntreadfile(params, pid, proc, update_vads, is_write=False):
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import FileRead
    from mw_monitor_classes import FileWrite
    from mw_monitor_classes import File
    from utils import get_addr_space
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    # IN HANDLE   FileHandle,
    # IN HANDLE Event     OPTIONAL,
    # IN PIO_APC_ROUTINE ApcRoutine   OPTIONAL,
    # IN PVOID ApcContext     OPTIONAL,
    # OUT PIO_STATUS_BLOCK    IoStatusBlock,
    # OUT PVOID   Buffer,
    # IN ULONG    Length,
    # IN PLARGE_INTEGER ByteOffset    OPTIONAL,
    # IN PULONG Key   OPTIONAL

    pgd = api.get_running_process(cpu_index)

    # Read the parameters
    ret_addr, file_handle, arg2, arg3, arg4, arg5, buff, length, offset_p, arg9 = read_parameters(
        cpu, 9)

    # Load volatility address space
    addr_space = get_addr_space(pgd)

    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]

    # Initialize file_obj, that will point to the object of the referenced file
    file_obj = None

    # Search handle table for the new created process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid() and handle.HandleValue == file_handle and handle.get_object_type() == "File":
                    file_obj = handle.dereference_as("_FILE_OBJECT")
                    break
            break

    if file_obj is not None:
        file_instance = None
        for fi in mwmon.data.files:
            if fi.file_name == str(file_obj.FileName):
                file_instance = fi
                break

        # If we have still not recorded the file, add it to files to record
        if file_instance is None:
            file_instance = File(str(file_obj.FileName))
            mwmon.data.files.append(file_instance)
        # Now, record the read/write
        # curr_file_offset is never used
        # curr_file_offset = int(file_obj.CurrentByteOffset.QuadPart)
        # FO_SYNCHRONOUS_IO     0x0000002
        is_offset_maintained = ((file_obj.Flags & 0x0000002) != 0)

        # If no offset was specified, and the offset is mantained, the real
        # offset is taken from the file object
        offset = None
        if offset_p == 0 and is_offset_maintained:
            offset = int(file_obj.CurrentByteOffset.QuadPart)
        elif offset_p != 0:
            # If an offset is provided, the current offset in the file_object
            # will be updated, regardless of the flag.
            try:
                offset = struct.unpack("Q", api.r_va(pgd, offset_p, 8))[0]
            except:
                offset = 0
                mwmon.printer("Could not dereference offset in NtReadFile call in interproc.py")
        else:
            # If no offset was specified and the file object does not have the flag set, we may be in front of some kind
            # of corruption error or deliberate manipulation
            print "[!] The file object flag FO_SYNCHRONOUS_IO is not set, and no offset was provided"
            return

        # At this moment we do not record the data
        op = None

        for proc in mwmon.data.procs:
            if proc.pid == pid:
                local_proc = proc
                break

        if not is_write:
            op = FileRead(file_instance, local_proc, offset, length, None)
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write("[PID: %x] NtReadFile: Offset: %08x Size: %08x / %s\n" %
                            (pid, offset, length, str(file_obj.FileName)))
                elif TARGET_LONG_SIZE == 8:
                    f.write("[PID: %x] NtReadFile: Offset: %16x Size: %16x / %s\n" %
                            (pid, offset, length, str(file_obj.FileName)))
        else:
            op = FileWrite(file_instance, local_proc, offset, length, None)
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write("[PID: %x] NtWriteFile: Offset: %08x Size: %08x / %s\n" %
                            (pid, offset, length, str(file_obj.FileName)))
                elif TARGET_LONG_SIZE == 8:
                    f.write("[PID: %x] NtWriteFile: Offset: %16x Size: %16x / %s\n" %
                            (pid, offset, length, str(file_obj.FileName)))

        file_instance.add_operation(op)
        local_proc.file_operations.append(op)

    if update_vads:
        proc.update_vads()
예제 #47
0
def ntwritevirtualmemory(params, pid, proc, update_vads, reverse=False):
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import Injection
    import api
    from utils import get_addr_space

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    # _In_ HANDLE     ProcessHandle,
    # _In_ PVOID  BaseAddress,
    # _In_ PVOID  Buffer,
    # _In_ SIZE_T     NumberOfBytesToWrite,
    # _Out_opt_ PSIZE_T   NumberOfBytesWritten

    pgd = api.get_running_process(cpu_index)

    # Read the parameters
    ret_addr, proc_hdl, remote_addr, local_addr, size, size_out = read_parameters(
        cpu, 5)

    local_proc = None
    # Get local proc
    for proc in mwmon.data.procs:
        if proc.pid == pid:
            local_proc = proc
            break
    # Try to get remote process from list of monitored processes
    remote_proc = None
    # Load volatility address space
    addr_space = get_addr_space(pgd)

    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]

    # Initialize proc_obj, that will point to the eprocess of the new created
    # process
    proc_obj = None

    # Search handle table for the new created process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid() and handle.HandleValue == proc_hdl and handle.get_object_type() == "Process":
                    proc_obj = handle.dereference_as("_EPROCESS")
                    break
            break
    if proc_obj is not None:
        for proc in mwmon.data.procs:
            if proc.pid == proc_obj.UniqueProcessId:
                remote_proc = proc
                break
    else:
        # Sometimes we get calls to this function over non-proc handles (e.g. type "Desktop")
        return
    if remote_proc is None:
        mwmon.printer(
            "[!]  Could not obtain remote proc, or it is not being monitored")
        return
    elif local_proc is None:
        mwmon.printer(
            "[!]  Could not obtain local proc, or it is not being monitored")
        return
    else:
        if reverse:
            data = None
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write("[PID: %x] NtReadVirtualMemory: PID: %x - Addr: %08x <-- PID: %x Addr: %08x / Size: %08x\n" %
                            (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size))
                elif TARGET_LONG_SIZE == 8:
                    f.write("[PID: %x] NtReadVirtualMemory: PID: %x - Addr: %16x <-- PID: %x Addr: %16x / Size: %16x\n" %
                            (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size))
        else:
            data = None
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write("[PID: %x] NtWriteVirtualMemory: PID: %x - Addr: %08x --> PID: %x Addr: %08x / Size: %08x\n" %
                            (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size))
                elif TARGET_LONG_SIZE == 8:
                    f.write("[PID: %x] NtWriteVirtualMemory: PID: %x - Addr: %16x --> PID: %x Addr: %16x / Size: %16x\n" %
                            (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size))

        inj = Injection(remote_proc, remote_addr,
                        local_proc, local_addr, size, data, reverse)
        local_proc.add_injection(inj)

    if update_vads:
        proc.update_vads()
예제 #48
0
def initialize_callbacks(module_hdl, printer):
    '''
    Initilize callbacks for this module. This function
    will be triggered whenever import_module command
    is triggered.
    '''
    from mw_monitor_classes import Process
    from mw_monitor_classes import mwmon
    import dumper
    import api
    from ipython_shell import add_command
    from plugins.guest_agent import guest_agent

    # Update printer
    mwmon.printer = printer
    # Read configuration
    mwmon.printer("Reading mw_monitor configuration...")
    # Config parser for main static configuration file
    config = ConfigParser.RawConfigParser()
    config.read('mw_monitor.conf')

    # Read run configuration from json file
    f = open("mw_monitor_run.json","r")
    config_run = json.load(f)
    f.close()

    # GENERAL CONFIGURATION
    if "files_path" not in config_run["general"] or \
       "main_executable" not in config_run["general"] or \
       "files_bundle" not in config_run["general"]:
        raise ValueError("File to run not properly specified")

    mwmon.output_bundle_name = config.get('general', 'output_bundle')
    mwmon.files_path = config_run['general']['files_path']
    mwmon.main_executable = config_run['general']['main_executable']
    mwmon.files_bundle = config_run['general']['files_bundle']
    mwmon.api_database_path = config.get('general', 'api_database')

    # Set up process copy and execution
    mwmon.printer("Copying host file to guest, using agent...")

    #Extract files in a temporary directory
    extracted_files_path = tempfile.mkdtemp()
    mwmon.extracted_files_path = extracted_files_path
    zip_ref = zipfile.ZipFile(mwmon.files_bundle, 'r')
    zip_ref.extractall(extracted_files_path)
    zip_ref.close()
    onlyfiles = [f for f in os.listdir(extracted_files_path) if os.path.isfile(os.path.join(extracted_files_path, f))]

    #Copy the files to the guest
    for f in onlyfiles:
        guest_agent.copy_file(os.path.join(extracted_files_path,f),os.path.join(mwmon.files_path,f))

    ex_path = str(ntpath.join(mwmon.files_path,mwmon.main_executable))
    # Instruct file execution
    guest_agent.execute_file(ex_path)

    # Stop agent
    # guest_agent.stop_agent()

    # MODULE CONFIGURATION
    mwmon.api_tracer = config_run['modules']['api_tracer']
    mwmon.interproc = config_run['modules']['interproc']
    mwmon.coverage = config_run['modules']['coverage']
    mwmon.dumper = config_run['modules']['dumper']

    # API TRACER CONFIGURATION
    if mwmon.api_tracer and "api_tracer" in config_run:
        # Static config
        mwmon.api_tracer_text_log_name = config.get(
            'api_tracer', 'text_log_name')
        mwmon.api_tracer_bin_log_name = config.get(
            'api_tracer', 'bin_log_name')

        # Run config
        mwmon.api_tracer_light_mode = config_run['api_tracer']['light_mode']
        mwmon.api_tracer_text_log = config_run['api_tracer']['text_log']
        mwmon.api_tracer_bin_log = config_run['api_tracer']['bin_log']

        if "include_apis" in config_run["api_tracer"]:
            mwmon.include_apis = []
            mwmon.include_apis_addrs = []
            for api_call in config_run["api_tracer"]["include_apis"]:
                try:
                    mod, fun = api_call.split("!")
                    mwmon.include_apis.append((mod.lower(), fun.lower()))
                except Exception:
                    # Just pass over the malformed api names
                    pass
        else:
            mwmon.include_apis = None

        if "exclude_apis" in config_run["api_tracer"]:
            mwmon.exclude_apis = []
            mwmon.exclude_apis_addrs = []
            for api_call in config_run["api_tracer"]["exclude_apis"]:
                try:
                    mod, fun = api_call.split("!")
                    mwmon.exclude_apis.append((mod.lower(), fun.lower()))
                except Exception:
                    # Just pass over the malformed api names
                    pass
        else:
            mwmon.excludeapis = None

        if "procs" in config_run["api_tracer"]:
            mwmon.api_tracer_procs = config_run["api_tracer"]["procs"]
        else:
            mwmon.api_tracer_procs = None

        if "exclude_modules" in config_run["api_tracer"]:
            mwmon.exclude_modules_addrs = []
            mwmon.exclude_modules = [s.lower() for s in config_run["api_tracer"]["exclude_modules"]]
        else:
            mwmon.exclude_modules = None

        if "exclude_origin_modules" in config_run["api_tracer"]:
            mwmon.exclude_origin_modules_addrs = []
            mwmon.exclude_origin_modules = [s.lower() for s in config_run["api_tracer"]["exclude_origin_modules"]]
        else:
            mwmon.exclude_origin_modules = None
            mwmon.exclude_origin_modules_addrs = None


    # interproc configuration 
    if mwmon.interproc:
        # Static config
        mwmon.interproc_bin_log_name = config.get('interproc', 'bin_log_name')
        mwmon.interproc_text_log_name = config.get(
            'interproc', 'text_log_name')
        mwmon.interproc_basic_stats_name = config.get(
            'interproc', 'basic_stats_name')
        # Run config
        mwmon.interproc_bin_log = config_run['interproc']['bin_log']
        mwmon.interproc_text_log = config_run['interproc']['text_log']
        mwmon.interproc_basic_stats = config_run['interproc']['basic_stats']
        if mwmon.interproc_text_log:
            mwmon.interproc_text_log_handle = open(
                mwmon.interproc_text_log_name, "w")

    if mwmon.coverage:
        # Static config
        mwmon.coverage_log_name = config.get('coverage', 'cov_log_name')
        mwmon.coverage_text_name = config.get('coverage', 'cov_text_name')
        # Run config
        if "procs" in config_run["coverage"]:
            mwmon.coverage_procs = config_run["coverage"]["procs"]
        else:
            mwmon.coverage_procs = None

    # Static config
    mwmon.dumper_path = config.get('dumper', 'path')

    # DUMPER CONFIGURATION
    if mwmon.dumper:
        if os.path.isdir(mwmon.dumper_path):
            shutil.rmtree(mwmon.dumper_path)
        os.makedirs(mwmon.dumper_path)

        # Run config
        mwmon.dumper_onexit = config_run['dumper']['dump_on_exit']
        # Possible formats for dump_at:
        # 0x00400000
        # user32.dll!CharNextW
        # user32.dll!CharNextW!0x00400000
        if "dump_at" in config_run["dumper"]:
            mwmon.dumper_dumpat = config_run['dumper']['dump_at']

    mwmon.printer("Initializing callbacks")
    mwmon.cm = CallbackManager(module_hdl, new_style = True)

    # Initialize first process
    proc_name = mwmon.main_executable
    mwmon.data.procs = [Process(proc_name)]

    procs = api.get_process_list()
    match_procs = []
    for proc in procs:
        name = proc["name"]
        pid = proc["pid"]
        pgd = proc["pgd"]
        if proc_name is not None and (proc_name in name or name in proc_name):
            match_procs.append((pid, pgd, name))

    if len(match_procs) == 0:
        mwmon.printer(
            "No process matching that process name, deferring process detection")
        mwmon.printer("Initializing process creation callback")
        # Monitor creation of new processes, to start tracing the first one.
        mwmon.cm.add_callback(
            CallbackManager.CREATEPROC_CB, new_process, name="vmi_new_proc")
    elif len(match_procs) == 1:
        mwmon.printer(
            "Process found with the name specified, monitoring process...")
        new_process({"pid": match_procs[0][0], "pgd": match_procs[0][1], "name": match_procs[0][2]})
    else:
        mwmon.printer(
            "Too many procs matching that name, please narrow down!!")

    if mwmon.dumper:
        mwmon.printer("Adding dumper commands")
        # Create and activate new command (dump_mwmon)
        add_command("dump_mwmon", dumper.dump_command)

    # Add a callback on process remove, to know when 
    # we dont have any monitored process left.
    mwmon.cm.add_callback(
        CallbackManager.REMOVEPROC_CB, remove_process, name="mwmon_vmi_remove_proc")


    mwmon.printer("Initialized callbacks")
예제 #49
0
def ntopenprocessret(params, pid, callback_name, proc_hdl_p, proc,
                     update_vads):
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import mw_monitor_start_monitoring_process
    from mw_monitor_classes import Process
    from api import get_running_process
    from utils import get_addr_space
    import api

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    pgd = get_running_process(cpu_index)

    # First, remove callback
    mwmon.cm.rm_callback(callback_name)

    # Do not continue if EAX/RAX returns and invalid return code.
    if read_return_parameter(cpu) != 0:
        return

    # Load volatility address space
    addr_space = get_addr_space(pgd)

    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]

    # Initialize proc_obj, that will point to the eprocess of the new created
    # process
    proc_obj = None

    # Dereference the output argument containing the hdl of the newly created
    # process
    proc_hdl = dereference_target_long(proc_hdl_p, pgd)

    # Search handle table for the new created process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid(
                ) and handle.HandleValue == proc_hdl and handle.get_object_type(
                ) == "Process":
                    proc_obj = handle.dereference_as("_EPROCESS")
                    break
            break

    if proc_obj is not None:
        if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
            f = mwmon.interproc_text_log_handle
            f.write("[PID: %x] NtOpenProcess: %s - PID: %x - CR3: %x\n" %
                    (pid, str(
                        proc_obj.ImageFileName), int(proc_obj.UniqueProcessId),
                     int(proc_obj.Pcb.DirectoryTableBase.v())))

        # Check if we are already monitoring the process
        for proc in mwmon.data.procs:
            if proc.pid == int(proc_obj.UniqueProcessId):
                return
        new_proc = Process(str(proc_obj.ImageFileName))
        new_proc.set_pid(int(proc_obj.UniqueProcessId))
        new_proc.set_pgd(int(proc_obj.Pcb.DirectoryTableBase.v()))
        mw_monitor_start_monitoring_process(new_proc)
    else:
        if TARGET_LONG_SIZE == 4:
            mwmon.printer(
                "Error while trying to retrieve EPROCESS for handle %x, PID %x, EAX: %x"
                % (proc_hdl, pid, cpu.EAX))
        elif TARGET_LONG_SIZE == 8:
            mwmon.printer(
                "Error while trying to retrieve EPROCESS for handle %x, PID %x, EAX: %x"
                % (proc_hdl, pid, cpu.RAX))

    if update_vads:
        proc.update_vads()

    return
예제 #50
0
def initialize_callbacks(module_hdl, printer):
    '''
    Initilize callbacks for this module. This function
    will be triggered whenever import_module command
    is triggered.
    '''
    from mw_monitor_classes import Process
    from mw_monitor_classes import mwmon
    import dumper
    import api
    from ipython_shell import add_command
    from plugins.guest_agent import guest_agent

    # Update printer
    mwmon.printer = printer
    # Read configuration
    mwmon.printer("Reading mw_monitor configuration...")
    # Config parser for main static configuration file
    config = ConfigParser.RawConfigParser()
    config.read('mw_monitor.conf')

    # Read run configuration from json file
    f = open("mw_monitor_run.json", "r")
    config_run = json.load(f)
    f.close()

    # GENERAL CONFIGURATION
    if "files_path" not in config_run["general"] or \
       "main_executable" not in config_run["general"] or \
       "files_bundle" not in config_run["general"]:
        raise ValueError("File to run not properly specified")

    mwmon.output_bundle_name = config.get('general', 'output_bundle')
    mwmon.files_path = config_run['general']['files_path']
    mwmon.main_executable = config_run['general']['main_executable']
    mwmon.files_bundle = config_run['general']['files_bundle']
    mwmon.api_database_path = config.get('general', 'api_database')

    # Set up process copy and execution
    mwmon.printer("Copying host file to guest, using agent...")

    #Extract files in a temporary directory
    extracted_files_path = tempfile.mkdtemp()
    mwmon.extracted_files_path = extracted_files_path
    zip_ref = zipfile.ZipFile(mwmon.files_bundle, 'r')
    zip_ref.extractall(extracted_files_path)
    zip_ref.close()
    onlyfiles = [
        f for f in os.listdir(extracted_files_path)
        if os.path.isfile(os.path.join(extracted_files_path, f))
    ]

    #Copy the files to the guest
    for f in onlyfiles:
        guest_agent.copy_file(os.path.join(extracted_files_path, f),
                              os.path.join(mwmon.files_path, f))

    ex_path = str(ntpath.join(mwmon.files_path, mwmon.main_executable))
    # Instruct file execution
    guest_agent.execute_file(ex_path)

    # Stop agent
    # guest_agent.stop_agent()

    # MODULE CONFIGURATION
    mwmon.api_tracer = config_run['modules']['api_tracer']
    mwmon.interproc = config_run['modules']['interproc']
    mwmon.coverage = config_run['modules']['coverage']
    mwmon.dumper = config_run['modules']['dumper']

    # API TRACER CONFIGURATION
    if mwmon.api_tracer and "api_tracer" in config_run:
        # Static config
        mwmon.api_tracer_text_log_name = config.get('api_tracer',
                                                    'text_log_name')
        mwmon.api_tracer_bin_log_name = config.get('api_tracer',
                                                   'bin_log_name')

        # Run config
        mwmon.api_tracer_light_mode = config_run['api_tracer']['light_mode']
        mwmon.api_tracer_text_log = config_run['api_tracer']['text_log']
        mwmon.api_tracer_bin_log = config_run['api_tracer']['bin_log']

        if "include_apis" in config_run["api_tracer"]:
            mwmon.include_apis = []
            mwmon.include_apis_addrs = []
            for api_call in config_run["api_tracer"]["include_apis"]:
                try:
                    mod, fun = api_call.split("!")
                    mwmon.include_apis.append((mod.lower(), fun.lower()))
                except Exception:
                    # Just pass over the malformed api names
                    pass
        else:
            mwmon.include_apis = None

        if "exclude_apis" in config_run["api_tracer"]:
            mwmon.exclude_apis = []
            mwmon.exclude_apis_addrs = []
            for api_call in config_run["api_tracer"]["exclude_apis"]:
                try:
                    mod, fun = api_call.split("!")
                    mwmon.exclude_apis.append((mod.lower(), fun.lower()))
                except Exception:
                    # Just pass over the malformed api names
                    pass
        else:
            mwmon.excludeapis = None

        if "procs" in config_run["api_tracer"]:
            mwmon.api_tracer_procs = config_run["api_tracer"]["procs"]
        else:
            mwmon.api_tracer_procs = None

        if "exclude_modules" in config_run["api_tracer"]:
            mwmon.exclude_modules_addrs = []
            mwmon.exclude_modules = [
                s.lower() for s in config_run["api_tracer"]["exclude_modules"]
            ]
        else:
            mwmon.exclude_modules = None

        if "exclude_origin_modules" in config_run["api_tracer"]:
            mwmon.exclude_origin_modules_addrs = []
            mwmon.exclude_origin_modules = [
                s.lower()
                for s in config_run["api_tracer"]["exclude_origin_modules"]
            ]
        else:
            mwmon.exclude_origin_modules = None
            mwmon.exclude_origin_modules_addrs = None

    # interproc configuration
    if mwmon.interproc:
        # Static config
        mwmon.interproc_bin_log_name = config.get('interproc', 'bin_log_name')
        mwmon.interproc_text_log_name = config.get('interproc',
                                                   'text_log_name')
        mwmon.interproc_basic_stats_name = config.get('interproc',
                                                      'basic_stats_name')
        # Run config
        mwmon.interproc_bin_log = config_run['interproc']['bin_log']
        mwmon.interproc_text_log = config_run['interproc']['text_log']
        mwmon.interproc_basic_stats = config_run['interproc']['basic_stats']
        if mwmon.interproc_text_log:
            mwmon.interproc_text_log_handle = open(
                mwmon.interproc_text_log_name, "w")

    if mwmon.coverage:
        # Static config
        mwmon.coverage_log_name = config.get('coverage', 'cov_log_name')
        mwmon.coverage_text_name = config.get('coverage', 'cov_text_name')
        # Run config
        if "procs" in config_run["coverage"]:
            mwmon.coverage_procs = config_run["coverage"]["procs"]
        else:
            mwmon.coverage_procs = None

    # Static config
    mwmon.dumper_path = config.get('dumper', 'path')

    # DUMPER CONFIGURATION
    if mwmon.dumper:
        if os.path.isdir(mwmon.dumper_path):
            shutil.rmtree(mwmon.dumper_path)
        os.makedirs(mwmon.dumper_path)

        # Run config
        mwmon.dumper_onexit = config_run['dumper']['dump_on_exit']
        # Possible formats for dump_at:
        # 0x00400000
        # user32.dll!CharNextW
        # user32.dll!CharNextW!0x00400000
        if "dump_at" in config_run["dumper"]:
            mwmon.dumper_dumpat = config_run['dumper']['dump_at']

    mwmon.printer("Initializing callbacks")
    mwmon.cm = CallbackManager(module_hdl, new_style=True)

    # Initialize first process
    proc_name = mwmon.main_executable
    mwmon.data.procs = [Process(proc_name)]

    procs = api.get_process_list()
    match_procs = []
    for proc in procs:
        name = proc["name"]
        pid = proc["pid"]
        pgd = proc["pgd"]
        if proc_name is not None and (proc_name in name or name in proc_name):
            match_procs.append((pid, pgd, name))

    if len(match_procs) == 0:
        mwmon.printer(
            "No process matching that process name, deferring process detection"
        )
        mwmon.printer("Initializing process creation callback")
        # Monitor creation of new processes, to start tracing the first one.
        mwmon.cm.add_callback(CallbackManager.CREATEPROC_CB,
                              new_process,
                              name="vmi_new_proc")
    elif len(match_procs) == 1:
        mwmon.printer(
            "Process found with the name specified, monitoring process...")
        new_process({
            "pid": match_procs[0][0],
            "pgd": match_procs[0][1],
            "name": match_procs[0][2]
        })
    else:
        mwmon.printer(
            "Too many procs matching that name, please narrow down!!")

    if mwmon.dumper:
        mwmon.printer("Adding dumper commands")
        # Create and activate new command (dump_mwmon)
        add_command("dump_mwmon", dumper.dump_command)

    # Add a callback on process remove, to know when
    # we dont have any monitored process left.
    mwmon.cm.add_callback(CallbackManager.REMOVEPROC_CB,
                          remove_process,
                          name="mwmon_vmi_remove_proc")

    mwmon.printer("Initialized callbacks")
예제 #51
0
def ntreadfile(params, pid, proc, update_vads, is_write=False):
    import volatility.win32.tasks as tasks
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import FileRead
    from mw_monitor_classes import FileWrite
    from mw_monitor_classes import File
    from utils import get_addr_space
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    # IN HANDLE   FileHandle,
    # IN HANDLE Event     OPTIONAL,
    # IN PIO_APC_ROUTINE ApcRoutine   OPTIONAL,
    # IN PVOID ApcContext     OPTIONAL,
    # OUT PIO_STATUS_BLOCK    IoStatusBlock,
    # OUT PVOID   Buffer,
    # IN ULONG    Length,
    # IN PLARGE_INTEGER ByteOffset    OPTIONAL,
    # IN PULONG Key   OPTIONAL

    pgd = api.get_running_process(cpu_index)

    # Read the parameters
    ret_addr, file_handle, arg2, arg3, arg4, arg5, buff, length, offset_p, arg9 = read_parameters(
        cpu, 9)

    # Load volatility address space
    addr_space = get_addr_space(pgd)

    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]

    # Initialize file_obj, that will point to the object of the referenced file
    file_obj = None

    # Search handle table for the new created process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid(
                ) and handle.HandleValue == file_handle and handle.get_object_type(
                ) == "File":
                    file_obj = handle.dereference_as("_FILE_OBJECT")
                    break
            break

    if file_obj is not None:
        file_instance = None
        for fi in mwmon.data.files:
            if fi.file_name == str(file_obj.FileName):
                file_instance = fi
                break

        # If we have still not recorded the file, add it to files to record
        if file_instance is None:
            file_instance = File(str(file_obj.FileName))
            mwmon.data.files.append(file_instance)
        # Now, record the read/write
        # curr_file_offset is never used
        # curr_file_offset = int(file_obj.CurrentByteOffset.QuadPart)
        # FO_SYNCHRONOUS_IO     0x0000002
        is_offset_maintained = ((file_obj.Flags & 0x0000002) != 0)

        # If no offset was specified, and the offset is mantained, the real
        # offset is taken from the file object
        offset = None
        if offset_p == 0 and is_offset_maintained:
            offset = int(file_obj.CurrentByteOffset.QuadPart)
        elif offset_p != 0:
            # If an offset is provided, the current offset in the file_object
            # will be updated, regardless of the flag.
            try:
                offset = struct.unpack("Q", api.r_va(pgd, offset_p, 8))[0]
            except:
                offset = 0
                mwmon.printer(
                    "Could not dereference offset in NtReadFile call in interproc.py"
                )
        else:
            # If no offset was specified and the file object does not have the flag set, we may be in front of some kind
            # of corruption error or deliberate manipulation
            print "[!] The file object flag FO_SYNCHRONOUS_IO is not set, and no offset was provided"
            return

        # At this moment we do not record the data
        op = None

        for proc in mwmon.data.procs:
            if proc.pid == pid:
                local_proc = proc
                break

        if not is_write:
            op = FileRead(file_instance, local_proc, offset, length, None)
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write(
                        "[PID: %x] NtReadFile: Offset: %08x Size: %08x / %s\n"
                        % (pid, offset, length, str(file_obj.FileName)))
                elif TARGET_LONG_SIZE == 8:
                    f.write(
                        "[PID: %x] NtReadFile: Offset: %16x Size: %16x / %s\n"
                        % (pid, offset, length, str(file_obj.FileName)))
        else:
            op = FileWrite(file_instance, local_proc, offset, length, None)
            if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None:
                f = mwmon.interproc_text_log_handle
                if TARGET_LONG_SIZE == 4:
                    f.write(
                        "[PID: %x] NtWriteFile: Offset: %08x Size: %08x / %s\n"
                        % (pid, offset, length, str(file_obj.FileName)))
                elif TARGET_LONG_SIZE == 8:
                    f.write(
                        "[PID: %x] NtWriteFile: Offset: %16x Size: %16x / %s\n"
                        % (pid, offset, length, str(file_obj.FileName)))

        file_instance.add_operation(op)
        local_proc.file_operations.append(op)

    if update_vads:
        proc.update_vads()
예제 #52
0
def opcodes(params, db, proc):
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import is_in_pending_resolution
    from api import CallbackManager
    import api
    from DeviareDbParser import ArgumentParser

    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]
    pc = params["cur_pc"]
    next_pc = params["next_pc"]

    # First, check if the next_pc is located in a module with
    # pending symbol resolution, and update symbols
    # accordingly
    if is_in_pending_resolution(proc.get_pgd(), next_pc):
        proc.update_symbols()

    try:
        # Locate nearest lower symbol
        sym = proc.locate_nearest_symbol(next_pc)
        if sym is None:
            return
        mod = sym.get_mod()
        fun = sym.get_fun()
        real_api_addr = sym.get_addr()
        # Reduce FP's by checking that the origin EIP is not also within the
        # same module (code reuse inside the dll)
        if mod in proc.modules:
            for (base, size) in proc.modules[mod]:
                if pc >= base and pc <= (base + size):
                    return

        # First shortcut: check if it is an excluded api/module, or included:
        if mwmon.exclude_apis_addrs is not None and len(
                mwmon.exclude_apis_addrs) > 0:
            if real_api_addr in mwmon.exclude_apis_addrs:
                return

        if mwmon.exclude_modules_addrs is not None and len(
                mwmon.exclude_modules_addrs) > 0:
            for (base, size) in mwmon.exclude_modules_addrs:
                if real_api_addr >= base and real_api_addr < (base + size):
                    return

        # Origin modules
        if mwmon.exclude_origin_modules_addrs is not None and len(
                mwmon.exclude_origin_modules_addrs) > 0:
            # pc is the originating pc
            for (base, size) in mwmon.exclude_origin_modules_addrs:
                if pc >= base and pc < (base + size):
                    return

        if mwmon.include_apis_addrs is not None and len(
                mwmon.include_apis_addrs) > 0:
            if real_api_addr not in mwmon.include_apis_addrs:
                return

        if proc.in_mod_boundaries(real_api_addr):

            pgd = api.get_running_process(cpu_index)

            # Set callback on return address
            if TARGET_LONG_SIZE == 4:
                try:
                    ret_addr_val = api.r_va(pgd, cpu.ESP, 4)
                    ret_addr = struct.unpack("<I", ret_addr_val)[0]
                except:
                    ret_addr = 0
                    mwmon.printer(
                        "Could not read return address on API tracer")
            elif TARGET_LONG_SIZE == 8:
                try:
                    ret_addr_val = api.r_va(pgd, cpu.RSP, 8)
                    ret_addr = struct.unpack("<Q", ret_addr_val)[0]
                except:
                    ret_addr = 0
                    mwmon.printer(
                        "Could not read return address on API tracer")

            if mwmon.api_tracer_light_mode:
                if real_api_addr == next_pc:
                    if TARGET_LONG_SIZE == 4:
                        proc.add_call(
                            pc, real_api_addr,
                            "[PID: %x] %08x --> %s:%s(%08x) --> %08x\n" %
                            (proc.pid, pc, mod, fun, real_api_addr, ret_addr))
                    elif TARGET_LONG_SIZE == 8:
                        proc.add_call(
                            pc, real_api_addr,
                            "[PID: %x] %016x --> %s:%s(%016x) --> %016x\n" %
                            (proc.pid, pc, mod, fun, real_api_addr, ret_addr))
                else:
                    if TARGET_LONG_SIZE == 4:
                        proc.add_call(
                            pc, real_api_addr,
                            "[PID: %x] %08x --> %s:%s(+%x)(%08x) --> %08x\n" %
                            (proc.pid, pc, mod, fun,
                             (next_pc - real_api_addr), next_pc, ret_addr))
                    elif TARGET_LONG_SIZE == 8:
                        proc.add_call(
                            pc, real_api_addr,
                            "[PID: %x] %016x --> %s:%s(+%x)(%016x) --> %016x\n"
                            % (proc.pid, pc, mod, fun,
                               (next_pc - real_api_addr), next_pc, ret_addr))
                return

            data = APICallData()
            data.pc = pc
            data.mod = mod
            data.fun = fun
            data.ret_addr = ret_addr

            if TARGET_LONG_SIZE == 4:
                argument_parser = ArgumentParser(db, cpu, cpu.ESP, mod, fun)
            elif TARGET_LONG_SIZE == 8:
                argument_parser = ArgumentParser(db, cpu, cpu.RSP, mod, fun)

            if not argument_parser.in_db():
                return

            data.in_args = [arg for arg in argument_parser.get_in_args()]

            # If return address could not be read, we skip the callback
            if ret_addr != 0:
                callback_name = "ret_bp_%d" % mwmon.bp_counter

                callback = functools.partial(opcodes_ret, pc, real_api_addr,
                                             data, callback_name,
                                             argument_parser, mod, fun, proc)

                mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                                      callback,
                                      name=callback_name,
                                      addr=data.ret_addr,
                                      pgd=pgd)

                mwmon.bp_counter += 1

    except Exception as e:
        mwmon.printer(str(e))
        traceback.print_exc()
    finally:
        return
예제 #53
0
def ntmapviewofsection(params, pid, proc, update_vads):
    import volatility.obj as obj
    import volatility.win32.tasks as tasks
    import volatility.plugins.overlays.windows.windows as windows
    from mw_monitor_classes import mwmon
    from mw_monitor_classes import Section
    from utils import get_addr_space
    import api
    from api import CallbackManager
    TARGET_LONG_SIZE = api.get_os_bits() / 8

    cpu_index = params["cpu_index"]
    cpu = params["cpu"]

    # IN HANDLE               SectionHandle,
    # IN HANDLE               ProcessHandle,
    # IN OUT PVOID            *BaseAddress OPTIONAL,
    # IN ULONG                ZeroBits OPTIONAL,
    # IN ULONG                CommitSize,
    # IN OUT PLARGE_INTEGER   SectionOffset OPTIONAL,
    # IN OUT PULONG           ViewSize,
    # IN                      InheritDisposition,
    # IN ULONG                AllocationType OPTIONAL,
    # IN ULONG                Protect

    pgd = api.get_running_process(cpu_index)

    # Read the parameters
    ret_addr, section_handle, proc_handle, base_p, arg_3, arg_4, offset_p, size_p = read_parameters(
        cpu, 7)

    # Load volatility address space
    addr_space = get_addr_space(pgd)

    class _SECTION_OBJECT(obj.CType, windows.ExecutiveObjectMixin):
        def is_valid(self):
            return obj.CType.is_valid(self)

    addr_space.profile.object_classes.update(
        {'_SECTION_OBJECT': _SECTION_OBJECT})
    # Get list of processes, and filter out by the process that triggered the
    # call (current process id)
    eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid]
    # Initialize proc_obj, that will point to the object of the referenced
    # process, and section_obj, idem
    proc_obj = None
    section_obj = None
    mapping_proc = None
    if (TARGET_LONG_SIZE == 4 and proc_handle == 0xffffffff) or (
            TARGET_LONG_SIZE == 8 and proc_handle == 0xffffffffffffffff):
        for proc in mwmon.data.procs:
            if proc.pid == pid:
                mapping_proc = proc
                break
    # Search handle table for the caller process
    for task in eprocs:
        if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList:
            for handle in task.ObjectTable.handles():
                if handle.is_valid():
                    if not mapping_proc and not proc_obj and \
                       handle.HandleValue == proc_handle and \
                       handle.get_object_type() == "Process":
                        proc_obj = handle.dereference_as("_EPROCESS")
                    elif handle.HandleValue == section_handle and handle.get_object_type(
                    ) == "Section":
                        # We dereference the object as _SECTION_OBJECT, although it is not a _SECTION_OBJECT but a
                        # _SECTION, that is not present in the volatility overlay:
                        # http://forum.sysinternals.com/section-object_topic24975.html
                        # For a better reference see the comments on the Section class
                        # in mw_monitor_classes.py
                        section_obj = handle.dereference_as("_SECTION_OBJECT")
                if (proc_obj or mapping_proc) and section_obj:
                    break
            break
    # proc_obj represents the process over which the section is mapped
    # section_object represents the section being mapped.
    if (proc_obj is not None
            or mapping_proc is not None) and section_obj is not None:
        mapped_sec = None
        if not mapping_proc:
            for proc in mwmon.data.procs:
                if proc.pid == proc_obj.UniqueProcessId:
                    mapping_proc = proc
                    break
        if mapping_proc is None:
            mwmon.printer(
                "[!] The mapping process is not being monitored," +
                " a handle was obtained with an API different from " +
                "OpenProcess or CreateProcess")
            return
        for sec in mwmon.data.sections:
            if sec.get_offset() == section_obj.obj_offset:
                mapped_sec = sec
                break

        # If the section was not in our list, we create an entry
        if mapped_sec is None:
            mapped_sec = Section(pgd, section_obj)
            mwmon.data.sections.append(mapped_sec)

        # Record the actual map once we return back from the call and we can
        # dereference output parameters
        callback_name = mwmon.cm.generate_callback_name("mapviewofsection_ret")
        # Arguments to callback: the callback name, so that it can unset it,
        # the process handle variable, and the section handle

        callback_function = functools.partial(ntmapviewofsection_ret,
                                              pid=pid,
                                              callback_name=callback_name,
                                              mapping_proc=mapping_proc,
                                              mapped_sec=mapped_sec,
                                              base_p=base_p,
                                              size_p=size_p,
                                              offset_p=offset_p,
                                              proc=proc,
                                              update_vads=update_vads)

        mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB,
                              callback_function,
                              name=callback_name,
                              addr=ret_addr,
                              pgd=pgd)
예제 #54
0
                finally:
                    of.close()

                # Dump every dll.
                mods = dict((mod.DllBase.v(), mod)
                            for mod in task.get_load_modules())

                # List of covered_ranges contains all the address ranges already dumped, to avoid
                # dumping them as vads.
                covered_ranges = [task.Peb.ImageBaseAddress]

                for mod in mods.values():
                    mod_base = mod.DllBase.v()
                    mod_name = mod.BaseDllName
                    if not task_space.is_valid_address(mod_base):
                        mwmon.printer(
                            "Error: DllBase is unavailable (possibly due to paging)")
                        continue
                    else:
                        mwmon.printer("Dumping module %s for %x" %
                                      (mod_name, task.UniqueProcessId))
                        dump_file = os.path.join(
                            path, "module.{0:x}.{1:x}.dll".format(task.UniqueProcessId, mod_base))
                        of = open(dump_file, 'wb')
                        pe_file = obj.Object(
                            "_IMAGE_DOS_HEADER", offset=mod_base, vm=task_space)
                        covered_ranges.append(mod_base)
                        try:
                            for offset, code in pe_file.get_image(unsafe=True,
                                                                  memory=False,
                                                                  fix=True):
                                of.seek(offset)