def queue_run(self, run_type, ep, run_args=[]): run = Run() if not isinstance(run_type, str): raise ApiEmuError('Invalid run type') if not isinstance(ep, int): raise ApiEmuError('Invalid run entry point') if not any((isinstance(run_args, list), isinstance(run_args, tuple))): raise ApiEmuError('Invalid run args') run.type = run_type run.start_addr = ep run.args = run_args self.emu.add_run(run)
def run_shellcode(self, sc_addr, offset=0): """ Begin emulating position independent code (i.e. shellcode) to prepare for emulation """ target = None for sc_path, _sc_addr, size in self.pic_buffers: if _sc_addr == sc_addr: target = _sc_addr break if not target: raise Win32EmuError("Invalid shellcode address") stack_commit = 0x4000 self.stack_base, stack_addr = self.alloc_stack(stack_commit) self.set_func_args(self.stack_base, self.return_hook, 0x7000) run = Run() run.type = "shellcode" run.start_addr = sc_addr + offset run.instr_cnt = 0 args = [ self.mem_map(1024, tag="emu.shellcode_arg_%d" % (i), base=0x41420000 + i) for i in range(4) ] run.args = args self.reg_write(_arch.X86_REG_ECX, 1024) self.add_run(run) # Create an empty process object for the shellcode if none is # supplied container = self.init_container_process() if container: self.processes.append(container) self.curr_process = container else: p = objman.Process(self) self.processes.append(p) self.curr_process = p mm = self.get_address_map(sc_addr) if mm: mm.set_process(self.curr_process) t = objman.Thread(self, stack_base=self.stack_base, stack_commit=stack_commit) self.om.objects.update({t.address: t}) self.curr_process.threads.append(t) self.curr_thread = t peb = self.alloc_peb(self.curr_process) # Set the TEB self.init_teb(t, peb) self.start()
def next_driver_func(self, drv): func_addr = None func_handler = None if self.curr_run.type is not None: self.curr_run.ret_val = self.get_return_val() # Check if theres anything in the run queue if len(self.run_queue): return if not self.all_entrypoints: return # TODO right now just use the first device object that was created dev = None if len(drv.devices): dev = drv.devices[0] # Run any remaining IRP handlers for hdlr, i in ( (self.irp_mj_create, ddk.IRP_MJ_CREATE), (self.irp_mj_dev_io, ddk.IRP_MJ_DEVICE_CONTROL), (self.irp_mj_read, ddk.IRP_MJ_READ), (self.irp_mj_write, ddk.IRP_MJ_WRITE), (self.irp_mj_close, ddk.IRP_MJ_CLOSE), (self.irp_mj_cleanup, ddk.IRP_MJ_CLEANUP), ): # Did we run this mj func yet? if i not in [r.type for r in self.runs]: func_handler = hdlr func_addr = int(drv.mj_funcs[i]) if not func_addr: continue break if len(self.delayed_runs): [self.add_run(r) for r in self.delayed_runs] self.delayed_runs = [] if not func_addr or not dev: # We are done here, call the unload routine self.driver_unload(drv) return irp = func_handler(func_addr, dev) run = Run() run.type = i run.start_addr = func_addr run.instr_cnt = 0 run.args = (dev.address, irp.address) self.add_run(run)
def driver_unload(self, drv): """ Call the unload routine for a driver """ if not drv.on_unload or drv.unload_called: self.on_emu_complete() return stk_ptr = self.get_stack_ptr() self.set_func_args(stk_ptr, self.exit_hook, drv.address) self.set_pc(drv.on_unload) run = Run() run.type = EP_DRIVER_UNLOAD run.start_addr = drv.on_unload run.instr_cnt = 0 run.args = (drv.address, ) run.ret_val = None self.add_run(run) drv.unload_called = True
def run_module(self, module, all_entrypoints=False): """ Begin emulating a previously loaded module Arguments: module: Module to emulate """ if not module: self.stop() raise Win32EmuError('Module not found') # Check if any TLS callbacks exist, these run before the module's entry point tls = module.get_tls_callbacks() for i, cb_addr in enumerate(tls): base = module.get_base() if base < cb_addr < base + module.get_image_size(): run = Run() run.start_addr = cb_addr run.type = 'tls_callback_%d' % (i) run.args = [base, DLL_PROCESS_ATTACH, 0] self.add_run(run) # Queue up the module's main entry point ep = module.base + module.ep run = Run() run.start_addr = ep main_exe = None if not module.is_exe(): run.args = [module.base, DLL_PROCESS_ATTACH, 0] run.type = 'dll_entry.DLL_PROCESS_ATTACH' container = self.init_container_process() if container: self.processes.append(container) self.curr_process = container else: run.type = 'module_entry' main_exe = module run.args = [ self.mem_map(8, tag='emu.module_arg_%d' % (i)) for i in range(4) ] if main_exe: self.user_modules = [main_exe] + self.user_modules self.add_run(run) if all_entrypoints: # Only emulate a subset of all the exported functions # There are some modules (such as the windows kernel) with # thousands of exports exports = [ k for k in module.get_exports()[:MAX_EXPORTS_TO_EMULATE] ] if exports: args = [ self.mem_map(8, tag='emu.export_arg_%d' % (i), base=0x41420000) for i in range(4) ] # noqa for exp in exports: if exp.name in ('DllMain', 'ServiceMain'): continue run = Run() if exp.name: fn = exp.name else: fn = 'no_name' run.type = 'export.%s' % (fn) run.start_addr = exp.address # Here we set dummy args to pass into the export function run.args = args # Store these runs and only queue them before the unload # routine this is because some exports may not be ready to # be called yet self.add_run(run) # Create an empty process object for the module if none is # supplied if len(self.processes) == 0: p = objman.Process(self, path=module.get_emu_path(), base=module.base, pe=module, cmdline=self.command_line) self.curr_process = p self.om.objects.update({p.address: p}) mm = self.get_address_map(module.base) if mm: mm.process = self.curr_process t = objman.Thread(self, stack_base=self.stack_base, stack_commit=module.stack_commit) self.om.objects.update({t.address: t}) self.curr_process.threads.append(t) self.curr_thread = t peb = self.alloc_peb(self.curr_process) # Set the TEB self.init_teb(t, peb) # Begin emulation self.start()
def prepare_module_for_emulation(self, module, all_entrypoints, entrypoints): if not module: self.stop() raise Win32EmuError("Module not found") # Check if any TLS callbacks exist, these run before the module's entry point tls = module.get_tls_callbacks() for i, cb_addr in enumerate(tls): base = module.get_base() if base < cb_addr < base + module.get_image_size(): run = Run() run.start_addr = cb_addr run.type = "tls_callback_%d" % (i) run.args = [base, DLL_PROCESS_ATTACH, 0] self.add_run(run) ep = module.base + module.ep run = Run() run.start_addr = ep main_exe = None if not module.is_exe(): run.args = [module.base, DLL_PROCESS_ATTACH, 0] run.type = "dll_entry.DLL_PROCESS_ATTACH" container = self.init_container_process() if container: self.processes.append(container) self.curr_process = container else: run.type = "module_entry" main_exe = module run.args = [ self.mem_map(8, tag="emu.module_arg_%d" % (i)) for i in range(4) ] if main_exe: self.user_modules = [main_exe] + self.user_modules # we consider this run only if all entry_points is selected or DLL_PROCESS_ATTACH is in the entrypoints if all_entrypoints or "DLL_PROCESS_ATTACH" in entrypoints: self.add_run(run) if all_entrypoints or entrypoints: # Only emulate a subset of all the exported functions # There are some modules (such as the windows kernel) with # thousands of exports exports = [k for k in module.get_exports()[:MAX_EXPORTS_TO_EMULATE]] if exports: args = [ self.mem_map(8, tag="emu.export_arg_%d" % (i), base=0x41420000) for i in range(4) ] # noqa for exp in exports: if exp.name in ("DllMain",): continue if all_entrypoints or exp.name in entrypoints: run = Run() if exp.name: fn = exp.name else: fn = "no_name" run.type = "export.%s" % (fn) run.start_addr = exp.address if exp.name == "ServiceMain": # ServiceMain accepts a (argc, argv) pair like main(). # # now, we're not exactly sure if we're in A or W mode. # maybe there are some hints we could take to guess this. # instead, we'll assume W mode and use default service name "IPRIP". # # hack: if we're actually in A mode, then string routines # will think the service name is "I" which isn't perfect, # but might still be good enough. # # layout: # argc: 1 # argv: # 0x00: (argv[0]) pointer to +0x10 -+ # 0x04/08: (argv[1]) 0x0 | # 0x10: "IPRIP" <------------------+ svc_name = "IPRIP\x00".encode("utf-16le") argc = 1 argv = self.mem_map( len(svc_name) + 0x10, tag="emu.export_ServiceMain_argv", base=0x41420000, ) self.write_ptr(argv, argv + 0x10) self.mem_write(argv + 0x10, svc_name) run.args = [argc, argv] else: # Here we set dummy args to pass into the export function run.args = args # Store these runs and only queue them before the unload # routine this is because some exports may not be ready to # be called yet self.add_run(run) return
def run_module(self, module, all_entrypoints=False): """ Begin emulation fo a previously loaded kernel module """ self.all_entrypoints = all_entrypoints # Create the service key for the driver drv = self.create_driver_object(pe=module) svc_key = self.regman.create_key(drv.get_reg_path()) # Create the values for the service key svc_key.create_value('ImagePath', regdefs.REG_EXPAND_SZ, module.get_emu_path()) svc_key.create_value('Type', regdefs.REG_DWORD, 0x1) # SERVICE_KERNEL_DRIVER svc_key.create_value('Start', regdefs.REG_DWORD, 0x3) # SERVICE_DEMAND_START svc_key.create_value('ErrorControl', regdefs.REG_DWORD, 0x1) # SERVICE_ERROR_NORMAL # Create the parameters subkey self.regman.create_key(drv.get_reg_path() + '\\Parameters') if module.ep > 0: ep = module.base + module.ep run = Run() run.type = EP_DRIVER_ENTRY run.start_addr = ep run.instr_cnt = 0 run.args = [drv.address, drv.reg_path_ptr] self.add_run(run) if self.all_entrypoints: # Only emulate a subset of all the exported functions # There are some modules (such as the windows kernel) with thousands of exports exports = [ k for k in module.get_exports()[:MAX_EXPORTS_TO_EMULATE] ] if exports: args = [ self.mem_map(8, tag='emu.export_arg_%d' % (i)) for i in range(4) ] for exp in exports: run = Run() if exp.name: fn = exp.name else: fn = 'no_name' run.type = 'export.%s' % (fn) run.start_addr = exp.address # Here we set dummy args to pass into the export function run.args = args # Store these runs and only queue them before the unload routine # this is because some exports may not be ready to be called yet self.delayed_runs.append(run) self.start()