def prepare(self): """Prepare the environment for attachment with a jitter""" # Disassembler self.mdis = self.machine.dis_engine(bin_stream_vm(self.jitter.vm), lines_wd=1, loc_db=self.loc_db) # Symbexec engine ## Prepare symbexec engines self.symb = self.SYMB_ENGINE(self.jitter.cpu, self.jitter.vm, self.ir_arch, {}) self.symb.enable_emulated_simplifications() self.symb_concrete = EmulatedSymbExec( self.jitter.cpu, self.jitter.vm, self.ir_arch, {} ) ### Avoid side effects on jitter while using 'symb_concrete' self.symb_concrete.func_write = None ## Update registers value self.symb.symbols[self.ir_arch.IRDst] = ExprInt( getattr(self.jitter.cpu, self.ir_arch.pc.name), self.ir_arch.IRDst.size ) # Avoid memory write self.symb.func_write = None # Activate callback on each instr self.jitter.jit.set_options(max_exec_per_call=1, jit_maxline=1) self.jitter.exec_cb = self.callback # Clean jit cache to avoid multi-line basic blocks already jitted self.jitter.jit.clear_jitted_blocks()
def __init__(self, ir_arch, bs=None): super(JitCore_Python, self).__init__(ir_arch, bs) self.ir_arch = ir_arch # CPU & VM (None for now) will be set later expr_simp = ExpressionSimplifier() expr_simp.enable_passes(ExpressionSimplifier.PASS_COMMONS) self.symbexec = EmulatedSymbExec(None, None, self.ir_arch, {}, sb_expr_simp=expr_simp) self.symbexec.enable_emulated_simplifications()
def prepare(self): """Prepare the environment for attachment with a jitter""" # Disassembler self.mdis = self.machine.dis_engine(bin_stream_vm(self.jitter.vm), lines_wd=1) # Symbexec engine ## Prepare symbexec engines self.symb = self.SYMB_ENGINE(self.jitter.cpu, self.jitter.vm, self.ir_arch, {}) self.symb.enable_emulated_simplifications() self.symb_concrete = EmulatedSymbExec(self.jitter.cpu, self.jitter.vm, self.ir_arch, {}) ## Update registers value self.symb.symbols[self.ir_arch.IRDst] = ExprInt(getattr(self.jitter.cpu, self.ir_arch.pc.name), self.ir_arch.IRDst.size) # Avoid memory write self.symb.func_write = None # Activate callback on each instr self.jitter.jit.set_options(max_exec_per_call=1, jit_maxline=1) self.jitter.exec_cb = self.callback
def __init__(self, ir_arch, jit_type="gcc"): """Init an instance of jitter. @ir_arch: ir instance for this architecture @jit_type: JiT backend to use. Available options are: - "gcc" - "tcc" - "llvm" - "python" """ self.arch = ir_arch.arch self.attrib = ir_arch.attrib arch_name = ir_arch.arch.name # (ir_arch.arch.name, ir_arch.attrib) try: if arch_name == "x86": from miasm2.jitter.arch import JitCore_x86 as jcore elif arch_name == "arm": from miasm2.jitter.arch import JitCore_arm as jcore elif arch_name == "aarch64": from miasm2.jitter.arch import JitCore_aarch64 as jcore elif arch_name == "msp430": from miasm2.jitter.arch import JitCore_msp430 as jcore elif arch_name == "mips32": from miasm2.jitter.arch import JitCore_mips32 as jcore else: raise ValueError("unknown jit arch: %s" % arch_name) except ImportError: raise RuntimeError('Unsupported jit arch: %s' % arch_name) self.vm = VmMngr.Vm() self.cpu = jcore.JitCpu() self.ir_arch = ir_arch self.bs = bin_stream_vm(self.vm) self.symbexec = EmulatedSymbExec(self.cpu, self.vm, self.ir_arch, {}) self.symbexec.reset_regs() try: if jit_type == "tcc": from miasm2.jitter.jitcore_tcc import JitCore_Tcc as JitCore elif jit_type == "llvm": from miasm2.jitter.jitcore_llvm import JitCore_LLVM as JitCore elif jit_type == "python": from miasm2.jitter.jitcore_python import JitCore_Python as JitCore elif jit_type == "gcc": from miasm2.jitter.jitcore_gcc import JitCore_Gcc as JitCore else: raise ValueError("Unknown jitter %s" % jit_type) except ImportError: raise RuntimeError('Unsupported jitter: %s' % jit_type) self.jit = JitCore(self.ir_arch, self.bs) if isinstance(self.jit, JitCore_Cc_Base): self.jit.init_codegen(self.C_Gen(self.ir_arch)) elif jit_type == "python": self.jit.set_cpu_vm(self.cpu, self.vm) self.cpu.init_regs() self.vm.init_memory_page_pool() self.vm.init_code_bloc_pool() self.vm.init_memory_breakpoint() self.jit.load() self.cpu.vmmngr = self.vm self.cpu.jitter = self.jit self.stack_size = 0x10000 self.stack_base = 0x1230000 # Init callback handler self.breakpoints_handler = CallbackHandler() self.exceptions_handler = CallbackHandlerBitflag() self.init_exceptions_handler() self.exec_cb = None
class jitter: "Main class for JIT handling" C_Gen = CGen def __init__(self, ir_arch, jit_type="gcc"): """Init an instance of jitter. @ir_arch: ir instance for this architecture @jit_type: JiT backend to use. Available options are: - "gcc" - "tcc" - "llvm" - "python" """ self.arch = ir_arch.arch self.attrib = ir_arch.attrib arch_name = ir_arch.arch.name # (ir_arch.arch.name, ir_arch.attrib) try: if arch_name == "x86": from miasm2.jitter.arch import JitCore_x86 as jcore elif arch_name == "arm": from miasm2.jitter.arch import JitCore_arm as jcore elif arch_name == "aarch64": from miasm2.jitter.arch import JitCore_aarch64 as jcore elif arch_name == "msp430": from miasm2.jitter.arch import JitCore_msp430 as jcore elif arch_name == "mips32": from miasm2.jitter.arch import JitCore_mips32 as jcore else: raise ValueError("unknown jit arch: %s" % arch_name) except ImportError: raise RuntimeError('Unsupported jit arch: %s' % arch_name) self.vm = VmMngr.Vm() self.cpu = jcore.JitCpu() self.ir_arch = ir_arch self.bs = bin_stream_vm(self.vm) self.symbexec = EmulatedSymbExec(self.cpu, self.vm, self.ir_arch, {}) self.symbexec.reset_regs() try: if jit_type == "tcc": from miasm2.jitter.jitcore_tcc import JitCore_Tcc as JitCore elif jit_type == "llvm": from miasm2.jitter.jitcore_llvm import JitCore_LLVM as JitCore elif jit_type == "python": from miasm2.jitter.jitcore_python import JitCore_Python as JitCore elif jit_type == "gcc": from miasm2.jitter.jitcore_gcc import JitCore_Gcc as JitCore else: raise ValueError("Unknown jitter %s" % jit_type) except ImportError: raise RuntimeError('Unsupported jitter: %s' % jit_type) self.jit = JitCore(self.ir_arch, self.bs) if isinstance(self.jit, JitCore_Cc_Base): self.jit.init_codegen(self.C_Gen(self.ir_arch)) elif jit_type == "python": self.jit.set_cpu_vm(self.cpu, self.vm) self.cpu.init_regs() self.vm.init_memory_page_pool() self.vm.init_code_bloc_pool() self.vm.init_memory_breakpoint() self.jit.load() self.cpu.vmmngr = self.vm self.cpu.jitter = self.jit self.stack_size = 0x10000 self.stack_base = 0x1230000 # Init callback handler self.breakpoints_handler = CallbackHandler() self.exceptions_handler = CallbackHandlerBitflag() self.init_exceptions_handler() self.exec_cb = None def init_exceptions_handler(self): "Add common exceptions handlers" def exception_automod(jitter): "Tell the JiT backend to update blocks modified" self.jit.updt_automod_code(jitter.vm) self.vm.set_exception(0) return True def exception_memory_breakpoint(jitter): "Stop the execution and return an identifier" return ExceptionHandle.memoryBreakpoint() self.add_exception_handler(EXCEPT_CODE_AUTOMOD, exception_automod) self.add_exception_handler(EXCEPT_BREAKPOINT_INTERN, exception_memory_breakpoint) def add_breakpoint(self, addr, callback): """Add a callback associated with addr. @addr: breakpoint address @callback: function with definition (jitter instance) """ self.breakpoints_handler.add_callback(addr, callback) self.jit.add_disassembly_splits(addr) # De-jit previously jitted blocks self.jit.updt_automod_code_range(self.vm, [(addr, addr)]) def set_breakpoint(self, addr, *args): """Set callbacks associated with addr. @addr: breakpoint address @args: functions with definition (jitter instance) """ self.breakpoints_handler.set_callback(addr, *args) self.jit.add_disassembly_splits(addr) def remove_breakpoints_by_callback(self, callback): """Remove callbacks associated with breakpoint. @callback: callback to remove """ empty_keys = self.breakpoints_handler.remove_callback(callback) for key in empty_keys: self.jit.remove_disassembly_splits(key) def add_exception_handler(self, flag, callback): """Add a callback associated with an exception flag. @flag: bitflag @callback: function with definition (jitter instance) """ self.exceptions_handler.add_callback(flag, callback) def runbloc(self, pc): """Wrapper on JiT backend. Run the code at PC and return the next PC. @pc: address of code to run""" return self.jit.runbloc(self.cpu, pc, self.breakpoints_handler.callbacks) def runiter_once(self, pc): """Iterator on callbacks results on code running from PC. Check exceptions before breakpoints.""" self.pc = pc # Callback called before exec if self.exec_cb is not None: res = self.exec_cb(self) if res is not True: yield res # Check breakpoints old_pc = self.pc for res in self.breakpoints_handler.call_callbacks(self.pc, self): if res is not True: if isinstance(res, collections.Iterator): # If the breakpoint is a generator, yield it step by step for tmp in res: yield tmp else: yield res # Check exceptions (raised by breakpoints) exception_flag = self.get_exception() for res in self.exceptions_handler(exception_flag, self): if res is not True: if isinstance(res, collections.Iterator): for tmp in res: yield tmp else: yield res # If a callback changed pc, re call every callback if old_pc != self.pc: return # Exceptions should never be activated before run assert (self.get_exception() == 0) # Run the bloc at PC self.pc = self.runbloc(self.pc) # Check exceptions (raised by the execution of the block) exception_flag = self.get_exception() for res in self.exceptions_handler(exception_flag, self): if res is not True: if isinstance(res, collections.Iterator): for tmp in res: yield tmp else: yield res def init_run(self, pc): """Create an iterator on pc with runiter. @pc: address of code to run """ self.run_iterator = self.runiter_once(pc) self.pc = pc self.run = True def continue_run(self, step=False): """PRE: init_run. Continue the run of the current session until iterator returns or run is set to False. If step is True, run only one time. Return the iterator value""" while self.run: try: return self.run_iterator.next() except StopIteration: pass self.run_iterator = self.runiter_once(self.pc) if step is True: return None return None def init_stack(self): self.vm.add_memory_page(self.stack_base, PAGE_READ | PAGE_WRITE, "\x00" * self.stack_size, "Stack") sp = self.arch.getsp(self.attrib) setattr(self.cpu, sp.name, self.stack_base + self.stack_size) # regs = self.cpu.get_gpreg() # regs[sp.name] = self.stack_base+self.stack_size # self.cpu.set_gpreg(regs) def get_exception(self): return self.cpu.get_exception() | self.vm.get_exception() # commun functions def get_str_ansi(self, addr, max_char=None): """Get ansi str from vm. @addr: address in memory @max_char: maximum len""" l = 0 tmp = addr while ((max_char is None or l < max_char) and self.vm.get_mem(tmp, 1) != "\x00"): tmp += 1 l += 1 return self.vm.get_mem(addr, l) def get_str_unic(self, addr, max_char=None): """Get unicode str from vm. @addr: address in memory @max_char: maximum len""" l = 0 tmp = addr while ((max_char is None or l < max_char) and self.vm.get_mem(tmp, 2) != "\x00\x00"): tmp += 2 l += 2 s = self.vm.get_mem(addr, l) s = s[::2] # TODO: real unicode decoding return s def set_str_ansi(self, addr, s): """Set an ansi string in memory""" s = s + "\x00" self.vm.set_mem(addr, s) def set_str_unic(self, addr, s): """Set an unicode string in memory""" s = "\x00".join(list(s)) + '\x00' * 3 self.vm.set_mem(addr, s) @staticmethod def handle_lib(jitter): """Resolve the name of the function which cause the handler call. Then call the corresponding handler from users callback. """ fname = jitter.libs.fad2cname[jitter.pc] if fname in jitter.user_globals: func = jitter.user_globals[fname] else: log.debug('%r', fname) raise ValueError('unknown api', hex(jitter.pc), repr(fname)) ret = func(jitter) jitter.pc = getattr(jitter.cpu, jitter.ir_arch.pc.name) # Don't break on a None return if ret is None: return True else: return ret def handle_function(self, f_addr): """Add a brakpoint which will trigger the function handler""" self.add_breakpoint(f_addr, self.handle_lib) def add_lib_handler(self, libs, user_globals=None): """Add a function to handle libs call with breakpoints @libs: libimp instance @user_globals: dictionary for defined user function """ if user_globals is None: user_globals = {} self.libs = libs self.user_globals = user_globals for f_addr in libs.fad2cname: self.handle_function(f_addr) def eval_expr(self, expr): """Eval expression @expr in the context of the current instance. Side effects are passed on it""" self.symbexec.update_engine_from_cpu() ret = self.symbexec.apply_expr(expr) self.symbexec.update_cpu_from_engine() return ret
def __init__(self, ir_arch, jit_type="tcc"): """Init an instance of jitter. @ir_arch: ir instance for this architecture @jit_type: JiT backend to use. Available options are: - "tcc" - "gcc" - "llvm" - "python" """ self.arch = ir_arch.arch self.attrib = ir_arch.attrib arch_name = ir_arch.arch.name # (ir_arch.arch.name, ir_arch.attrib) try: if arch_name == "x86": from miasm2.jitter.arch import JitCore_x86 as jcore elif arch_name == "arm": from miasm2.jitter.arch import JitCore_arm as jcore elif arch_name == "aarch64": from miasm2.jitter.arch import JitCore_aarch64 as jcore elif arch_name == "msp430": from miasm2.jitter.arch import JitCore_msp430 as jcore elif arch_name == "mips32": from miasm2.jitter.arch import JitCore_mips32 as jcore else: raise ValueError("unknown jit arch: %s" % arch_name) except ImportError: raise RuntimeError('Unsupported jit arch: %s' % arch_name) self.vm = VmMngr.Vm() self.cpu = jcore.JitCpu() self.ir_arch = ir_arch self.bs = bin_stream_vm(self.vm) self.symbexec = EmulatedSymbExec(self.cpu, self.vm, self.ir_arch, {}) self.symbexec.reset_regs() try: if jit_type == "tcc": from miasm2.jitter.jitcore_tcc import JitCore_Tcc as JitCore elif jit_type == "llvm": from miasm2.jitter.jitcore_llvm import JitCore_LLVM as JitCore elif jit_type == "python": from miasm2.jitter.jitcore_python import JitCore_Python as JitCore elif jit_type == "gcc": from miasm2.jitter.jitcore_gcc import JitCore_Gcc as JitCore else: raise ValueError("Unknown jitter %s" % jit_type) except ImportError: raise RuntimeError('Unsupported jitter: %s' % jit_type) self.jit = JitCore(self.ir_arch, self.bs) if isinstance(self.jit, JitCore_Cc_Base): self.jit.init_codegen(self.C_Gen(self.ir_arch)) elif jit_type == "python": self.jit.set_cpu_vm(self.cpu, self.vm) self.cpu.init_regs() self.vm.init_memory_page_pool() self.vm.init_code_bloc_pool() self.vm.init_memory_breakpoint() self.jit.load() self.cpu.vmmngr = self.vm self.cpu.jitter = self.jit self.stack_size = 0x10000 self.stack_base = 0x1230000 # Init callback handler self.breakpoints_handler = CallbackHandler() self.exceptions_handler = CallbackHandlerBitflag() self.init_exceptions_handler() self.exec_cb = None
class jitter: "Main class for JIT handling" C_Gen = CGen def __init__(self, ir_arch, jit_type="tcc"): """Init an instance of jitter. @ir_arch: ir instance for this architecture @jit_type: JiT backend to use. Available options are: - "tcc" - "gcc" - "llvm" - "python" """ self.arch = ir_arch.arch self.attrib = ir_arch.attrib arch_name = ir_arch.arch.name # (ir_arch.arch.name, ir_arch.attrib) try: if arch_name == "x86": from miasm2.jitter.arch import JitCore_x86 as jcore elif arch_name == "arm": from miasm2.jitter.arch import JitCore_arm as jcore elif arch_name == "aarch64": from miasm2.jitter.arch import JitCore_aarch64 as jcore elif arch_name == "msp430": from miasm2.jitter.arch import JitCore_msp430 as jcore elif arch_name == "mips32": from miasm2.jitter.arch import JitCore_mips32 as jcore else: raise ValueError("unknown jit arch: %s" % arch_name) except ImportError: raise RuntimeError('Unsupported jit arch: %s' % arch_name) self.vm = VmMngr.Vm() self.cpu = jcore.JitCpu() self.ir_arch = ir_arch self.bs = bin_stream_vm(self.vm) self.symbexec = EmulatedSymbExec(self.cpu, self.vm, self.ir_arch, {}) self.symbexec.reset_regs() try: if jit_type == "tcc": from miasm2.jitter.jitcore_tcc import JitCore_Tcc as JitCore elif jit_type == "llvm": from miasm2.jitter.jitcore_llvm import JitCore_LLVM as JitCore elif jit_type == "python": from miasm2.jitter.jitcore_python import JitCore_Python as JitCore elif jit_type == "gcc": from miasm2.jitter.jitcore_gcc import JitCore_Gcc as JitCore else: raise ValueError("Unknown jitter %s" % jit_type) except ImportError: raise RuntimeError('Unsupported jitter: %s' % jit_type) self.jit = JitCore(self.ir_arch, self.bs) if isinstance(self.jit, JitCore_Cc_Base): self.jit.init_codegen(self.C_Gen(self.ir_arch)) elif jit_type == "python": self.jit.set_cpu_vm(self.cpu, self.vm) self.cpu.init_regs() self.vm.init_memory_page_pool() self.vm.init_code_bloc_pool() self.vm.init_memory_breakpoint() self.jit.load() self.cpu.vmmngr = self.vm self.cpu.jitter = self.jit self.stack_size = 0x10000 self.stack_base = 0x1230000 # Init callback handler self.breakpoints_handler = CallbackHandler() self.exceptions_handler = CallbackHandlerBitflag() self.init_exceptions_handler() self.exec_cb = None def init_exceptions_handler(self): "Add common exceptions handlers" def exception_automod(jitter): "Tell the JiT backend to update blocs modified" self.jit.updt_automod_code(jitter.vm) self.vm.set_exception(0) return True def exception_memory_breakpoint(jitter): "Stop the execution and return an identifier" return ExceptionHandle.memoryBreakpoint() self.add_exception_handler(EXCEPT_CODE_AUTOMOD, exception_automod) self.add_exception_handler(EXCEPT_BREAKPOINT_INTERN, exception_memory_breakpoint) def add_breakpoint(self, addr, callback): """Add a callback associated with addr. @addr: breakpoint address @callback: function with definition (jitter instance) """ self.breakpoints_handler.add_callback(addr, callback) self.jit.add_disassembly_splits(addr) # De-jit previously jitted blocks self.jit.updt_automod_code_range(self.vm, [(addr, addr)]) def set_breakpoint(self, addr, *args): """Set callbacks associated with addr. @addr: breakpoint address @args: functions with definition (jitter instance) """ self.breakpoints_handler.set_callback(addr, *args) self.jit.add_disassembly_splits(addr) def remove_breakpoints_by_callback(self, callback): """Remove callbacks associated with breakpoint. @callback: callback to remove """ empty_keys = self.breakpoints_handler.remove_callback(callback) for key in empty_keys: self.jit.remove_disassembly_splits(key) def add_exception_handler(self, flag, callback): """Add a callback associated with an exception flag. @flag: bitflag @callback: function with definition (jitter instance) """ self.exceptions_handler.add_callback(flag, callback) def runbloc(self, pc): """Wrapper on JiT backend. Run the code at PC and return the next PC. @pc: address of code to run""" return self.jit.runbloc(self.cpu, pc, self.breakpoints_handler.callbacks) def runiter_once(self, pc): """Iterator on callbacks results on code running from PC. Check exceptions before breakpoints.""" self.pc = pc # Callback called before exec if self.exec_cb is not None: res = self.exec_cb(self) if res is not True: yield res # Check breakpoints old_pc = self.pc for res in self.breakpoints_handler.call_callbacks(self.pc, self): if res is not True: if isinstance(res, collections.Iterator): # If the breakpoint is a generator, yield it step by step for tmp in res: yield tmp else: yield res # Check exceptions (raised by breakpoints) exception_flag = self.get_exception() for res in self.exceptions_handler(exception_flag, self): if res is not True: if isinstance(res, collections.Iterator): for tmp in res: yield tmp else: yield res # If a callback changed pc, re call every callback if old_pc != self.pc: return # Exceptions should never be activated before run assert(self.get_exception() == 0) # Run the bloc at PC self.pc = self.runbloc(self.pc) # Check exceptions (raised by the execution of the block) exception_flag = self.get_exception() for res in self.exceptions_handler(exception_flag, self): if res is not True: if isinstance(res, collections.Iterator): for tmp in res: yield tmp else: yield res def init_run(self, pc): """Create an iterator on pc with runiter. @pc: address of code to run """ self.run_iterator = self.runiter_once(pc) self.pc = pc self.run = True def continue_run(self, step=False): """PRE: init_run. Continue the run of the current session until iterator returns or run is set to False. If step is True, run only one time. Return the iterator value""" while self.run: try: return self.run_iterator.next() except StopIteration: pass self.run_iterator = self.runiter_once(self.pc) if step is True: return None return None def init_stack(self): self.vm.add_memory_page( self.stack_base, PAGE_READ | PAGE_WRITE, "\x00" * self.stack_size, "Stack") sp = self.arch.getsp(self.attrib) setattr(self.cpu, sp.name, self.stack_base + self.stack_size) # regs = self.cpu.get_gpreg() # regs[sp.name] = self.stack_base+self.stack_size # self.cpu.set_gpreg(regs) def get_exception(self): return self.cpu.get_exception() | self.vm.get_exception() # commun functions def get_str_ansi(self, addr, max_char=None): """Get ansi str from vm. @addr: address in memory @max_char: maximum len""" l = 0 tmp = addr while ((max_char is None or l < max_char) and self.vm.get_mem(tmp, 1) != "\x00"): tmp += 1 l += 1 return self.vm.get_mem(addr, l) def get_str_unic(self, addr, max_char=None): """Get unicode str from vm. @addr: address in memory @max_char: maximum len""" l = 0 tmp = addr while ((max_char is None or l < max_char) and self.vm.get_mem(tmp, 2) != "\x00\x00"): tmp += 2 l += 2 s = self.vm.get_mem(addr, l) s = s[::2] # TODO: real unicode decoding return s def set_str_ansi(self, addr, s): """Set an ansi string in memory""" s = s + "\x00" self.vm.set_mem(addr, s) def set_str_unic(self, addr, s): """Set an unicode string in memory""" s = "\x00".join(list(s)) + '\x00' * 3 self.vm.set_mem(addr, s) @staticmethod def handle_lib(jitter): """Resolve the name of the function which cause the handler call. Then call the corresponding handler from users callback. """ fname = jitter.libs.fad2cname[jitter.pc] if fname in jitter.user_globals: func = jitter.user_globals[fname] else: log.debug('%r', fname) raise ValueError('unknown api', hex(jitter.pc), repr(fname)) ret = func(jitter) jitter.pc = getattr(jitter.cpu, jitter.ir_arch.pc.name) # Don't break on a None return if ret is None: return True else: return ret def handle_function(self, f_addr): """Add a brakpoint which will trigger the function handler""" self.add_breakpoint(f_addr, self.handle_lib) def add_lib_handler(self, libs, user_globals=None): """Add a function to handle libs call with breakpoints @libs: libimp instance @user_globals: dictionary for defined user function """ if user_globals is None: user_globals = {} self.libs = libs self.user_globals = user_globals for f_addr in libs.fad2cname: self.handle_function(f_addr) def eval_expr(self, expr): """Eval expression @expr in the context of the current instance. Side effects are passed on it""" self.symbexec.update_engine_from_cpu() ret = self.symbexec.apply_expr(expr) self.symbexec.update_cpu_from_engine() return ret
class DSEEngine(object): """Dynamic Symbolic Execution Engine This class aims to be overridden for each specific purpose """ SYMB_ENGINE = ESETrackModif def __init__(self, machine): self.machine = machine self.loc_db = LocationDB() self.handler = {} # addr -> callback(DSEEngine instance) self.instrumentation = {} # addr -> callback(DSEEngine instance) self.addr_to_cacheblocks = {} # addr -> {label -> IRBlock} self.ir_arch = self.machine.ir(loc_db=self.loc_db) # corresponding IR self.ircfg = self.ir_arch.new_ircfg() # corresponding IR # Defined after attachment self.jitter = None # Jitload (concrete execution) self.symb = None # SymbolicExecutionEngine self.symb_concrete = None # Concrete SymbExec for path desambiguisation self.mdis = None # DisasmEngine def prepare(self): """Prepare the environment for attachment with a jitter""" # Disassembler self.mdis = self.machine.dis_engine(bin_stream_vm(self.jitter.vm), lines_wd=1, loc_db=self.loc_db) # Symbexec engine ## Prepare symbexec engines self.symb = self.SYMB_ENGINE(self.jitter.cpu, self.jitter.vm, self.ir_arch, {}) self.symb.enable_emulated_simplifications() self.symb_concrete = EmulatedSymbExec( self.jitter.cpu, self.jitter.vm, self.ir_arch, {} ) ### Avoid side effects on jitter while using 'symb_concrete' self.symb_concrete.func_write = None ## Update registers value self.symb.symbols[self.ir_arch.IRDst] = ExprInt( getattr(self.jitter.cpu, self.ir_arch.pc.name), self.ir_arch.IRDst.size ) # Avoid memory write self.symb.func_write = None # Activate callback on each instr self.jitter.jit.set_options(max_exec_per_call=1, jit_maxline=1) self.jitter.exec_cb = self.callback # Clean jit cache to avoid multi-line basic blocks already jitted self.jitter.jit.clear_jitted_blocks() def attach(self, emulator): """Attach the DSE to @emulator @emulator: jitload (or API equivalent) instance To attach *DURING A BREAKPOINT*, one may consider using the following snippet: def breakpoint(self, jitter): ... dse.attach(jitter) dse.update... ... # Additional call to the exec callback is necessary, as breakpoints are # honored AFTER exec callback jitter.exec_cb(jitter) return True Without it, one may encounteer a DriftException error due to a "desynchronization" between jitter and dse states. Indeed, on 'handle' call, the jitter must be one instruction AFTER the dse. """ self.jitter = emulator self.prepare() def handle(self, cur_addr): r"""Handle destination @cur_addr: Expr of the next address in concrete execution /!\ cur_addr may be a loc_key In this method, self.symb is in the "just before branching" state """ pass def add_handler(self, addr, callback): """Add a @callback for address @addr before any state update. The state IS NOT updated after returning from the callback @addr: int @callback: func(dse instance)""" self.handler[addr] = callback def add_lib_handler(self, libimp, namespace): """Add search for handler based on a @libimp libimp instance Known functions will be looked by {name}_symb in the @namespace """ # lambda cannot contain statement def default_func(dse): fname = "%s_symb" % libimp.fad2cname[dse.jitter.pc] raise RuntimeError("Symbolic stub '%s' not found" % fname) for addr, fname in libimp.fad2cname.iteritems(): fname = "%s_symb" % fname func = namespace.get(fname, None) if func is not None: self.add_handler(addr, func) else: self.add_handler(addr, default_func) def add_instrumentation(self, addr, callback): """Add a @callback for address @addr before any state update. The state IS updated after returning from the callback @addr: int @callback: func(dse instance)""" self.instrumentation[addr] = callback def _check_state(self): """Check the current state against the concrete one""" errors = [] # List of DriftInfo for symbol in self.symb.modified_expr: # Do not consider PC if symbol in [self.ir_arch.pc, self.ir_arch.IRDst]: continue # Consider only concrete values symb_value = self.eval_expr(symbol) if not symb_value.is_int(): continue symb_value = int(symb_value) # Check computed values against real ones if symbol.is_id(): if hasattr(self.jitter.cpu, symbol.name): value = getattr(self.jitter.cpu, symbol.name) if value != symb_value: errors.append(DriftInfo(symbol, symb_value, value)) elif symbol.is_mem() and symbol.ptr.is_int(): value_chr = self.jitter.vm.get_mem(int(symbol.ptr), symbol.size / 8) exp_value = int(value_chr[::-1].encode("hex"), 16) if exp_value != symb_value: errors.append(DriftInfo(symbol, symb_value, exp_value)) # Check for drift, and act accordingly if errors: raise DriftException(errors) def callback(self, _): """Called before each instruction""" # Assert synchronization with concrete execution self._check_state() # Call callbacks associated to the current address cur_addr = self.jitter.pc if isinstance(cur_addr, LocKey): lbl = self.ir_arch.loc_db.loc_key_to_label(cur_addr) cur_addr = lbl.offset if cur_addr in self.handler: self.handler[cur_addr](self) return True if cur_addr in self.instrumentation: self.instrumentation[cur_addr](self) # Handle current address self.handle(ExprInt(cur_addr, self.ir_arch.IRDst.size)) # Avoid memory issue in ExpressionSimplifier if len(self.symb.expr_simp.simplified_exprs) > 100000: self.symb.expr_simp.simplified_exprs.clear() # Get IR blocks if cur_addr in self.addr_to_cacheblocks: self.ircfg.blocks.clear() self.ircfg.blocks.update(self.addr_to_cacheblocks[cur_addr]) else: ## Reset cache structures self.ircfg.blocks.clear()# = {} ## Update current state asm_block = self.mdis.dis_block(cur_addr) self.ir_arch.add_asmblock_to_ircfg(asm_block, self.ircfg) self.addr_to_cacheblocks[cur_addr] = dict(self.ircfg.blocks) # Emulate the current instruction self.symb.reset_modified() # Is the symbolic execution going (potentially) to jump on a lbl_gen? if len(self.ircfg.blocks) == 1: self.symb.run_at(self.ircfg, cur_addr) else: # Emulation could stuck in generated IR blocks # But concrete execution callback is not enough precise to obtain # the full IR blocks path # -> Use a fully concrete execution to get back path # Update the concrete execution self._update_state_from_concrete_symb( self.symb_concrete, cpu=True, mem=True ) while True: next_addr_concrete = self.symb_concrete.run_block_at( self.ircfg, cur_addr ) self.symb.run_block_at(self.ircfg, cur_addr) if not (isinstance(next_addr_concrete, ExprLoc) and self.ir_arch.loc_db.get_location_offset( next_addr_concrete.loc_key ) is None): # Not a lbl_gen, exit break # Call handle with lbl_gen state self.handle(next_addr_concrete) cur_addr = next_addr_concrete # At this stage, symbolic engine is one instruction after the concrete # engine return True def _get_gpregs(self): """Return a dict of regs: value from the jitter This version use the regs associated to the attrib (!= cpu.get_gpreg()) """ out = {} regs = self.ir_arch.arch.regs.attrib_to_regs[self.ir_arch.attrib] for reg in regs: if hasattr(self.jitter.cpu, reg.name): out[reg.name] = getattr(self.jitter.cpu, reg.name) return out def take_snapshot(self): """Return a snapshot of the current state (including jitter state)""" snapshot = { "mem": self.jitter.vm.get_all_memory(), "regs": self._get_gpregs(), "symb": self.symb.symbols.copy(), } return snapshot def restore_snapshot(self, snapshot, memory=True): """Restore a @snapshot taken with .take_snapshot @snapshot: .take_snapshot output @memory: (optional) if set, also restore the memory """ # Restore memory if memory: self.jitter.vm.reset_memory_page_pool() self.jitter.vm.reset_code_bloc_pool() for addr, metadata in snapshot["mem"].iteritems(): self.jitter.vm.add_memory_page(addr, metadata["access"], metadata["data"]) # Restore registers self.jitter.pc = snapshot["regs"][self.ir_arch.pc.name] for reg, value in snapshot["regs"].iteritems(): setattr(self.jitter.cpu, reg, value) # Reset intern elements self.jitter.vm.set_exception(0) self.jitter.cpu.set_exception(0) self.jitter.bs._atomic_mode = False # Reset symb exec for key, _ in self.symb.symbols.items(): del self.symb.symbols[key] for expr, value in snapshot["symb"].items(): self.symb.symbols[expr] = value def update_state(self, assignblk): """From this point, assume @assignblk in the symbolic execution @assignblk: AssignBlock/{dst -> src} """ for dst, src in assignblk.iteritems(): self.symb.apply_change(dst, src) def _update_state_from_concrete_symb(self, symbexec, cpu=True, mem=False): if mem: # Values will be retrieved from the concrete execution if they are # not present symbexec.symbols.symbols_mem.base_to_memarray.clear() if cpu: regs = self.ir_arch.arch.regs.attrib_to_regs[self.ir_arch.attrib] for reg in regs: if hasattr(self.jitter.cpu, reg.name): value = ExprInt(getattr(self.jitter.cpu, reg.name), size=reg.size) symbexec.symbols[reg] = value def update_state_from_concrete(self, cpu=True, mem=False): r"""Update the symbolic state with concrete values from the concrete engine @cpu: (optional) if set, update registers' value @mem: (optional) if set, update memory value /!\ all current states will be loss. This function is usually called when states are no more synchronized (at the beginning, returning from an unstubbed syscall, ...) """ self._update_state_from_concrete_symb(self.symb, cpu, mem) def eval_expr(self, expr): """Return the evaluation of @expr: @expr: Expr instance""" return self.symb.eval_expr(expr) @staticmethod def memory_to_expr(addr): """Translate an address to its corresponding symbolic ID (8bits) @addr: int""" return ExprId("MEM_0x%x" % int(addr), 8) def symbolize_memory(self, memory_range): """Register a range of memory addresses to symbolize @memory_range: object with support of __in__ operation (intervals, list, ...) """ self.symb.dse_memory_range = memory_range self.symb.dse_memory_to_expr = self.memory_to_expr
class DSEEngine(object): """Dynamic Symbolic Execution Engine This class aims to be overrided for each specific purpose """ SYMB_ENGINE = ESETrackModif def __init__(self, machine): self.machine = machine self.handler = {} # addr -> callback(DSEEngine instance) self.instrumentation = {} # addr -> callback(DSEEngine instance) self.addr_to_cacheblocks = {} # addr -> {label -> IRBlock} self.ir_arch = self.machine.ir() # corresponding IR # Defined after attachment self.jitter = None # Jitload (concrete execution) self.symb = None # SymbolicExecutionEngine self.symb_concrete = None # Concrete SymbExec for path desambiguisation self.mdis = None # DisasmEngine def prepare(self): """Prepare the environment for attachment with a jitter""" # Disassembler self.mdis = self.machine.dis_engine(bin_stream_vm(self.jitter.vm), lines_wd=1) # Symbexec engine ## Prepare symbexec engines self.symb = self.SYMB_ENGINE(self.jitter.cpu, self.jitter.vm, self.ir_arch, {}) self.symb.enable_emulated_simplifications() self.symb_concrete = EmulatedSymbExec(self.jitter.cpu, self.jitter.vm, self.ir_arch, {}) ## Update registers value self.symb.symbols[self.ir_arch.IRDst] = ExprInt(getattr(self.jitter.cpu, self.ir_arch.pc.name), self.ir_arch.IRDst.size) # Avoid memory write self.symb.func_write = None # Activate callback on each instr self.jitter.jit.set_options(max_exec_per_call=1, jit_maxline=1) self.jitter.exec_cb = self.callback def attach(self, emulator): """Attach the DSE to @emulator @emulator: jitload (or API equivalent) instance""" self.jitter = emulator self.prepare() def handle(self, cur_addr): """Handle destination @cur_addr: Expr of the next address in concrete execution /!\ cur_addr may be a lbl_gen In this method, self.symb is in the "just before branching" state """ pass def add_handler(self, addr, callback): """Add a @callback for address @addr before any state update. The state IS NOT updated after returning from the callback @addr: int @callback: func(dse instance)""" self.handler[addr] = callback def add_lib_handler(self, libimp, namespace): """Add search for handler based on a @libimp libimp instance Known functions will be looked by {name}_symb in the @namespace """ # lambda cannot contain statement def default_func(dse): fname = "%s_symb" % libimp.fad2cname[dse.jitter.pc] raise RuntimeError("Symbolic stub '%s' not found" % fname) for addr, fname in libimp.fad2cname.iteritems(): fname = "%s_symb" % fname func = namespace.get(fname, None) if func is not None: self.add_handler(addr, func) else: self.add_handler(addr, default_func) def add_instrumentation(self, addr, callback): """Add a @callback for address @addr before any state update. The state IS updated after returning from the callback @addr: int @callback: func(dse instance)""" self.instrumentation[addr] = callback def _check_state(self): """Check the current state against the concrete one""" errors = [] # List of DriftInfo for symbol in self.symb.modified_expr: # Do not consider PC if symbol in [self.ir_arch.pc, self.ir_arch.IRDst]: continue # Consider only concrete values symb_value = self.eval_expr(symbol) if not symb_value.is_int(): continue symb_value = int(symb_value) # Check computed values against real ones if symbol.is_id(): if hasattr(self.jitter.cpu, symbol.name): value = getattr(self.jitter.cpu, symbol.name) if value != symb_value: errors.append(DriftInfo(symbol, symb_value, value)) elif symbol.is_mem() and symbol.arg.is_int(): value_chr = self.jitter.vm.get_mem(int(symbol.arg), symbol.size / 8) exp_value = int(value_chr[::-1].encode("hex"), 16) if exp_value != symb_value: errors.append(DriftInfo(symbol, symb_value, exp_value)) # Check for drift, and act accordingly if errors: raise DriftException(errors) def callback(self, _): """Called before each instruction""" # Assert synchronization with concrete execution self._check_state() # Call callbacks associated to the current address cur_addr = self.jitter.pc if cur_addr in self.handler: self.handler[cur_addr](self) return True if cur_addr in self.instrumentation: self.instrumentation[cur_addr](self) # Handle current address self.handle(ExprInt(cur_addr, self.ir_arch.IRDst.size)) # Avoid memory issue in ExpressionSimplifier if len(self.symb.expr_simp.simplified_exprs) > 100000: self.symb.expr_simp.simplified_exprs.clear() # Get IR blocks if cur_addr in self.addr_to_cacheblocks: self.ir_arch.blocks.clear() self.ir_arch.blocks.update(self.addr_to_cacheblocks[cur_addr]) else: ## Reset cache structures self.mdis.job_done.clear() self.ir_arch.blocks.clear()# = {} ## Update current state asm_block = self.mdis.dis_bloc(cur_addr) self.ir_arch.add_bloc(asm_block) self.addr_to_cacheblocks[cur_addr] = dict(self.ir_arch.blocks) # Emulate the current instruction self.symb.reset_modified() # Is the symbolic execution going (potentially) to jump on a lbl_gen? if len(self.ir_arch.blocks) == 1: next_addr = self.symb.emul_ir_blocks(cur_addr) else: # Emulation could stuck in generated IR blocks # But concrete execution callback is not enough precise to obtain # the full IR blocks path # -> Use a fully concrete execution to get back path # Update the concrete execution self._update_state_from_concrete_symb(self.symb_concrete) while True: next_addr_concrete = self.symb_concrete.emul_ir_block(cur_addr) self.symb.emul_ir_block(cur_addr) if not(expr_is_label(next_addr_concrete) and next_addr_concrete.name.offset is None): # Not a lbl_gen, exit break # Call handle with lbl_gen state self.handle(next_addr_concrete) cur_addr = next_addr_concrete # At this stage, symbolic engine is one instruction after the concrete # engine return True def take_snapshot(self): """Return a snapshot of the current state (including jitter state)""" snapshot = { "mem": self.jitter.vm.get_all_memory(), "regs": self.jitter.cpu.get_gpreg(), "symb": self.symb.symbols.copy() } return snapshot def restore_snapshot(self, snapshot, memory=True): """Restore a @snapshot taken with .take_snapshot @snapshot: .take_snapshot output @memory: (optional) if set, also restore the memory """ # Restore memory if memory: self.jitter.vm.reset_memory_page_pool() self.jitter.vm.reset_code_bloc_pool() for addr, metadata in snapshot["mem"].iteritems(): self.jitter.vm.add_memory_page(addr, metadata["access"], metadata["data"]) # Restore registers self.jitter.pc = snapshot["regs"][self.ir_arch.pc.name] self.jitter.cpu.set_gpreg(snapshot["regs"]) # Reset intern elements self.jitter.vm.set_exception(0) self.jitter.cpu.set_exception(0) self.jitter.bs._atomic_mode = False # Reset symb exec for key, _ in self.symb.symbols.items(): del self.symb.symbols[key] for expr, value in snapshot["symb"].items(): self.symb.symbols[expr] = value def update_state(self, assignblk): """From this point, assume @assignblk in the symbolic execution @assignblk: AssignBlock/{dst -> src} """ for dst, src in assignblk.iteritems(): self.symb.apply_change(dst, src) def _update_state_from_concrete_symb(self, symbexec, cpu=True, mem=False): if mem: # Values will be retrieved from the concrete execution if they are # not present for symbol in symbexec.symbols.symbols_mem.copy(): del symbexec.symbols[symbol] if cpu: regs = self.ir_arch.arch.regs.attrib_to_regs[self.ir_arch.attrib] for reg in regs: if hasattr(self.jitter.cpu, reg.name): value = ExprInt(getattr(self.jitter.cpu, reg.name), size=reg.size) symbexec.symbols[reg] = value def update_state_from_concrete(self, cpu=True, mem=False): """Update the symbolic state with concrete values from the concrete engine @cpu: (optional) if set, update registers' value @mem: (optional) if set, update memory value /!\ all current states will be loss. This function is usually called when states are no more synchronized (at the beginning, returning from an unstubbed syscall, ...) """ self._update_state_from_concrete_symb(self.symb, cpu, mem) def eval_expr(self, expr): """Return the evaluation of @expr: @expr: Expr instance""" return self.symb.eval_expr(expr) @staticmethod def memory_to_expr(addr): """Translate an address to its corresponding symbolic ID (8bits) @addr: int""" return ExprId("MEM_0x%x" % int(addr), 8) def symbolize_memory(self, memory_range): """Register a range of memory addresses to symbolize @memory_range: object with support of __in__ operation (intervals, list, ...) """ self.symb.dse_memory_range = memory_range self.symb.dse_memory_to_expr = self.memory_to_expr
def __init__(self, ir_arch, jit_type="tcc"): """Init an instance of jitter. @ir_arch: ir instance for this architecture @jit_type: JiT backend to use. Available options are: - "tcc" - "llvm" - "python" """ self.arch = ir_arch.arch self.attrib = ir_arch.attrib arch_name = ir_arch.arch.name # (ir_arch.arch.name, ir_arch.attrib) if arch_name == "x86": from miasm2.jitter.arch import JitCore_x86 as jcore elif arch_name == "arm": from miasm2.jitter.arch import JitCore_arm as jcore elif arch_name == "aarch64": from miasm2.jitter.arch import JitCore_aarch64 as jcore elif arch_name == "msp430": from miasm2.jitter.arch import JitCore_msp430 as jcore elif arch_name == "mips32": from miasm2.jitter.arch import JitCore_mips32 as jcore else: raise ValueError("unsupported jit arch!") self.vm = VmMngr.Vm() self.cpu = jcore.JitCpu() self.ir_arch = ir_arch self.bs = bin_stream_vm(self.vm) init_arch_C(self.arch) self.symbexec = EmulatedSymbExec(self.cpu, self.ir_arch, {}) self.symbexec.reset_regs() if jit_type == "tcc": self.jit = JitCore_Tcc(self.ir_arch, self.bs) elif jit_type == "llvm": self.jit = JitCore_LLVM(self.ir_arch, self.bs) elif jit_type == "python": self.jit = JitCore_Python(self.ir_arch, self.bs) else: raise Exception("Unkown JiT Backend") self.cpu.init_regs() self.vm.init_memory_page_pool() self.vm.init_code_bloc_pool() self.vm.init_memory_breakpoint() self.vm.set_addr2obj(self.jit.addr2obj) self.jit.load() self.cpu.vmmngr = self.vm self.cpu.jitter = self.jit self.stack_size = 0x10000 self.stack_base = 0x1230000 # Init callback handler self.breakpoints_handler = CallbackHandler() self.exceptions_handler = CallbackHandlerBitflag() self.init_exceptions_handler() self.exec_cb = None
def __init__(self, ir_arch, bs=None): super(JitCore_Python, self).__init__(ir_arch, bs) self.ir_arch = ir_arch # CPU (None for now) will be set by the "jitted" Python function self.symbexec = EmulatedSymbExec(None, self.ir_arch, {})
class JitCore_Python(jitcore.JitCore): "JiT management, using Miasm2 Symbol Execution engine as backend" def __init__(self, ir_arch, bs=None): super(JitCore_Python, self).__init__(ir_arch, bs) self.ir_arch = ir_arch # CPU (None for now) will be set by the "jitted" Python function self.symbexec = EmulatedSymbExec(None, self.ir_arch, {}) def load(self): "Preload symbols according to current architecture" self.symbexec.reset_regs() def jitirblocs(self, label, irblocs): """Create a python function corresponding to an irblocs' group. @label: the label of the irblocs @irblocs: a gorup of irblocs """ def myfunc(cpu, vmmngr): """Execute the function according to cpu and vmmngr states @cpu: JitCpu instance @vm: VmMngr instance """ # Keep current location in irblocs cur_label = label # Required to detect new instructions offsets_jitted = set() # Get exec engine exec_engine = self.symbexec exec_engine.cpu = cpu # For each irbloc inside irblocs while True: # Get the current bloc for irb in irblocs: if irb.label == cur_label: break else: raise RuntimeError("Irblocs must end with returning an " "ExprInt instance") # Refresh CPU values according to @cpu instance exec_engine.update_engine_from_cpu() # Execute current ir bloc for ir, line in zip(irb.irs, irb.lines): # For each new instruction (in assembly) if line.offset not in offsets_jitted: offsets_jitted.add(line.offset) # Log registers values if self.log_regs: exec_engine.update_cpu_from_engine() cpu.dump_gpregs() # Log instruction if self.log_mn: print "%08x %s" % (line.offset, line) # Check for memory exception if (vmmngr.get_exception() != 0): exec_engine.update_cpu_from_engine() return line.offset # Eval current instruction (in IR) exec_engine.eval_ir(ir) # Check for memory exception which do not update PC if (vmmngr.get_exception() & csts.EXCEPT_DO_NOT_UPDATE_PC != 0): exec_engine.update_cpu_from_engine() return line.offset # Get next bloc address ad = expr_simp(exec_engine.eval_expr(self.ir_arch.IRDst)) # Updates @cpu instance according to new CPU values exec_engine.update_cpu_from_engine() # Manage resulting address if isinstance(ad, m2_expr.ExprInt): return ad.arg.arg elif isinstance(ad, m2_expr.ExprId): cur_label = ad.name else: raise NotImplementedError("Type not handled: %s" % ad) # Associate myfunc with current label self.lbl2jitbloc[label.offset] = myfunc def jit_call(self, label, cpu, vmmngr, _breakpoints): """Call the function label with cpu and vmmngr states @label: function's label @cpu: JitCpu instance @vm: VmMngr instance """ # Get Python function corresponding to @label fc_ptr = self.lbl2jitbloc[label] # Execute the function return fc_ptr(cpu, vmmngr)
class JitCore_Python(jitcore.JitCore): "JiT management, using Miasm2 Symbol Execution engine as backend" def __init__(self, ir_arch, bs=None): super(JitCore_Python, self).__init__(ir_arch, bs) self.ir_arch = ir_arch # CPU & VM (None for now) will be set later expr_simp = ExpressionSimplifier() expr_simp.enable_passes(ExpressionSimplifier.PASS_COMMONS) self.symbexec = EmulatedSymbExec(None, None, self.ir_arch, {}, sb_expr_simp=expr_simp) self.symbexec.enable_emulated_simplifications() def set_cpu_vm(self, cpu, vm): self.symbexec.cpu = cpu self.symbexec.vm = vm def load(self): "Preload symbols according to current architecture" self.symbexec.reset_regs() def jitirblocs(self, label, irblocs): """Create a python function corresponding to an irblocs' group. @label: the label of the irblocs @irblocs: a gorup of irblocs """ def myfunc(cpu, vmmngr): """Execute the function according to cpu and vmmngr states @cpu: JitCpu instance @vm: VmMngr instance """ # Keep current location in irblocs cur_label = label # Required to detect new instructions offsets_jitted = set() # Get exec engine exec_engine = self.symbexec expr_simp = exec_engine.expr_simp # For each irbloc inside irblocs while True: # Get the current bloc for irb in irblocs: if irb.label == cur_label: break else: raise RuntimeError("Irblocs must end with returning an " "ExprInt instance") # Refresh CPU values according to @cpu instance exec_engine.update_engine_from_cpu() # Execute current ir bloc for ir, line in zip(irb.irs, irb.lines): # For each new instruction (in assembly) if line.offset not in offsets_jitted: # Test exceptions vmmngr.check_invalid_code_blocs() vmmngr.check_memory_breakpoint() if vmmngr.get_exception(): exec_engine.update_cpu_from_engine() return line.offset offsets_jitted.add(line.offset) # Log registers values if self.log_regs: exec_engine.update_cpu_from_engine() exec_engine.cpu.dump_gpregs() # Log instruction if self.log_mn: print "%08x %s" % (line.offset, line) # Check for exception if (vmmngr.get_exception() != 0 or cpu.get_exception() != 0): exec_engine.update_cpu_from_engine() return line.offset # Eval current instruction (in IR) exec_engine.eval_ir(ir) # Check for exceptions which do not update PC exec_engine.update_cpu_from_engine() if (vmmngr.get_exception() & csts.EXCEPT_DO_NOT_UPDATE_PC != 0 or cpu.get_exception() > csts.EXCEPT_NUM_UPDT_EIP): return line.offset vmmngr.check_invalid_code_blocs() vmmngr.check_memory_breakpoint() # Get next bloc address ad = expr_simp(exec_engine.eval_expr(self.ir_arch.IRDst)) # Updates @cpu instance according to new CPU values exec_engine.update_cpu_from_engine() # Manage resulting address if isinstance(ad, m2_expr.ExprInt): return ad.arg.arg elif isinstance(ad, m2_expr.ExprId): cur_label = ad.name else: raise NotImplementedError("Type not handled: %s" % ad) # Associate myfunc with current label self.lbl2jitbloc[label.offset] = myfunc def jit_call(self, label, cpu, vmmngr, _breakpoints): """Call the function label with cpu and vmmngr states @label: function's label @cpu: JitCpu instance @vm: VmMngr instance """ # Get Python function corresponding to @label fc_ptr = self.lbl2jitbloc[label] # Execute the function return fc_ptr(cpu, vmmngr)