def block_begin(params): global cm from api import CallbackManager import api from ipython_shell import start_shell cpu_index = params["cpu_index"] cpu = params["cpu"] tb = params["tb"] pgd = api.get_running_process(cpu_index) start_shell() if api.get_os_bits() == 32: pyrebox_print("Adding optimized callback at 0x100218f\n") cm.add_callback(CallbackManager.BLOCK_BEGIN_CB, optimized_block_begin, name="block_begin_optimized", addr=0x100218f, pgd=pgd) elif api.get_os_bits() == 64: pyrebox_print("Adding optimized callback at 0xfffff96000139f20\n") cm.add_callback(CallbackManager.BLOCK_BEGIN_CB, optimized_block_begin, name="block_begin_optimized", addr=0xfffff96000139f20, pgd=pgd) api.stop_monitoring_process(pgd) pyrebox_print("Stopped monitoring process\n") cm.rm_callback("block_begin") pyrebox_print("Unregistered callback\n")
def ntopenprocess(params, pid, proc, update_vads): # OUT PHANDLE ProcessHandle, # IN ACCESS_MASK AccessMask, # IN POBJECT_ATTRIBUTES ObjectAttributes, # IN PCLIENT_ID ClientId ); from mw_monitor_classes import mwmon import api cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = api.get_running_process(cpu_index) # Read the first parameter (process handle) params = read_parameters(cpu, 1) # Set callback on return address callback_name = mwmon.cm.generate_callback_name("ntopenprocess_ret") callback_function = functools.partial(ntopenprocessret, pid=pid, callback_name=callback_name, proc_hdl_p=params[1], proc=proc, update_vads=update_vads) mwmon.cm.add_callback(api.CallbackManager.INSN_BEGIN_CB, callback_function, name=callback_name, addr=params[0], pgd=pgd)
def __handle_host_request_exec_args_linux(self, cpu_index, cpu): """ Handle the host_request_exec_args interface call. Argument in EAX: the buffer to write to Argument in EBX: the max size of the buffer to write to Returns number of bytes written in EAX, or -1 if the call failed. """ if isinstance(cpu, X86CPU): buf = cpu.EAX size = cpu.EBX elif isinstance(cpu, X64CPU): buf = cpu.RAX size = cpu.RBX TARGET_LONG_SIZE = api.get_os_bits() / 8 args = self.__file_to_execute["args"] argv_size = TARGET_LONG_SIZE * (len(args) + 1) + sum(len(x) + 1 for x in args) if argv_size > self.__agent_buffer_size: raise ValueError("The size of the args should not exceed %d bytes" % self.__agent_buffer_size) # self.__printer("GuestAgentPlugin: host_request_exec_args(0x%08x, %d) # called" % (buf, size)) pgd = api.get_running_process(cpu_index) try: # Security check: the buffer should be located on the allowed # boundaries if self.__check_buffer_validity(buf, size): self.__write_strings_array( pgd, buf, args) if isinstance(cpu, X86CPU): api.w_r( cpu_index, "EAX", argv_size) elif isinstance(cpu, X64CPU): api.w_r( cpu_index, "RAX", argv_size) else: self.__printer("HostFilesPlugin: Declared buffer or buffer size are not" + "within the allowed boundaries %x (%x)" % (buf, size)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) except Exception as ex: self.__printer( "HostFilesPlugin: Exception %s while trying to write file args to guest" % (str(ex))) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1)
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()
def tlb_exec(params): global cm global counter import api cpu = params["cpu"] addr = params["vaddr"] pgd = api.get_running_process(cpu.CPU_INDEX) pyrebox_print("TLB exec, PGD %x Addr %x\n" % (pgd, addr))
def my_function(params): global cm import api from ipython_shell import start_shell cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = api.get_running_process(cpu_index) pyrebox_print("Insn end at (%x) %x\n" % (pgd, cpu.PC)) start_shell()
def op_insn_begin(params): global cm import api cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = api.get_running_process(cpu_index) pyrebox_print("Process %x hit the callback at %x\n" % (pgd, cpu.PC)) cm.rm_callback("insn_begin_optimized") pyrebox_print("Unregistered callback\n")
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)
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))
def __handle_host_read(self, cpu_index, cpu): """ Handle the host_read interface call. Argument in EAX: The file descriptor. Argument in EBX: Pointer to the buffer (VA) where bytes should be read into. Argument in ECX: Size of the buffer. Returns number of bytes read in EAX, or -1 if the call failed. """ if isinstance(cpu, X86CPU): fd = cpu.EAX buf = cpu.EBX size = cpu.ECX elif isinstance(cpu, X64CPU): fd = cpu.RAX buf = cpu.RBX size = cpu.RCX # self.__printer("GuestAgentPlugin: host_read(%d, 0x%08x, %d) called" % # (fd, buf, size)) if fd not in self.__file_descriptors: self.__printer( "HostFilesPlugin: host_read tried to access invalid file descriptor %d" % fd) return pgd = api.get_running_process(cpu_index) try: data = self.__file_descriptors[fd].read(size) # Security check: the buffer should be located on the allowed # boundaries if self.__check_buffer_validity(buf, size): api.w_va(pgd, buf, data, len(data)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", len(data)) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", len(data)) else: self.__printer("HostFilesPlugin: Declared buffer or buffer size are not" + "within the allowed boundaries %x (%x)" % (buf, size)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) except Exception as ex: self.__printer( "HostFilesPlugin: Exception %s while trying to read from file descriptor %d" % (str(ex), fd)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1)
def my_function(params): global cm import api from ipython_shell import start_shell cpu_index = params["cpu_index"] cpu = params["cpu"] tb = params["tb"] cur_pc = params["cur_pc"] next_pc = params["next_pc"] pgd = api.get_running_process(cpu_index) pyrebox_print("Block end at (%x) %x -> %x\n" % (pgd, cur_pc, next_pc)) start_shell()
def optimized_block_begin(params): global cm import api cpu_index = params["cpu_index"] cpu = params["cpu"] tb = params["tb"] assert(cpu.PC == 0x100218f or cpu.PC == 0xfffff96000139f20) pgd = api.get_running_process(cpu_index) pyrebox_print("Process %x hit the callback at %x\n" % (pgd, cpu.PC)) cm.rm_callback("block_begin_optimized") pyrebox_print("Unregistered callback\n")
def insn_begin(params): global cm global pyrebox_print cpu_index = params["cpu_index"] cpu = params["cpu"] if cpu.PC == 0x100218f and cm.callback_exists("insn_begin"): pgd = api.get_running_process(cpu_index) pyrebox_print("Process %x hit the callback at 0x100218f" % pgd) api.stop_monitoring_process(pgd) pyrebox_print("Stopped monitoring process") cm.rm_callback("insn_begin") else: print("This message should never be printed")
def insn_begin(params): global cm from api import CallbackManager import api cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = api.get_running_process(cpu_index) pyrebox_print("Adding insn optimized callback at %x\n" % cpu.PC) cm.add_callback(CallbackManager.INSN_BEGIN_CB, op_insn_begin, name="insn_begin_optimized", addr=cpu.PC, pgd=pgd) api.stop_monitoring_process(pgd) pyrebox_print("Stopped monitoring process\n") cm.rm_callback("insn_begin") pyrebox_print("Unregistered callback\n")
def ntallocatevirtualmemory_ret(params, pid, callback_name, mapping_proc=None, base_addr_p=None, zerobits=None, size_p=None, aloc_type=None, access=None, proc=None, update_vads=None): from mw_monitor_classes import mwmon 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) # Now, dereference all the output pointers # base and size_p depend on 32-bit vs. 64 bit. This should be turned into # 8 bytes for 64 bit guest. if base_addr_p != 0: base = dereference_target_long(base_addr_p, pgd) else: base = 0 if size_p != 0: size = dereference_target_long(size_p, pgd) else: size = 0 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] NtAllocateVirtualMemory: Base: %08x Size: %08x Protect: %08x\n" % (pid, base, size, access)) elif TARGET_LONG_SIZE == 8: f.write("[PID: %x] NtAllocateVirtualMemory: Base: %016x Size: %016x Protect: %016x\n" % (pid, base, size, access)) if update_vads: proc.update_vads()
def my_function(params): global cm global counter import api cpu_index = params["cpu_index"] cpu = params["cpu"] tb = params["tb"] pgd = api.get_running_process(cpu_index) pyrebox_print("Process %x hit the callback at %x\n" % (pgd, cpu.PC)) counter = counter + 1 if counter >= 100: api.stop_monitoring_process(pgd) pyrebox_print("Stopped monitoring process\n") cm.rm_callback("block_begin") pyrebox_print("Unregistered callback\n")
def __handle_host_request_exec_path(self, cpu_index, cpu): """ Handle the host_request_exec_path interface call. Argument in EAX: the buffer to write to Argument in EBX: the max size of the buffer to write to Returns number of bytes written in EAX, or -1 if the call failed. """ if isinstance(cpu, X86CPU): buf = cpu.EAX size = cpu.EBX elif isinstance(cpu, X64CPU): buf = cpu.RAX size = cpu.RBX # self.__printer("GuestAgentPlugin: host_request_exec_path(0x%08x, %d) # called" % (buf, size)) pgd = api.get_running_process(cpu_index) try: # Security check: the buffer should be located on the allowed # boundaries if self.__check_buffer_validity(buf, size): api.w_va(pgd, buf, self.__file_to_execute[ "path"] + "\x00", len(self.__file_to_execute["path"]) + 1) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", len( self.__file_to_execute["path"]) + 1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", len( self.__file_to_execute["path"]) + 1) else: self.__printer("HostFilesPlugin: Declared buffer or buffer size are not" + "within the allowed boundaries %x (%x)" % (buf, size)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) except Exception as ex: self.__printer( "HostFilesPlugin: Exception %s while trying to write file path to guest" % (str(ex))) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1)
def dump(cpu_index=None, cpu=None, 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 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
def ntunmapviewofsection(params, pid, proc, update_vads): 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"] # IN HANDLE ProcessHandle, # IN PVOID BaseAddress); # Search for the map, and deactivate it pgd = api.get_running_process(cpu_index) ret_addr, proc_handle, base = read_parameters(cpu, 2) # Load volatility address space addr_space = get_addr_space(pgd) # Get list of processes, and filter out by the process that triggered the # call (current process id) eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid] # Initialize proc_obj, that will point to the object of the referenced # process, and section_obj, idem proc_obj = None # Search handle table for the caller process for task in eprocs: if task.UniqueProcessId == pid: if (TARGET_LONG_SIZE == 4 and proc_handle == 0xffffffff) or \ (TARGET_LONG_SIZE == 8 and proc_handle == 0xffffffffffffffff): proc_obj = task break elif task.UniqueProcessId == pid and task.ObjectTable.HandleTableList: for handle in task.ObjectTable.handles(): if handle.is_valid(): if handle.HandleValue == proc_handle and handle.get_object_type() == "Process": proc_obj = handle.dereference_as("_EPROCESS") break break mapping_proc = None if proc_obj is not None: for proc in mwmon.data.procs: if proc.pid == proc_obj.UniqueProcessId: mapping_proc = proc break if mapping_proc is not None: for m in mapping_proc.section_maps: if m.base == base and m.is_active(): m.deactivate() 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] NtUnmapViewOfSection: Base: %08x Size: %08x / Section: %s\n" % (pid, base, m.size, m.section.backing_file)) elif (TARGET_LONG_SIZE == 8): f.write("[PID: %x] NtUnmapViewOfSection: Base: %16x Size: %16x / Section: %s\n" % (pid, base, m.size, m.section.backing_file)) if update_vads: proc.update_vads()
def module_entry_point(params): ''' Callback on the entry point of the main module being monitored ''' global APITRACER_ANTI_STOLEN global APITRACER_ENABLE_JMP global APITRACER_ENABLE_RET global cm global pyrebox_print import os from api import CallbackManager import api from interproc import interproc_data # Get pameters cpu_index = params["cpu_index"] cpu = params["cpu"] # Get running process pgd = api.get_running_process(cpu_index) new_proc = interproc_data.get_process_by_pgd(pgd) pid = new_proc.get_pid() pyrebox_print("Initializing API tracer for process %x" % pgd) cb_func = functools.partial(opcodes, proc=new_proc) #E8 cw CALL rel16 Call near, relative, displacement relative to next instruction #E8 cd CALL rel32 Call near, relative, displacement relative to next instruction #FF /2 CALL r/m16 Call near, absolute indirect, address given in r/m16 #FF /2 CALL r/m32 Call near, absolute indirect, address given in r/m32 #9A cd CALL ptr16:16 Call far, absolute, address given in operand #9A cp CALL ptr16:32 Call far, absolute, address given in operand #FF /3 CALL m16:16 Call far, absolute indirect, address given in m16:16 #FF /3 CALL m16:32 Call far, absolute indirect, address given in m16:32 cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="call_e8_%x" % pid), name="call_e8_%x" % pid, start_opcode=0xE8, end_opcode=0xE8) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="call_ff_%x" % pid), name="call_ff_%x" % pid, start_opcode=0xFF, end_opcode=0xFF) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="call_9a_%x" % pid), name="call_9a_%x" % pid, start_opcode=0x9A, end_opcode=0x9A) cm.add_trigger("call_e8_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("call_e8_%x" % pid, "pgd", pgd) cm.set_trigger_var("call_e8_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("call_ff_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("call_ff_%x" % pid, "pgd", pgd) cm.set_trigger_var("call_ff_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("call_9a_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("call_9a_%x" % pid, "pgd", pgd) cm.set_trigger_var("call_9a_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) #C3 RET NP Valid Valid Near return to calling procedure. #CB RET NP Valid Valid Far return to calling procedure. #C2 iw RET imm16 I Valid Valid Near return to calling procedure and pop imm16 bytes from stack. #CA iw RET imm16 I Valid Valid Far return to calling procedure and pop imm16 bytes from stack. if APITRACER_ENABLE_RET: cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="ret_c3_%x" % pid), name="ret_c3_%x" % pid, start_opcode=0xC3, end_opcode=0xC3) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="ret_cb_%x" % pid), name="ret_cb_%x" % pid, start_opcode=0xCB, end_opcode=0xCB) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="ret_c2_%x" % pid), name="ret_c2_%x" % pid, start_opcode=0xC2, end_opcode=0xC2) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="ret_ca_%x" % pid), name="ret_ca_%x" % pid, start_opcode=0xCA, end_opcode=0xCA) cm.add_trigger("ret_c3_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("ret_c3_%x" % pid, "pgd", pgd) cm.set_trigger_var("ret_c3_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("ret_cb_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("ret_cb_%x" % pid, "pgd", pgd) cm.set_trigger_var("ret_cb_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("ret_c2_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("ret_c2_%x" % pid, "pgd", pgd) cm.set_trigger_var("ret_c2_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("ret_ca_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("ret_ca_%x" % pid, "pgd", pgd) cm.set_trigger_var("ret_ca_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) #EB cb JMP rel8 Jump short, RIP = RIP + 8-bit displacement sign extended to 64-bits #E9 cw JMP rel16 Jump near, relative, displacement relative to next instruction. Not supported in 64-bit mode. #E9 cd JMP rel32 Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits #FF /4 JMP r/m16 Jump near, absolute indirect, address = zeroextended r/m16. Not supported in 64-bit mode. #FF /4 JMP r/m32 Jump near, absolute indirect, address given in r/m32. Not supported in 64-bit mode. #FF /4 JMP r/m64 Jump near, absolute indirect, RIP = 64-Bit offset from register or memory #EA cd JMP ptr16:16 Jump far, absolute, address given in operand #EA cp JMP ptr16:32 Jump far, absolute, address given in operand #FF /5 JMP m16:16 Jump far, absolute indirect, address given in m16:16 #FF /5 JMP m16:32 Jump far, absolute indirect, address given in m16:32. #FF /5 JMP m16:64 Jump far, absolute indirect, address given in m16:64 if APITRACER_ENABLE_JMP: cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="jmp_e9_%x" % pid), name="jmp_e9_%x" % pid, start_opcode=0xE9, end_opcode=0xE9) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="jmp_ea_%x" % pid), name="jmp_ea_%x" % pid, start_opcode=0xEA, end_opcode=0xEA) cm.add_callback(CallbackManager.OPCODE_RANGE_CB, functools.partial(cb_func, cb_name="jmp_eb_%x" % pid), name="jmp_eb_%x" % pid, start_opcode=0xEB, end_opcode=0xEB) cm.add_trigger("jmp_e9_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("jmp_e9_%x" % pid, "pgd", pgd) cm.set_trigger_var("jmp_e9_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("jmp_ea_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("jmp_ea_%x" % pid, "pgd", pgd) cm.set_trigger_var("jmp_ea_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) cm.add_trigger("jmp_eb_%x" % pid, "mw_monitor2/trigger_jmp_call_ret_tracer.so") cm.set_trigger_var("jmp_eb_%x" % pid, "pgd", pgd) cm.set_trigger_var("jmp_eb_%x" % pid, "enable_ff_jmp", 1 if APITRACER_ENABLE_JMP else 0) # Start monitoring process api.start_monitoring_process(pgd)
def __handle_host_request_exec_env_linux(self, cpu_index, cpu): """ Handle the host_request_exec_env interface call. Argument in EAX: the buffer to write to Argument in EBX: the max size of the buffer to write to Returns number of bytes written in EAX, or -1 if the call failed. """ if isinstance(cpu, X86CPU): buf = cpu.EAX size = cpu.EBX elif isinstance(cpu, X64CPU): buf = cpu.RAX size = cpu.RBX TARGET_LONG_SIZE = api.get_os_bits() / 8 env = self.__file_to_execute["env"] pgd = api.get_running_process(cpu_index) # self.__printer("GuestAgentPlugin: host_request_exec_env(0x%08x, %d) # called" % (buf, size)) if len(env) > 0: env = ["{:s}={:s}".format(k, v) for k, v in env.items()] env_size = sum(len(x) + 1 for x in env) + TARGET_LONG_SIZE * (len(env) + 1) try: # Security check: the buffer should be located on the allowed # boundaries if self.__check_buffer_validity(buf, size): self.__write_strings_array( pgd, buf, env) if isinstance(cpu, X86CPU): api.w_r( cpu_index, "EAX", env_size) elif isinstance(cpu, X64CPU): api.w_r( cpu_index, "RAX", env_size) else: self.__printer("HostFilesPlugin: Declared buffer or buffer size are not" + "within the allowed boundaries %x (%x)" % (buf, size)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) except Exception as ex: self.__printer( "HostFilesPlugin: Exception %s while trying to write env vars to guest" % (str(ex))) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) else: if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", 0) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", 0)
def ntcreateprocess(params, pid, proc, update_vads): # This function interface is for NTCreateProcess. # NtCreateProcessEx has different interface, but # the parameters we need, are aligned. # From Vista onwards, kernel32.dll calls NtCreateUserProcess # instead of NtCreateProcess(Ex). # NtCreateProcess: # OUT PHANDLE ProcessHandle, # IN ACCESS_MASK DesiredAccess, # IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, # IN HANDLE ParentProcess, # IN BOOLEAN InheritObjectTable, # IN HANDLE SectionHandle OPTIONAL, # IN HANDLE DebugPort OPTIONAL, # IN HANDLE ExceptionPort OPTIONAL # NtCreateProcessEx # OUT PHANDLE ProcessHandle, # IN ACCESS_MASK DesiredAccess, # IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, # IN HANDLE ParentProcess, # IN ULONG Flags, # IN HANDLE SectionHandle OPTIONAL, # IN HANDLE DebugPort OPTIONAL, # IN HANDLE ExceptionPort OPTIONAL, # IN BOOLEAN InJob # NtCreateUserProcess # PHANDLE ProcessHandle, # PHANDLE ThreadHandle, # ACCESS_MASK ProcessDesiredAccess, # ACCESS_MASK ThreadDesiredAccess, # POBJECT_ATTRIBUTES ProcessObjectAttributes, # POBJECT_ATTRIBUTES ThreadObjectAttributes, # ULONG ulProcessFlags, # ULONG ulThreadFlags, # PRTL_USER_PROCESS_PARAMETERS RtlUserProcessParameters, # PPS_CREATE_INFO PsCreateInfo, # PPS_ATTRIBUTE_LIST PsAttributeList from mw_monitor_classes import mwmon import api cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = api.get_running_process(cpu_index) # Set callback on return address # Read the first parameter (process handle) params = read_parameters(cpu, 1) callback_name = mwmon.cm.generate_callback_name("ntcreateprocess_ret") # Arguments to callback: the callback name, so that it can unset it, the # process handle variable callback_function = functools.partial(ntcreateprocessret, pid=pid, callback_name=callback_name, proc_hdl_p=params[1], proc=proc, update_vads=update_vads) mwmon.cm.add_callback(api.CallbackManager.INSN_BEGIN_CB, callback_function, name=callback_name, addr=params[0], pgd=pgd)
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
def opcode_range(cpu_index,cpu,cur_pc,next_pc): global cm pgd = api.get_running_process(cpu_index) pyrebox_print("Opcode range callback (%x) PGD %x cur_pc %x next_pc %x\n" % (cpu_index,pgd,cur_pc,next_pc)) start_shell()
def my_function(cpu_index, cpu, tb, cur_pc, next_pc): global cm pgd = api.get_running_process(cpu_index) pyrebox_print("Block end at (%x) %x -> %x\n" % (pgd, cur_pc, next_pc)) start_shell()
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()
def ntvirtualprotect(params, pid, proc, update_vads): 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"] pgd = api.get_running_process(cpu_index) # IN HANDLE ProcessHandle, # IN OUT PVOID *BaseAddress, # IN OUT PULONG NumberOfBytesToProtect, # IN ULONG NewAccessProtection, # OUT PULONG OldAccessProtection ); # Keep a log of page permissions for each VAD. Log changes for every virtualprotect call. # Output this informaiton on the log file, signal the sections with # changed permissions. ret_addr, proc_handle, base_addr_p, size_p, new_access, old_access = read_parameters( cpu, 5) # Load volatility address space addr_space = get_addr_space(pgd) # Get call parameters base_addr = dereference_target_long(base_addr_p, pgd) size = dereference_target_long(size_p, pgd) # Get list of processes, and filter out by the process that triggered the # call (current process id) eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid] # Initialize proc_obj, that will point to the object of the referenced # process, and section_obj, idem proc_obj = None # Search handle table for the caller process for task in eprocs: if task.UniqueProcessId == pid: if (TARGET_LONG_SIZE == 4 and proc_handle == 0xffffffff) or \ (TARGET_LONG_SIZE == 8 and proc_handle == 0xffffffffffffffff): proc_obj = task break elif task.UniqueProcessId == pid and task.ObjectTable.HandleTableList: for handle in task.ObjectTable.handles(): if handle.is_valid(): if handle.HandleValue == proc_handle and handle.get_object_type() == "Process": proc_obj = handle.dereference_as("_EPROCESS") break break mapping_proc = None if proc_obj is not None: for proc in mwmon.data.procs: if proc.pid == proc_obj.UniqueProcessId: mapping_proc = proc break if mapping_proc is not None: for v in mapping_proc.vads: # If the block overlaps the vad: if base_addr >= v.start and base_addr < (v.start + v.size): v.update_page_access(base_addr, size, new_access) 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] NtVirtualProtect: Base: %08x Size: %08x NewProtect: %08x\n" % (pid, base_addr, size, new_access)) elif TARGET_LONG_SIZE == 8: f.write("[PID: %x] NtVirtualProtect: Base: %016x Size: %016x NewProtect: %016x\n" % (pid, base_addr, size, new_access)) if update_vads: proc.update_vads()
def ntopenprocessret(params, pid, callback_name, proc_hdl_p, proc, update_vads): import volatility.win32.tasks as tasks from mw_monitor_classes import mwmon from mw_monitor_classes import mw_monitor_start_monitoring_process from mw_monitor_classes import Process from api import get_running_process from utils import get_addr_space import api TARGET_LONG_SIZE = api.get_os_bits() / 8 cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = get_running_process(cpu_index) # First, remove callback mwmon.cm.rm_callback(callback_name) # Do not continue if EAX/RAX returns and invalid return code. if read_return_parameter(cpu) != 0: return # Load volatility address space addr_space = get_addr_space(pgd) # Get list of processes, and filter out by the process that triggered the # call (current process id) eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid] # Initialize proc_obj, that will point to the eprocess of the new created # process proc_obj = None # Dereference the output argument containing the hdl of the newly created # process proc_hdl = dereference_target_long(proc_hdl_p, pgd) # Search handle table for the new created process for task in eprocs: if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList: for handle in task.ObjectTable.handles(): if handle.is_valid() and handle.HandleValue == proc_hdl and handle.get_object_type() == "Process": proc_obj = handle.dereference_as("_EPROCESS") break break if proc_obj is not None: if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None: f = mwmon.interproc_text_log_handle f.write("[PID: %x] NtOpenProcess: %s - PID: %x - CR3: %x\n" % (pid, str(proc_obj.ImageFileName), int(proc_obj.UniqueProcessId), int(proc_obj.Pcb.DirectoryTableBase.v()))) # Check if we are already monitoring the process for proc in mwmon.data.procs: if proc.pid == int(proc_obj.UniqueProcessId): return new_proc = Process(str(proc_obj.ImageFileName)) new_proc.set_pid(int(proc_obj.UniqueProcessId)) new_proc.set_pgd(int(proc_obj.Pcb.DirectoryTableBase.v())) mw_monitor_start_monitoring_process(new_proc) else: if TARGET_LONG_SIZE == 4: mwmon.printer("Error while trying to retrieve EPROCESS for handle %x, PID %x, EAX: %x" % (proc_hdl, pid, cpu.EAX)) elif TARGET_LONG_SIZE == 8: mwmon.printer("Error while trying to retrieve EPROCESS for handle %x, PID %x, EAX: %x" % (proc_hdl, pid, cpu.RAX)) if update_vads: proc.update_vads() return
def ntwritevirtualmemory(params, pid, proc, update_vads, reverse=False): import volatility.win32.tasks as tasks from mw_monitor_classes import mwmon from mw_monitor_classes import Injection import api from utils import get_addr_space TARGET_LONG_SIZE = api.get_os_bits() / 8 cpu_index = params["cpu_index"] cpu = params["cpu"] # _In_ HANDLE ProcessHandle, # _In_ PVOID BaseAddress, # _In_ PVOID Buffer, # _In_ SIZE_T NumberOfBytesToWrite, # _Out_opt_ PSIZE_T NumberOfBytesWritten pgd = api.get_running_process(cpu_index) # Read the parameters ret_addr, proc_hdl, remote_addr, local_addr, size, size_out = read_parameters( cpu, 5) local_proc = None # Get local proc for proc in mwmon.data.procs: if proc.pid == pid: local_proc = proc break # Try to get remote process from list of monitored processes remote_proc = None # Load volatility address space addr_space = get_addr_space(pgd) # Get list of processes, and filter out by the process that triggered the # call (current process id) eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid] # Initialize proc_obj, that will point to the eprocess of the new created # process proc_obj = None # Search handle table for the new created process for task in eprocs: if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList: for handle in task.ObjectTable.handles(): if handle.is_valid() and handle.HandleValue == proc_hdl and handle.get_object_type() == "Process": proc_obj = handle.dereference_as("_EPROCESS") break break if proc_obj is not None: for proc in mwmon.data.procs: if proc.pid == proc_obj.UniqueProcessId: remote_proc = proc break else: # Sometimes we get calls to this function over non-proc handles (e.g. type "Desktop") return if remote_proc is None: mwmon.printer( "[!] Could not obtain remote proc, or it is not being monitored") return elif local_proc is None: mwmon.printer( "[!] Could not obtain local proc, or it is not being monitored") return else: if reverse: data = None if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None: f = mwmon.interproc_text_log_handle if TARGET_LONG_SIZE == 4: f.write("[PID: %x] NtReadVirtualMemory: PID: %x - Addr: %08x <-- PID: %x Addr: %08x / Size: %08x\n" % (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size)) elif TARGET_LONG_SIZE == 8: f.write("[PID: %x] NtReadVirtualMemory: PID: %x - Addr: %16x <-- PID: %x Addr: %16x / Size: %16x\n" % (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size)) else: data = None if mwmon.interproc_text_log and mwmon.interproc_text_log_handle is not None: f = mwmon.interproc_text_log_handle if TARGET_LONG_SIZE == 4: f.write("[PID: %x] NtWriteVirtualMemory: PID: %x - Addr: %08x --> PID: %x Addr: %08x / Size: %08x\n" % (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size)) elif TARGET_LONG_SIZE == 8: f.write("[PID: %x] NtWriteVirtualMemory: PID: %x - Addr: %16x --> PID: %x Addr: %16x / Size: %16x\n" % (pid, local_proc.pid, local_addr, remote_proc.pid, remote_addr, size)) inj = Injection(remote_proc, remote_addr, local_proc, local_addr, size, data, reverse) local_proc.add_injection(inj) if update_vads: proc.update_vads()
def ntallocatevirtualmemory(params, pid, proc, update_vads): import volatility.win32.tasks as tasks from mw_monitor_classes import mwmon import api from api import CallbackManager from utils import get_addr_space TARGET_LONG_SIZE = api.get_os_bits() / 8 cpu_index = params["cpu_index"] cpu = params["cpu"] pgd = api.get_running_process(cpu_index) # IN HANDLE ProcessHandle, # IN OUT PVOID *BaseAddress, # IN ULONG ZeroBits, # IN OUT PULONG RegionSize, # IN ULONG AllocationType, # IN ULONG Protect ); # Only used for logging the event if not mwmon.interproc_text_log: return # Get call parameters (ret_addr, proc_handle, base_addr_p, zerobits, size_p, aloc_type, access) = read_parameters(cpu, 6) # Load volatility address space addr_space = get_addr_space(pgd) # Get list of processes, and filter out by the process that triggered the # call (current process id) eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid] # Initialize proc_obj, that will point to the object of the referenced # process, and section_obj, idem proc_obj = None # Search handle table for the caller process for task in eprocs: if task.UniqueProcessId == pid: if (TARGET_LONG_SIZE == 4 and proc_handle == 0xffffffff) or \ (TARGET_LONG_SIZE == 8 and proc_handle == 0xffffffffffffffff): proc_obj = task break elif task.UniqueProcessId == pid and task.ObjectTable.HandleTableList: for handle in task.ObjectTable.handles(): if handle.is_valid(): if handle.HandleValue == proc_handle and handle.get_object_type() == "Process": proc_obj = handle.dereference_as("_EPROCESS") break break mapping_proc = None if proc_obj is not None: for proc in mwmon.data.procs: if proc.pid == proc_obj.UniqueProcessId: mapping_proc = proc break if mapping_proc is not None: # Arguments to callback: the callback name, so that it can unset it, # the process handle variable, and the section handle callback_name = mwmon.cm.generate_callback_name( "ntallocatevirtualmemory_ret") callback_function = functools.partial(ntallocatevirtualmemory_ret, pid=pid, callback_name=callback_name, mapping_proc=mapping_proc, base_addr_p=base_addr_p, zerobits=zerobits, size_p=size_p, aloc_type=aloc_type, access=access, proc=proc, update_vads=update_vads) mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB, callback_function, name=callback_name, addr=ret_addr, pgd=pgd)
def tlb_exec(cpu, addr): global cm global counter import api pgd = api.get_running_process(cpu.CPU_INDEX) pyrebox_print("TLB exec, PGD %x Addr %x\n" % (pgd, addr))
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
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
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
def op_insn_begin(cpu_index, cpu): global cm pgd = api.get_running_process(cpu_index) pyrebox_print("Process %x hit the callback at %x\n" % (pgd, cpu.PC)) cm.rm_callback("insn_begin_optimized") pyrebox_print("Unregistered callback\n")
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("--")
def __handle_host_request_exec_env(self, cpu_index, cpu): """ Handle the host_request_exec_env interface call. Argument in EAX: the buffer to write to Argument in EBX: the max size of the buffer to write to Returns number of bytes written in EAX, or -1 if the call failed. """ if isinstance(cpu, X86CPU): buf = cpu.EAX size = cpu.EBX elif isinstance(cpu, X64CPU): buf = cpu.RAX size = cpu.RBX env = self.__file_to_execute["env"] pgd = api.get_running_process(cpu_index) # self.__printer("GuestAgentPlugin: host_request_exec_env(0x%08x, %d) # called" % (buf, size)) if len(env) > 0: env_size = 0 if len(env) > 0: env = ["{:s}={:s}".format(k, v) for k, v in env.items()] env_size = sum(len(x) + 1 for x in env) + 1 if env_size > self.__agent_buffer_size: raise ValueError( "The size of the env variables should not exceed %d bytes" % self.__agent_buffer_size) try: # Security check: the buffer should be located on the allowed # boundaries if self.__check_buffer_validity(buf, size): self.__write_env_strings_array(pgd, buf, env) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", env_size) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", env_size) else: self.__printer( "HostFilesPlugin: Declared buffer or buffer size are not" + "within the allowed boundaries %x (%x)" % (buf, size)) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) except Exception as ex: self.__printer( "HostFilesPlugin: Exception %s while trying to write env vars to guest" % (str(ex))) if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", -1) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", -1) else: if isinstance(cpu, X86CPU): api.w_r(cpu_index, "EAX", 0) elif isinstance(cpu, X64CPU): api.w_r(cpu_index, "RAX", 0)
def my_function(cpu_index,cpu): global cm pgd = api.get_running_process(cpu_index) pyrebox_print("Insn end at (%x) %x\n" % (pgd,cpu.PC)) start_shell()
def ntmapviewofsection(params, pid, proc, update_vads): import volatility.obj as obj import volatility.win32.tasks as tasks import volatility.plugins.overlays.windows.windows as windows from mw_monitor_classes import mwmon from mw_monitor_classes import Section from utils import get_addr_space import api from api import CallbackManager TARGET_LONG_SIZE = api.get_os_bits() / 8 cpu_index = params["cpu_index"] cpu = params["cpu"] # IN HANDLE SectionHandle, # IN HANDLE ProcessHandle, # IN OUT PVOID *BaseAddress OPTIONAL, # IN ULONG ZeroBits OPTIONAL, # IN ULONG CommitSize, # IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, # IN OUT PULONG ViewSize, # IN InheritDisposition, # IN ULONG AllocationType OPTIONAL, # IN ULONG Protect pgd = api.get_running_process(cpu_index) # Read the parameters ret_addr, section_handle, proc_handle, base_p, arg_3, arg_4, offset_p, size_p = read_parameters( cpu, 7) # Load volatility address space addr_space = get_addr_space(pgd) class _SECTION_OBJECT(obj.CType, windows.ExecutiveObjectMixin): def is_valid(self): return obj.CType.is_valid(self) addr_space.profile.object_classes.update( {'_SECTION_OBJECT': _SECTION_OBJECT}) # Get list of processes, and filter out by the process that triggered the # call (current process id) eprocs = [t for t in tasks.pslist(addr_space) if t.UniqueProcessId == pid] # Initialize proc_obj, that will point to the object of the referenced # process, and section_obj, idem proc_obj = None section_obj = None mapping_proc = None if (TARGET_LONG_SIZE == 4 and proc_handle == 0xffffffff) or (TARGET_LONG_SIZE == 8 and proc_handle == 0xffffffffffffffff): for proc in mwmon.data.procs: if proc.pid == pid: mapping_proc = proc break # Search handle table for the caller process for task in eprocs: if task.UniqueProcessId == pid and task.ObjectTable.HandleTableList: for handle in task.ObjectTable.handles(): if handle.is_valid(): if not mapping_proc and not proc_obj and \ handle.HandleValue == proc_handle and \ handle.get_object_type() == "Process": proc_obj = handle.dereference_as("_EPROCESS") elif handle.HandleValue == section_handle and handle.get_object_type() == "Section": # We dereference the object as _SECTION_OBJECT, although it is not a _SECTION_OBJECT but a # _SECTION, that is not present in the volatility overlay: # http://forum.sysinternals.com/section-object_topic24975.html # For a better reference see the comments on the Section class # in mw_monitor_classes.py section_obj = handle.dereference_as("_SECTION_OBJECT") if (proc_obj or mapping_proc) and section_obj: break break # proc_obj represents the process over which the section is mapped # section_object represents the section being mapped. if (proc_obj is not None or mapping_proc is not None) and section_obj is not None: mapped_sec = None if not mapping_proc: for proc in mwmon.data.procs: if proc.pid == proc_obj.UniqueProcessId: mapping_proc = proc break if mapping_proc is None: mwmon.printer("[!] The mapping process is not being monitored," + " a handle was obtained with an API different from " + "OpenProcess or CreateProcess") return for sec in mwmon.data.sections: if sec.get_offset() == section_obj.obj_offset: mapped_sec = sec break # If the section was not in our list, we create an entry if mapped_sec is None: mapped_sec = Section(pgd, section_obj) mwmon.data.sections.append(mapped_sec) # Record the actual map once we return back from the call and we can # dereference output parameters callback_name = mwmon.cm.generate_callback_name("mapviewofsection_ret") # Arguments to callback: the callback name, so that it can unset it, # the process handle variable, and the section handle callback_function = functools.partial(ntmapviewofsection_ret, pid=pid, callback_name=callback_name, mapping_proc=mapping_proc, mapped_sec=mapped_sec, base_p=base_p, size_p=size_p, offset_p=offset_p, proc=proc, update_vads=update_vads) mwmon.cm.add_callback(CallbackManager.INSN_BEGIN_CB, callback_function, name=callback_name, addr=ret_addr, pgd=pgd)