Example #1
0
def read_parameters(cpu, num_params):
    '''
        Reads parameters from the registers/stack
    '''
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    if TARGET_LONG_SIZE == 4:
        # All the parameters are on the stack
        # We need to read num_params values + the return address
        try:
            buff = api.r_va(cpu.CR3, cpu.ESP, (num_params + 1) * 4)
        except:
            buff = "\x00" * len((num_params + 1) * 4)
            mwmon.printer(
                "Could not read properly the parameters in interproc.py")
        params = struct.unpack("<" + "I" * (1 + num_params), buff)
        return params
    elif TARGET_LONG_SIZE == 8:
        params_regs = []
        params_stack = ()

        # Add the return address as parameter 0
        try:
            buff = api.r_va(cpu.CR3, cpu.RSP, 8)
        except:
            buff = "\x00" * 8
            mwmon.printer(
                "Could not read properly the parameters in interproc.py")

        params_regs.append(struct.unpack("<Q", buff)[0])

        if num_params <= 1:
            params_regs.append(cpu.RCX)
        if num_params <= 2:
            params_regs.append(cpu.RDX)
        if num_params <= 3:
            params_regs.append(cpu.R8)
        if num_params <= 4:
            params_regs.append(cpu.R9)
        if num_params > 4:
            # We need to read num_params + the return address + 0x20
            # 0x20 is for the 4 slots (of 8 bytes) allocated to store
            # register parameters (allocated by caller, used by callee)
            try:
                buff = api.r_va(cpu.CR3, cpu.RSP, (num_params + 5) * 8)
            except:
                buff = "\x00" * ((num_params + 5) * 8)
                mwmon.printer(
                    "Could not read properly the parameters in interproc.py")

            params_stack = struct.unpack("<" + "Q" * (5 + num_params), buff)
            params_stack = params_stack[5:]
        return (tuple(params_regs) + params_stack)
    else:
        raise Exception(
            "[interproc::read_return_parameter(cpu)] : Non-supported TARGET_LONG_SIZE: %d"
            % TARGET_LONG_SIZE)
Example #2
0
def read_parameters(cpu, num_params):
    '''
        Reads parameters from the registers/stack
    '''
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    if TARGET_LONG_SIZE == 4:
        # All the parameters are on the stack
        # We need to read num_params values + the return address
        try:
            buff = api.r_va(cpu.CR3, cpu.ESP, (num_params + 1) * 4)
        except:
            buff = "\x00" * len((num_params + 1) * 4)
            mwmon.printer("Could not read properly the parameters in interproc.py")
        params = struct.unpack("<" + "I" * (1 + num_params), buff)
        return params
    elif TARGET_LONG_SIZE == 8:
        params_regs = []
        params_stack = ()

        # Add the return address as parameter 0
        try:
            buff = api.r_va(cpu.CR3, cpu.RSP, 8)
        except:
            buff = "\x00" * 8
            mwmon.printer("Could not read properly the parameters in interproc.py")

        params_regs.append(struct.unpack("<Q", buff)[0])

        if num_params <= 1:
            params_regs.append(cpu.RCX)
        if num_params <= 2:
            params_regs.append(cpu.RDX)
        if num_params <= 3:
            params_regs.append(cpu.R8)
        if num_params <= 4:
            params_regs.append(cpu.R9)
        if num_params > 4:
            # We need to read num_params + the return address + 0x20
            # 0x20 is for the 4 slots (of 8 bytes) allocated to store
            # register parameters (allocated by caller, used by callee)
            try:
                buff = api.r_va(cpu.CR3, cpu.RSP, (num_params + 5) * 8)
            except:
                buff = "\x00" * ((num_params + 5) * 8)
                mwmon.printer("Could not read properly the parameters in interproc.py")
                
            params_stack = struct.unpack(
                "<" + "Q" * (5 + num_params), buff)
            params_stack = params_stack[5:]
        return (tuple(params_regs) + params_stack)
    else:
        raise Exception(
            "[interproc::read_return_parameter(cpu)] : Non-supported TARGET_LONG_SIZE: %d" % TARGET_LONG_SIZE)
Example #3
0
def gdb_memory_rw_debug(thread_id, thread_list, addr, length, buf, is_write):
    ''' Read / Write memory '''

    thread = None
    # First, check if we can read the register from the CPU object
    for element in thread_list:
        if element['id'] == thread_id:
            thread = element
            break

    if thread is None:
        return None

    if is_write:
        from api import w_va
        w_va(thread['pgd'], addr, buf, length)
        return buf
    else:
        try:
            from api import r_va
            import binascii
            mem = r_va(thread['pgd'], addr, length)
            return mem
        except Exception as e:
            raise e
Example #4
0
def ntmapviewofsection_ret(params, cm, callback_name, mapped_sec, mapping_proc,
                           base_p, size_p, offset_p, proc, update_vads,
                           long_size):
    from core import SectionMap
    import api
    global interproc_data
    global interproc_config

    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
    cm.rm_callback(callback_name)

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

    if size_p != 0:
        size = dereference_target_long(size_p, pgd, long_size)
    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
            pp_debug(
                "Could not dereference offset in NtMapViewOfSection return, in interproc_callbacks.py\n"
            )
    else:
        offset = 0

    mapping_proc.add_section_map(
        SectionMap(mapped_sec, pgd, base, size, offset))

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

    if update_vads:
        proc.update_vads()
Example #5
0
def read(pgd, addr, length):
    '''
        Wrapper to read data from memory
    '''
    import api
    try:
        return api.r_va(pgd, addr, length)
    except:
        return "\x00" * length
Example #6
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()
Example #7
0
def dereference_target_long(addr, pgd):
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    typ = "<I" if TARGET_LONG_SIZE == 4 else "<Q"
    try:
        buff = api.r_va(pgd, addr, TARGET_LONG_SIZE)
    except:
        buff = "\x00" * TARGET_LONG_SIZE
        mwmon.printer("Could not dereference TARGET_LONG in interproc.py")
    return struct.unpack(typ, buff)[0]
Example #8
0
def dereference_target_long(addr, pgd):
    import api
    TARGET_LONG_SIZE = api.get_os_bits() / 8
    typ = "<I" if TARGET_LONG_SIZE == 4 else "<Q"
    try:
        buff = api.r_va(pgd, addr, TARGET_LONG_SIZE)
    except:
        buff = "\x00" * TARGET_LONG_SIZE
        mwmon.printer("Could not dereference TARGET_LONG in interproc.py")
    return struct.unpack(typ, buff)[0]
Example #9
0
    def __opcode_range_callback(self, params):
        """
            Called by the callback manager when the desired opcode is hit.
        """
        cpu_index = params["cpu_index"]
        cpu = params["cpu"]
        cur_pc = params["cur_pc"]
        next_pc = params["next_pc"]
        try:
            if self.__status == GuestAgentPlugin.__AGENT_READY:
                function = api.r_va(api.get_running_process(cpu_index),
                                    cur_pc + 3, 2)
                try:
                    handler = {
                        "\x00\x00": self.__handle_host_version,
                        "\x00\x01": self.__handle_host_message,
                        "\x00\x02": self.__handle_host_get_command,
                        "\x10\x00": self.__handle_host_open,
                        "\x10\x01": self.__handle_host_read,
                        "\x10\x02": self.__handle_host_close,
                        "\x10\x03": self.__handle_host_get_file_name,
                        "\x20\x00": self.__handle_host_request_exec_path,
                        "\x20\x01": self.__handle_host_request_exec_args,
                        "\x20\x02": self.__handle_host_request_exec_env,
                        "\x20\x03": self.__handle_host_request_exec_args_linux,
                        "\x20\x04": self.__handle_host_request_exec_env_linux
                    }[function]
                    handler(cpu_index, cpu)
                except KeyError:
                    self.__printer(
                        "HostFilePlugin: Unknown host opcode %x at 0x%08x" %
                        (function, cur_pc))

                # Advance the program counter.
                # Needs to be done explicitly, as Qemu doesn't know the instruction
                # length.
                if isinstance(cpu, X86CPU):
                    api.w_r(cpu_index, "EIP", cpu.EIP + 10)
                elif isinstance(cpu, X64CPU):
                    api.w_r(cpu_index, "RIP", cpu.RIP + 10)
            elif self.__status == GuestAgentPlugin.__AGENT_RUNNING:
                # Agent already running but not ready yet (the base
                # address was not correctly determined yet.

                # Advance the program counter.
                # Needs to be done explicitly, as Qemu doesn't know the instruction
                # length.
                if isinstance(cpu, X86CPU):
                    api.w_r(cpu_index, "EIP", cpu.EIP + 10)
                elif isinstance(cpu, X64CPU):
                    api.w_r(cpu_index, "RIP", cpu.RIP + 10)
        except Exception as e:
            self.__printer("Exception occurred on opcode callback: %s" %
                           str(e))
Example #10
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()
Example #11
0
 def __read_string(self, cpu_index, addr):
     """
         Read a string from the virtual address space of the guest's user mode.
     """
     pgd = api.get_running_process(cpu_index)
     string = []
     for i in range(256):
         byte = api.r_va(pgd, addr + i, 1)
         if byte == "\x00":
             break
         string.append(byte)
     return "".join(string)
Example #12
0
 def __read_string(self, cpu_index, addr):
     """
         Read a string from the virtual address space of the guest's user mode.
     """
     pgd = api.get_running_process(cpu_index)
     string = []
     for i in range(256):
         byte = api.r_va(pgd, addr + i, 1)
         if byte == "\x00":
             break
         string.append(byte)
     return "".join(string)
Example #13
0
    def __opcode_range_callback(self, params):
        """
            Called by the callback manager when the desired opcode is hit.
        """
        cpu_index = params["cpu_index"]
        cpu = params["cpu"]
        cur_pc = params["cur_pc"]
        next_pc = params["next_pc"]
        try:
            if self.__status == GuestAgentPlugin.__AGENT_READY:
                function = api.r_va(api.get_running_process(cpu_index), cur_pc + 3, 2)
                try:
                    handler = {
                        "\x00\x00": self.__handle_host_version,
                        "\x00\x01": self.__handle_host_message,
                        "\x00\x02": self.__handle_host_get_command,
                        "\x10\x00": self.__handle_host_open,
                        "\x10\x01": self.__handle_host_read,
                        "\x10\x02": self.__handle_host_close,
                        "\x10\x03": self.__handle_host_get_file_name,
                        "\x20\x00": self.__handle_host_request_exec_path,
                        "\x20\x01": self.__handle_host_request_exec_args,
                        "\x20\x02": self.__handle_host_request_exec_env,
                        "\x20\x03": self.__handle_host_request_exec_args_linux,
                        "\x20\x04": self.__handle_host_request_exec_env_linux

                    }[function]
                    handler(cpu_index, cpu)
                except KeyError:
                    self.__printer(
                        "HostFilePlugin: Unknown host opcode %x at 0x%08x" % (function, cur_pc))

                # Advance the program counter.
                # Needs to be done explicitly, as Qemu doesn't know the instruction
                # length.
                if isinstance(cpu, X86CPU):
                    api.w_r(cpu_index, "EIP", cpu.EIP + 10)
                elif isinstance(cpu, X64CPU):
                    api.w_r(cpu_index, "RIP", cpu.RIP + 10)
            elif self.__status == GuestAgentPlugin.__AGENT_RUNNING:
                # Agent already running but not ready yet (the base
                # address was not correctly determined yet.

                # Advance the program counter.
                # Needs to be done explicitly, as Qemu doesn't know the instruction
                # length.
                if isinstance(cpu, X86CPU):
                    api.w_r(cpu_index, "EIP", cpu.EIP + 10)
                elif isinstance(cpu, X64CPU):
                    api.w_r(cpu_index, "RIP", cpu.RIP + 10)
        except Exception as e:
            self.__printer("Exception occurred on opcode callback: %s" % str(e))
Example #14
0
def dereference_target_long(addr, pgd, long_size):
    import api
    typ = "<I" if long_size == 4 else "<Q"
    try:
        buff = api.r_va(pgd, addr, long_size)
    except:
        buff = "\x00" * long_size
        pp_debug("Could not dereference TARGET_LONG in interproc_callbacks.py")

    if len(buff) == 0:
        pp_warning(
            "[interproc_callbacks.py:dereference_target_long] Error while dereferencing parameter with address %x"
            % addr)
        return 0
    return struct.unpack(typ, buff)[0]
Example #15
0
def load_module(params):
    '''
        Callback trigger for every module loaded.
    '''
    global cm
    global pyrebox_print
    global entry_point_bp
    global target_pgd
    global target_procname
    import pefile
    import api
    from api import BP

    pid = params["pid"]
    pgd = params["pgd"]
    base = params["base"]
    size = params["size"]
    name = params["name"]
    fullname = params["fullname"]

    if pgd == target_pgd and target_procname.lower().startswith(name.lower()):
        # Loaded main module, try to read EP
        ep = None
        try:
            pe_data = api.r_va(pgd, base, 0x1000)
            pe = pefile.PE(data=pe_data)
            ep = base + pe.OPTIONAL_HEADER.AddressOfEntryPoint
        except Exception as e:
            print(e)
            pyrebox_print("Could not read EP from module %s on load" % name)

        # If we have the EP, put a breakpoint there
        if ep is not None:
            pyrebox_print("The entry point for %s is 0x%x\n" %
                          (target_procname, ep))

            cm.rm_callback("load_module")

            pyrebox_print("Setting BP on entrypoint")

            # Set a breakpoint on the EP, that will start a shell
            entry_point_bp = BP(ep,
                                pgd,
                                new_style=True,
                                func=module_entry_point)
            entry_point_bp.enable()
Example #16
0
def find_ep(pgd, proc_name):
    '''Given an address space and a process name, uses pefile module
       to get its entry point
    '''
    global cm
    global loaded_processes
    import api
    for m in api.get_module_list(pgd):
        name = m["name"]
        base = m["base"]
        # size = m["size"]
        if name == proc_name:
            try:
                pe_data = api.r_va(pgd, base, 0x1000)
                pe = pefile.PE(data=pe_data)
                ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
                return (base + ep)
            except Exception:
                pyrebox_print("Unable to run pefile on loaded module %s" % name)
Example #17
0
def find_ep(pgd, proc_name):
    '''Given an address space and a process name, uses pefile module
       to get its entry point
    '''
    global cm
    global loaded_processes
    import api
    for m in api.get_module_list(pgd):
        name = m["name"]
        base = m["base"]
        # size = m["size"]
        if name == proc_name:
            try:
                pe_data = api.r_va(pgd, base, 0x1000)
                pe = pefile.PE(data=pe_data)
                ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
                return (base + ep)
            except Exception:
                pyrebox_print("Unable to run pefile on loaded module %s" % name)
Example #18
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
Example #19
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
Example #20
0
def opcodes(params, cb_name, proc):
    from api import CallbackManager
    import api
    from deviare_db_parser import ArgumentParser
    import struct

    global bp_counter
    global APITRACER_DATABASE32
    global APITRACER_DATABASE64
    global APITRACER_RULES
    global APITRACER_ANTI_STOLEN

    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"]
    pgd = api.get_running_process(cpu_index)

    try:
        if APITRACER_ANTI_STOLEN:
            # Locate nearest lower symbol
            sym = proc.locate_nearest_symbol(next_pc)
        else:
            sym = proc.locate_nearest_symbol(next_pc, tolerate_offset=0x0)

        if sym is None:
            return

        mod = sym.get_mod()
        mod_fullname = sym.get_mod_fullname()
        fun = sym.get_fun()
        real_api_addr = sym.get_addr()

        caller_module_name = proc.get_overlapping_module(pc)

        # Reduce FP's by checking that the origin EIP is not also within the
        # same module (code reuse inside the dll)

        if mod_fullname == caller_module_name:
            return

        matched = False
        # Check if the API is in the list (included or excluded)
        for rule in APITRACER_RULES["rules"]:
            if rule["mod"] == "" or fnmatch.fnmatch(mod_fullname.lower(),
                                                    rule["mod"].lower()):
                if rule["fun"] == "" or fnmatch.fnmatch(
                        fun.lower(), rule["fun"].lower()):
                    if "from_mod" in rule and rule["from_mod"] != "":
                        if caller_module_name is not None:
                            if fnmatch.fnmatch(caller_module_name,
                                               rule["from_mod"].lower()):
                                matched = True
                                if rule["action"] == "reject":
                                    return
                                else:
                                    break
                                break
                    else:
                        matched = True
                        if rule["action"] == "reject":
                            return
                        else:
                            break

        # Apply default policy if not matched:
        if not matched and APITRACER_RULES["policy"] == "reject":
            return

        # 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 Exception as e:
                ret_addr = 0
                pyrebox_print(
                    "Could not read return address on API tracer: %s" % str(e))
        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 Exception as e:
                ret_addr = 0
                pyrebox_print(
                    "Could not read return address on API tracer: %s" % str(e))

        is_64_bit_dll = True
        if TARGET_LONG_SIZE == 4 or (TARGET_LONG_SIZE == 8 and proc.is_wow64()
                                     and "windows\\syswow64"
                                     in mod_fullname.lower()):
            is_64_bit_dll = False

        if APITRACER_LIGHT_MODE:
            bits = 32 if not is_64_bit_dll else 64
            if real_api_addr == next_pc:
                if TARGET_LONG_SIZE == 4:
                    proc.add_call(
                        pc, real_api_addr,
                        "[PID: %x] (%d) %08x --> %s:%s(%08x) --> %08x\n" %
                        (proc.get_pid(), bits, pc, mod_fullname, fun,
                         real_api_addr, ret_addr))
                elif TARGET_LONG_SIZE == 8:
                    proc.add_call(
                        pc, real_api_addr,
                        "[PID: %x] (%d) %016x --> %s:%s(%016x) --> %016x\n" %
                        (proc.get_pid(), bits, pc, mod_fullname, fun,
                         real_api_addr, ret_addr))
            else:
                if TARGET_LONG_SIZE == 4:
                    proc.add_call(
                        pc, real_api_addr,
                        "[PID: %x] (%d) %08x --> %s:%s(+%x)(%08x) --> %08x\n" %
                        (proc.get_pid(), bits, pc, mod_fullname, fun,
                         (next_pc - real_api_addr), next_pc, ret_addr))
                elif TARGET_LONG_SIZE == 8:
                    proc.add_call(
                        pc, real_api_addr,
                        "[PID: %x] (%d) %016x --> %s:%s(+%x)(%016x) --> %016x\n"
                        % (proc.get_pid(), bits, pc, mod_fullname, fun,
                           (next_pc - real_api_addr), next_pc, ret_addr))
            return

        data = APICallData()
        data.set_pc(pc)
        data.set_mod(mod)
        data.set_fun(fun)
        data.set_ret_addr(ret_addr)

        if not is_64_bit_dll:
            argument_parser = ArgumentParser(cpu, cpu.ESP, mod, fun, 32)
        else:
            argument_parser = ArgumentParser(cpu, cpu.RSP, mod, fun, 64)

        if not argument_parser.in_db():
            #pyrebox_print("API function not present in db: %s - %s" % (mod, fun))
            return

        data.set_in_args([arg for arg in argument_parser.get_in_args()])

        # Add the call as soon as it is produced, and update
        # the output parameters on return
        proc.add_call(pc, real_api_addr, data)

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

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

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

            bp_counter += 1

    except Exception as e:
        pyrebox_print(str(e))
        traceback.print_exc()
    finally:
        return
Example #21
0
def linux_insert_module(task, pid, pgd, base, size, basename, fullname, update_symbols=False):
    from utils import ConfigurationManager as conf_m
    import volatility.obj as obj
    from vmi import modules
    from vmi import symbols
    from vmi import Module
    import api
    import hashlib

    pgd_for_memory_read = conf_m.addr_space.vtop(task.mm.pgd) or task.mm.pgd

    # Create module, use 0 as checksum as it is irrelevant here
    mod = Module(base, size, pid, pgd, 0, basename, fullname)

    # Add an entry in the module list, if necessary
    if (pid, pgd) not in modules:
        modules[(pid, pgd)] = {}

    # Add the module to the module list
    if base in modules[(pid, pgd)]:
        del modules[(pid, pgd)][base]

    modules[(pid, pgd)][base] = mod

    if update_symbols:
        # Compute the checksum of the ELF Header, as a way to avoid name
        # collisions on the symbol cache. May extend this hash to other parts
        # of the binary if necessary in the future.
        elf_hdr = obj.Object(
            "elf_hdr", offset=base, vm=task.get_process_address_space())

        if elf_hdr.is_valid():
            elf_hdr_size = elf_hdr.elf_obj.size()
            buf = api.r_va(pgd_for_memory_read, base, elf_hdr_size)
            h = hashlib.sha256()
            h.update(buf)
            checksum = h.hexdigest()

            if (checksum, fullname) not in symbols:
                symbols[(checksum, fullname)] = {}
                syms = symbols[(checksum, fullname)]
                # Fetch symbols
                for sym in elf_hdr.symbols():
                    if sym.st_value == 0 or (sym.st_info & 0xf) != 2:
                        continue

                    sym_name = elf_hdr.symbol_name(sym)
                    sym_offset = sym.st_value
                    if sym_name in syms:
                        if syms[sym_name] != sym_offset:
                            # There are cases in which the same import is present twice, such as in this case:
                            # nm /lib/x86_64-linux-gnu/libpthread-2.24.so | grep "pthread_getaffinity_np"
                            # 00000000000113f0 T pthread_getaffinity_np@GLIBC_2.3.3
                            # 00000000000113a0 T
                            # pthread_getaffinity_np@@GLIBC_2.3.4
                            sym_name = sym_name + "_"
                            while sym_name in syms and syms[sym_name] != sym_offset:
                                sym_name = sym_name + "_"
                            if sym_name not in syms:
                                syms[sym_name] = sym_offset
                    else:
                        syms[sym_name] = sym_offset

            mod.set_symbols(symbols[(checksum, fullname)])

    return None
Example #22
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()
Example #23
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()
Example #24
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
Example #25
0
def windows_read_paged_out_memory(pgd, addr, size):
    import api
    import api_internal
    import struct
    from utils import get_addr_space

    VALID_BIT = 0x1
    PROTOTYPE_BIT = 0x1 << 10
    TRANSITION_BIT = 0x1 << 11

    PPTE_VALID_BIT = 0x1
    PPTE_TRANSITION_BIT = 0x1 << 11
    PPTE_DIRTY_BIT = 0x1 << 6
    PPTE_P_BIT = 0x1 << 10  # Thit bit means it is a...
    #...memory mapped file,  instead of "prototype"

    # Get PTE and 'mode'
    pte = api_internal.x86_get_pte(pgd, addr)
    is_pae = api_internal.x86_is_pae()
    bitness = api.get_os_bits()

    addr_space = get_addr_space(pgd)

    # if PTE is None, it could mean that the page directory has not been created.
    # if PTE is 0, it could mean the pte has not been yet created.
    # In these cases, we still check the VAD to see if it corresponds to a
    # memory mapped file, and if so, read that file at the correct offset
    if pte is None or pte == 0:
        return windows_read_memory_mapped(pgd, addr, size, pte, is_pae,
                                          bitness)

    # Make sure the page is invalid and we cannot read it:
    if (pte & VALID_BIT) == 1:
        return api.r_va(pgd, addr, size)

    # The PTE is INVALID. First, we check it doesn't point to a
    # prototype page table entry (PPTE).
    if (pte & PROTOTYPE_BIT) == 0:
        # The page is in the pagefile, or is demand zero, or memory mapped
        if (pte & TRANSITION_BIT) == 0:
            number_bits_offset = 0
            number_bits_number = 0
            page_file_offset = 0
            page_file_number = 0
            if (bitness == 32 and not is_pae) or bitness == 64:
                offset_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileHigh"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileHigh"][1][1]["end_bit"])
                number_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["end_bit"])
                number_bits_offset = addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]["PageFileHigh"][1][1]["end_bit"] - \
                                     addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]["PageFileHigh"][1][1]["start_bit"]
                number_bits_number = addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["end_bit"] - \
                                     addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["start_bit"]
                page_file_offset = (
                    pte & offset_mask
                ) >> addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1][
                    "PageFileHigh"][1][1]["start_bit"]
                page_file_number = (
                    pte & number_mask) >> addr_space.profile.vtypes[
                        "_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["start_bit"]
            elif bitness == 32 and is_pae:
                # See Intel manual, consider 24 bits of address for the 4KiB page offset
                # Page file offset should correspond to the same 24 bits
                offset_mask = generate_mask(12, 12 + 24)
                # Reuse the same as for 32/64 bits
                number_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["end_bit"])
                number_bits_offset = 24
                number_bits_number = addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["end_bit"] - \
                                     addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["start_bit"]
                page_file_offset = (pte & offset_mask) >> 12
                page_file_number = (
                    pte & number_mask) >> addr_space.profile.vtypes[
                        "_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["start_bit"]

            else:
                raise NotImplementedError()

            #Demand zero
            if page_file_offset == 0x0:
                return "\x00" * size
            #Check VAD (memory mapped file) (all 1's)
            elif page_file_offset == generate_mask(0, number_bits_offset):
                return windows_read_memory_mapped(pgd, addr, size, pte, is_pae,
                                                  bitness)
            # Page file
            else:
                return windows_read_paged_file(pgd, addr, size,
                                               page_file_offset,
                                               page_file_number)
        # Transition page -> Can be read normally from memory,
        # so proceed with the read even if valid bit is 0.
        else:
            # Get the offset from the PTE, and compute ourselves the physical address
            page_offset = 0
            if (bitness == 32 and not is_pae) or bitness == 64:
                offset_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_HARDWARE"][1]
                    ["PageFrameNumber"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_HARDWARE"][1]
                    ["PageFrameNumber"][1][1]["end_bit"])
                page_offset = (pte & offset_mask)
            elif bitness == 32 and is_pae:
                # See Intel manual, consider 24 bits of address for the 4KiB page offset
                # Page file offset should correspond to the same 24 bits
                offset_mask = generate_mask(12, 12 + 24)
                # Reuse the same as for 32/64 bits
                page_offset = (pte & offset_mask)
            else:
                raise NotImplementedError()
            # Read physical address, always 12 bits for a 4KiB page.
            # XXX: Here, we should should also consider 4Mb pages.
            return api.r_pa(page_offset | (addr & generate_mask(0, 12)), size)
    # The page points to a prototype PTE
    else:
        # We read the PPTE
        ppte_addr = 0
        ppte_size = 0
        if bitness == 32 and not is_pae:
            # In this case, the PPTE pointer is not a pointer, but an index, so it
            # needs some additional computation
            index_low_mask = generate_mask(
                addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]
                ["ProtoAddressLow"][1][1]["start_bit"],
                addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]
                ["ProtoAddressLow"][1][1]["end_bit"])
            index_low = (pte & index_low_mask) >> addr_space.profile.vtypes[
                "_MMPTE_PROTOTYPE"][1]["ProtoAddressLow"][1][1]["start_bit"]

            index_high_mask = generate_mask(
                addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]
                ["ProtoAddressHigh"][1][1]["start_bit"],
                addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]
                ["ProtoAddressHigh"][1][1]["end_bit"])
            index_high = (pte & index_high_mask) >> addr_space.profile.vtypes[
                "_MMPTE_PROTOTYPE"][1]["ProtoAddressHigh"][1][1]["start_bit"]

            number_bits_index_low = addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]["ProtoAddressLow"][1][1]["end_bit"]- \
                                        addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]["ProtoAddressLow"][1][1]["start_bit"]

            ppte_size = addr_space.profile.vtypes["_MMPTE"][0]

            # Formula to compute the index
            index = ((index_high << number_bits_index_low) | index_low) << 2

            # The index is an address relative to the base of a paged pool.
            # By debugging several systems, these bases where fixed (on 32 bit systems)
            # to 0x80000000 or 0xe1000000 (windows 7 and windows xp respectively).
            # This address points to one of the PrototypePTEs that are part of the Segment,
            # pointed out by the ControlArea of the corresponding VAD. Therefore, we should
            # be able to bruteforce the first 8 bits of the address to find the correct base.
            # First, get the Segment, and the first prototype PTE, as well as the number of
            # prototype PTEs for that Segment.
            res = windows_get_prototype_pte_address_range(pgd, addr)
            found = False
            if res is not None:
                start, end = res
                for i in range(0, 255):
                    ppte_addr = (i << 24) | index
                    if ppte_addr >= start and ppte_addr <= end:
                        found = True
                        break
                if not found:
                    raise RuntimeError(
                        "Could not read memory on second chance (using filesystem)"
                    )
            else:
                raise RuntimeError(
                    "Could not read memory on second chance (using filesystem)"
                )

        elif bitness == 32 and is_pae:
            # According to paper: "Windows Operating Systems Agnostic Memory Analysis"
            offset_mask = generate_mask(32, 64)
            ppte_addr = (pte & offset_mask) >> 32
            ppte_size = 64

        elif bitness == 64:
            offset_mask = generate_mask(
                addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]
                ["ProtoAddress"][1][1]["start_bit"],
                addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][1]
                ["ProtoAddress"][1][1]["end_bit"])
            ppte_addr = (pte & offset_mask) >> addr_space.profile.vtypes[
                "_MMPTE_PROTOTYPE"][1]["ProtoAddress"][1][1]["start_bit"]
            ppte_size = addr_space.profile.vtypes["_MMPTE"][0]

        else:
            raise NotImplementedError()

        # Now, read the PPTE given its address. The PPTE address is a virtual address!!!! (on a paged pool)
        if ppte_size == 4:
            ppte = struct.unpack("<I", api.r_va(pgd, ppte_addr, 4))[0]
        elif ppte_size == 8:
            ppte = struct.unpack("<K", api.r_va(pgd, ppte_addr, 8))[0]
        else:
            raise NotImplementedError()

        if (ppte & PPTE_VALID_BIT == 1) or (ppte & PPTE_TRANSITION_BIT == 1):
            # State: Active/Valid - Transision - Modified-no-write
            # The PPTE contains a valid entry, so we can
            # just use the info in it to translate the page.
            # Get the offset from the PTE, and compute ourselves the physical address
            page_offset = 0
            if (bitness == 32 and not is_pae) or bitness == 64:
                offset_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_HARDWARE"][1]
                    ["PageFrameNumber"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_HARDWARE"][1]
                    ["PageFrameNumber"][1][1]["end_bit"])
                page_offset = (ppte & offset_mask)
            elif bitness == 32 and is_pae:
                # See Intel manual, consider 24 bits of address for the 4KiB page offset
                # Page file offset should correspond to the same 24 bits
                offset_mask = generate_mask(12, 12 + 24)
                # Reuse the same as for 32/64 bits
                page_offset = (ppte & offset_mask)
            else:
                raise NotImplementedError()

            # Read physical address, always 12 bits for a 4KiB page.
            # XXX: Here, we should should also consider 4Mb pages.
            return api.r_pa(page_offset | (addr & generate_mask(0, 12)), size)
        elif (ppte & (PPTE_VALID_BIT | PPTE_TRANSITION_BIT | PPTE_P_BIT)) == 0:
            # Demand zero or pagefile
            #Read page_file_offset and page_file_number
            page_file_offset = 0
            page_file_number = 0
            if (bitness == 32 and not is_pae) or bitness == 64:
                offset_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileHigh"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileHigh"][1][1]["end_bit"])
                number_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["end_bit"])
                page_file_offset = (
                    pte & offset_mask
                ) >> addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1][
                    "PageFileHigh"][1][1]["start_bit"]
                page_file_number = (
                    pte & number_mask) >> addr_space.profile.vtypes[
                        "_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["start_bit"]
            elif bitness == 32 and is_pae:
                # See Intel manual, consider 24 bits of address for the 4KiB page offset
                # Page file offset should correspond to the same 24 bits
                offset_mask = generate_mask(12, 12 + 24)
                # Reuse the same as for 32/64 bits
                number_mask = generate_mask(
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["start_bit"],
                    addr_space.profile.vtypes["_MMPTE_SOFTWARE"][1]
                    ["PageFileLow"][1][1]["end_bit"])
                page_file_offset = (pte & offset_mask) >> 12
                page_file_number = (
                    pte & number_mask) >> addr_space.profile.vtypes[
                        "_MMPTE_SOFTWARE"][1]["PageFileLow"][1][1]["start_bit"]
            else:
                raise NotImplementedError()

            if page_file_offset == 0 and page_file_number == 0:
                #Demand zero
                return "\x00" * size
            else:
                # PageFile
                return windows_read_paged_file(pgd, addr, size,
                                               page_file_offset,
                                               page_file_number)
        elif (ppte & PPTE_P_BIT) == 1:
            # Memory mapped file
            return windows_read_memory_mapped(pgd, addr, size, ppte, is_pae,
                                              bitness)
Example #26
0
def linux_insert_module(task, pid, pgd, base, size, basename, fullname, update_symbols=False):
    from utils import ConfigurationManager as conf_m
    import volatility.obj as obj
    from vmi import modules
    from vmi import symbols
    from vmi import Module
    from api_internal import dispatch_module_load_callback
    from api_internal import dispatch_module_remove_callback
    import api
    import hashlib

    pgd_for_memory_read = conf_m.addr_space.vtop(task.mm.pgd) or task.mm.pgd

    # Create module, use 0 as checksum as it is irrelevant here
    mod = Module(base, size, pid, pgd, 0, basename, fullname)

    # Add an entry in the module list, if necessary
    if (pid, pgd) not in modules:
        modules[(pid, pgd)] = {}

    #Module load/del notification
    if base in modules[(pid, pgd)]:
        if modules[(pid, pgd)][base].get_size() != size or \
           modules[(pid, pgd)][base].get_checksum() != checksum or \
           modules[(pid, pgd)][base].get_name() != basename or \
           modules[(pid, pgd)][base].get_fullname() != fullname:
            # Notify of module deletion and module load
            dispatch_module_remove_callback(pid, pgd, base,
                                            modules[(pid, pgd)][base].get_size(),
                                            modules[(pid, pgd)][base].get_name(),
                                            modules[(pid, pgd)][base].get_fullname())
            del modules[(pid, pgd)][base]
            dispatch_module_load_callback(pid, pgd, base, size, basename, fullname)
            modules[(pid, pgd)][base] = mod
    else:
        # Just notify of module load
        dispatch_module_load_callback(pid, pgd, base, size, basename, fullname)
        modules[(pid, pgd)][base] = mod

    # Mark the module as present
    modules[(pid, pgd)][base].set_present()

    if update_symbols:
        # Compute the checksum of the ELF Header, as a way to avoid name
        # collisions on the symbol cache. May extend this hash to other parts
        # of the binary if necessary in the future.
        elf_hdr = obj.Object(
            "elf_hdr", offset=base, vm=task.get_process_address_space())

        if elf_hdr.is_valid():
            elf_hdr_size = elf_hdr.elf_obj.size()
            buf = ""

            try:
                buf = api.r_va(pgd_for_memory_read, base, elf_hdr_size)
            except:
                pp_warning("Could not read ELF header at address %x" % base)

            h = hashlib.sha256()
            h.update(buf)
            checksum = h.hexdigest()

            if (checksum, fullname) not in symbols:
                symbols[(checksum, fullname)] = {}
                syms = symbols[(checksum, fullname)]
                # Fetch symbols
                for sym in elf_hdr.symbols():
                    if sym.st_value == 0 or (sym.st_info & 0xf) != 2:
                        continue

                    sym_name = elf_hdr.symbol_name(sym)
                    sym_offset = sym.st_value
                    if sym_name in syms:
                        if syms[sym_name] != sym_offset:
                            # There are cases in which the same import is present twice, such as in this case:
                            # nm /lib/x86_64-linux-gnu/libpthread-2.24.so | grep "pthread_getaffinity_np"
                            # 00000000000113f0 T pthread_getaffinity_np@GLIBC_2.3.3
                            # 00000000000113a0 T
                            # pthread_getaffinity_np@@GLIBC_2.3.4
                            sym_name = sym_name + "_"
                            while sym_name in syms and syms[sym_name] != sym_offset:
                                sym_name = sym_name + "_"
                            if sym_name not in syms:
                                syms[sym_name] = sym_offset
                    else:
                        syms[sym_name] = sym_offset

            mod.set_symbols(symbols[(checksum, fullname)])

    return None
Example #27
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
Example #28
0
    def __init__(self, pgd, section_object):
        import api
        global interproc_data

        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
            pp_print("Could not read flags in Section __init__\n")
        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 "_EX_FAST_REF" in str(type(file_obj)):
                    # 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)))

            self.__backing_file = interproc_data.get_file_by_file_name(
                str(file_obj.FileName))

            # 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))
                interproc_data.add_file(self.__backing_file)

        self.__unpickled = False
        self.__offset = self.__section_object.obj_offset
Example #29
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
Example #30
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
Example #31
0
def linux_insert_module(task,
                        pid,
                        pgd,
                        base,
                        size,
                        basename,
                        fullname,
                        update_symbols=False):
    from utils import ConfigurationManager as conf_m
    import volatility.obj as obj
    from vmi import add_symbols
    from vmi import get_symbols
    from vmi import has_symbols
    from vmi import add_module
    from vmi import has_module
    from vmi import get_module
    from vmi import Module
    from api_internal import dispatch_module_load_callback
    from api_internal import dispatch_module_remove_callback
    import api
    import hashlib

    pgd_for_memory_read = conf_m.addr_space.vtop(task.mm.pgd) or task.mm.pgd

    # Create module, use 0 as checksum as it is irrelevant here
    mod = Module(base, size, pid, pgd, 0, basename, fullname)

    #Module load/del notification
    if has_module(pid, pgd, base):
        ex_mod = get_module(pid, pgd, base)
        if ex_mod.get_size() != size or \
           ex_mod.get_checksum() != checksum or \
           ex_mod.get_name() != basename or \
           ex_mod.get_fullname() != fullname:
            # Notify of module deletion and module load
            dispatch_module_remove_callback(pid, pgd, base, ex_mod.get_size(),
                                            ex_mod.get_name(),
                                            ex_mod.get_fullname())
            add_module(pid, pgd, base, mod)
            dispatch_module_load_callback(pid, pgd, base, size, basename,
                                          fullname)
    else:
        # Just notify of module load
        dispatch_module_load_callback(pid, pgd, base, size, basename, fullname)
        add_module(pid, pgd, base, mod)

    # Mark the module as present
    get_module(pid, pgd, base).set_present()

    if update_symbols:
        # Compute the checksum of the ELF Header, as a way to avoid name
        # collisions on the symbol cache. May extend this hash to other parts
        # of the binary if necessary in the future.
        elf_hdr = obj.Object("elf_hdr",
                             offset=base,
                             vm=task.get_process_address_space())

        if elf_hdr.is_valid():
            elf_hdr_size = elf_hdr.elf_obj.size()
            buf = ""

            try:
                buf = api.r_va(pgd_for_memory_read, base, elf_hdr_size)
            except:
                pp_warning("Could not read ELF header at address %x" % base)

            if not has_symbols(fullname):
                syms = {}
                # Fetch symbols
                for sym in elf_hdr.symbols():
                    if sym.st_value == 0 or (sym.st_info & 0xf) != 2:
                        continue

                    sym_name = elf_hdr.symbol_name(sym)
                    sym_offset = sym.st_value
                    if sym_name in syms:
                        if syms[sym_name] != sym_offset:
                            # There are cases in which the same import is present twice, such as in this case:
                            # nm /lib/x86_64-linux-gnu/libpthread-2.24.so | grep "pthread_getaffinity_np"
                            # 00000000000113f0 T pthread_getaffinity_np@GLIBC_2.3.3
                            # 00000000000113a0 T
                            # pthread_getaffinity_np@@GLIBC_2.3.4
                            sym_name = sym_name + "_"
                            while sym_name in syms and syms[
                                    sym_name] != sym_offset:
                                sym_name = sym_name + "_"
                            if sym_name not in syms:
                                syms[sym_name] = sym_offset
                    else:
                        syms[sym_name] = sym_offset

                add_symbols(fullname, syms)

            mod.set_symbols(get_symbols(fullname))

    return None
Example #32
0
def disassemble(addr, cpu_index):
    '''
	Disassemble instruction. Receives 2 parameters:
		:param addr: Address from instruction to disassemble
		:type addr: int
		:param cpu_index: CPU index 
		:type cpu_index: int
	'''

    global logger
    global num_ins

    pgd = api.get_running_process(cpu_index)

    if api.get_os_bits() == 32:
        md = Cs(CS_ARCH_X86, CS_MODE_32)
        content = api.r_va(pgd, addr, 0x4)
    else:
        md = Cs(CS_ARCH_X86, CS_MODE_64)
        content = api.r_va(pgd, addr, 0x6)

    md.detail = True

    for insn in md.disasm(content, addr):

        if insn.mnemonic == "call":

            if len(insn.operands) > 0:

                mycpu = api.r_cpu(0)

                simbolo = None

                if api.get_os_bits() == 32:
                    simbolo = api.va_to_sym(pgd, mycpu.EIP)
                else:
                    simbolo = api.va_to_sym(pgd, mycpu.RIP)

                if simbolo != None and api.get_os_bits() != 32:

                    ## Microsoft x64 calling convention
                    ##

                    logger.info(
                        "[API]0x%x:\t%s\t%s\t[RIP]:0x%x\t%s\t[PGD]: %x",
                        insn.address, insn.mnemonic, insn.op_str, mycpu.RIP,
                        simbolo, pgd)
                    num_ins = num_ins + 1

                    ## RCX - Arg 1

                    try:

                        rcx_content = api.r_va(pgd, mycpu.RCX, 0x100)
                        logger.info("[RCX]: 0x%x [Data]: %s", mycpu.RCX,
                                    smart_format(rcx_content, 0x100, True))

                    except:
                        logger.info("[RCX]: 0x%x", mycpu.RCX)

                    ## RDX - Arg 2

                    try:

                        rdx_content = api.r_va(pgd, mycpu.RDX, 0x100)
                        logger.info("[RDX]: 0x%x [Data]: %s", mycpu.RDX,
                                    smart_format(rdx_content, 0x100, True))

                    except:
                        logger.info("[RDX]: 0x%x", mycpu.RDX)

                    ## R8 - Arg 3

                    try:

                        r8_content = api.r_va(pgd, mycpu.R8, 0x100)
                        logger.info("[R8]: 0x%x [Data]: %s", mycpu.R8,
                                    smart_format(r8_content, 0x100, True))

                    except:
                        logger.info("[R8]: 0x%x", mycpu.R8)

                    ## R9 - Arg 4

                    try:

                        r9_content = api.r_va(pgd, mycpu.R9, 0x100)
                        logger.info("[R9]: 0x%x [Data]: %s", mycpu.R9,
                                    smart_format(r9_content, 0x100, True))

                    except:
                        logger.info("[R9]: 0x%x", mycpu.R9)

                    ## RAX - return value

                    try:
                        rax_content = api.r_va(pgd, mycpu.RAX, 0x100)
                        logger.info("[RAX]: 0x%x [Data]: %s", mycpu.RAX,
                                    smart_format(rax_content, 0x100, True))

                    except:

                        logger.info("[RAX]: 0x%x", mycpu.RAX)

                    logger.info("--")

                elif simbolo != None:

                    ## x86 call conventions
                    # cdecl -> arguments are pushed on the stack in the reverse order. EAX return
                    # syscall -> arguments are pushed on the stack right to left.
                    # optlink -> arguments are pushed on the stack right to left.
                    # ...

                    logger.info(
                        "[API]0x%x:\t%s\t%s\t[EIP]:0x%x\t%s\t[PGD]: %x",
                        insn.address, insn.mnemonic, insn.op_str, mycpu.EIP,
                        simbolo, pgd)
                    num_ins = num_ins + 1

                    bytestoread = 0x200

                    try:
                        eax_content = api.r_va(pgd, mycpu.EAX, bytestoread)
                        logger.info(
                            "[EAX]: 0x%x [Data]: %s", mycpu.EAX,
                            smart_format(eax_content, bytestoread, True))

                    except:
                        logger.info("[EAX]: 0x%x", mycpu.EAX)

                    try:
                        ecx_content = api.r_va(pgd, mycpu.ECX, bytestoread)
                        logger.info(
                            "[ECX]: 0x%x [Data]: %s", mycpu.ECX,
                            smart_format(ecx_content, bytestoread, True))

                    except:
                        logger.info("[ECX]: 0x%x", mycpu.ECX)

                    try:
                        edx_content = api.r_va(pgd, mycpu.EDX, bytestoread)
                        logger.info(
                            "[EDX]: 0x%x [Data]: %s", mycpu.EDX,
                            smart_format(edx_content, bytestoread, True))

                    except:
                        logger.info("[EDX]: 0x%x", mycpu.EDX)

                    try:
                        ebp_arg2_content = api.r_va(pgd, mycpu.EBP + 8,
                                                    bytestoread)
                        logger.info(
                            "[EBP+8]: 0x%x [Data]: %s", mycpu.EBP + 8,
                            smart_format(ebp_arg2_content, bytestoread, True))
                    except:
                        logger.info("[EBP+8]: 0x%x", mycpu.EBP + 8)

                    try:
                        ebp_arg3_content = api.r_va(pgd, mycpu.EBP + 12,
                                                    bytestoread)
                        logger.info(
                            "[EBP+12]: 0x%x [Data]: %s", mycpu.EBP + 12,
                            smart_format(ebp_arg3_content, bytestoread, True))
                    except:
                        logger.info("[EBP+12]: 0x%x", mycpu.EBP + 12)

                    try:
                        ebp_arg4_content = api.r_va(pgd, mycpu.EBP + 16,
                                                    bytestoread)
                        logger.info(
                            "[EBP+16]: 0x%x [Data]: %s", mycpu.EBP + 16,
                            smart_format(ebp_arg4_content, bytestoread, True))
                    except:
                        logger.info("[EBP+16]: 0x%x", mycpu.EBP + 16)

                    try:
                        ebp_arg5_content = api.r_va(pgd, mycpu.EBP + 20,
                                                    bytestoread)
                        logger.info(
                            "[EBP+20]: 0x%x [Data]: %s", mycpu.EBP + 20,
                            smart_format(ebp_arg5_content, bytestoread, True))
                    except:
                        logger.info("[EBP+20]: 0x%x", mycpu.EBP + 20)

                    try:
                        ebp_arg6_content = api.r_va(pgd, mycpu.EBP + 24,
                                                    bytestoread)
                        logger.info(
                            "[EBP+24]: 0x%x [Data]: %s", mycpu.EBP + 24,
                            smart_format(ebp_arg6_content, bytestoread, True))
                    except:
                        logger.info("[EBP+24]: 0x%x", mycpu.EBP + 24)

                    logger.info("--")
Example #33
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