diff options
| author | serpilliere <serpilliere@users.noreply.github.com> | 2016-02-16 16:36:43 +0100 |
|---|---|---|
| committer | serpilliere <serpilliere@users.noreply.github.com> | 2016-02-16 16:36:43 +0100 |
| commit | 91e9abd906c0a9f5b43bad5b9789ffa1b054f6fe (patch) | |
| tree | 30f32695bc995a4cd56f902313aad3a1d4a411f4 | |
| parent | 2cf69707481ba4b0dd163b49d99bc9a021162944 (diff) | |
| parent | 052c02757c8c7aecabb9d86c30dfd672e46ccfa4 (diff) | |
| download | miasm-91e9abd906c0a9f5b43bad5b9789ffa1b054f6fe.tar.gz miasm-91e9abd906c0a9f5b43bad5b9789ffa1b054f6fe.zip | |
Merge pull request #325 from commial/jitter-symbexec
Eval_Expr in jitter context
| -rw-r--r-- | miasm2/ir/symbexec.py | 14 | ||||
| -rw-r--r-- | miasm2/jitter/emulatedsymbexec.py | 83 | ||||
| -rw-r--r-- | miasm2/jitter/jitcore_python.py | 106 | ||||
| -rw-r--r-- | miasm2/jitter/jitload.py | 16 | ||||
| -rw-r--r-- | test/ir/symbexec.py | 6 | ||||
| -rw-r--r-- | test/jitter/jitload.py | 49 | ||||
| -rw-r--r-- | test/test_all.py | 6 |
7 files changed, 185 insertions, 95 deletions
diff --git a/miasm2/ir/symbexec.py b/miasm2/ir/symbexec.py index 9ac79b1f..ba19ccf7 100644 --- a/miasm2/ir/symbexec.py +++ b/miasm2/ir/symbexec.py @@ -336,7 +336,6 @@ class symbexec(object): val = self.symbols[a][ptr_diff * 8 + b.size:a.size] out.append((m2_expr.ExprMem(ex, val.size), val)) return out - # give mem stored overlapping requested mem ptr def get_mem_overlapping(self, e, eval_cache=None): if eval_cache is None: @@ -452,3 +451,16 @@ class symbexec(object): if m.arg == 1: del self.symbols[mem] + def apply_expr(self, expr): + """Evaluate @expr and apply side effect if needed (ie. if expr is an + assignment). Return the evaluated value""" + + # Eval expression + to_eval = expr.src if isinstance(expr, m2_expr.ExprAff) else expr + ret = self.expr_simp(self.eval_expr(to_eval)) + + # Update value if needed + if isinstance(expr, m2_expr.ExprAff): + self.eval_ir([m2_expr.ExprAff(expr.dst, ret)]) + + return ret diff --git a/miasm2/jitter/emulatedsymbexec.py b/miasm2/jitter/emulatedsymbexec.py new file mode 100644 index 00000000..a5e4d340 --- /dev/null +++ b/miasm2/jitter/emulatedsymbexec.py @@ -0,0 +1,83 @@ +import miasm2.expression.expression as m2_expr +from miasm2.ir.symbexec import symbexec + + +class EmulatedSymbExec(symbexec): + """Symbolic exec instance linked with a jitter""" + + def __init__(self, cpu, *args, **kwargs): + """Instanciate an EmulatedSymbExec, associated to CPU @cpu and bind + memory accesses. + @cpu: JitCpu instance + """ + super(EmulatedSymbExec, self).__init__(*args, **kwargs) + self.cpu = cpu + self.func_read = self._func_read + self.func_write = self._func_write + + def reset_regs(self): + """Set registers value to 0. Ignore register aliases""" + for reg in self.ir_arch.arch.regs.all_regs_ids_no_alias: + self.symbols.symbols_id[reg] = m2_expr.ExprInt(0, size=reg.size) + + # Memory management + def _func_read(self, expr_mem): + """Memory read wrapper for symbolic execution + @expr_mem: ExprMem""" + + addr = expr_mem.arg.arg.arg + size = expr_mem.size / 8 + value = self.cpu.get_mem(addr, size) + + return m2_expr.ExprInt(int(value[::-1].encode("hex"), 16), + expr_mem.size) + + def _func_write(self, symb_exec, dest, data): + """Memory read wrapper for symbolic execution + @symb_exec: symbexec instance + @dest: ExprMem instance + @data: Expr instance""" + + # Get the content to write + data = self.expr_simp(data) + if not isinstance(data, m2_expr.ExprInt): + raise RuntimeError("A simplification is missing: %s" % data) + to_write = data.arg.arg + + # Format information + addr = dest.arg.arg.arg + size = data.size / 8 + content = hex(to_write).replace("0x", "").replace("L", "") + content = "0" * (size * 2 - len(content)) + content + content = content.decode("hex")[::-1] + + # Write in VmMngr context + self.cpu.set_mem(addr, content) + + # Interaction symbexec <-> jitter + def update_cpu_from_engine(self): + """Updates @cpu instance according to new CPU values""" + + for symbol in self.symbols: + if isinstance(symbol, m2_expr.ExprId): + if hasattr(self.cpu, symbol.name): + value = self.symbols.symbols_id[symbol] + if not isinstance(value, m2_expr.ExprInt): + raise ValueError("A simplification is missing: %s" % value) + + setattr(self.cpu, symbol.name, value.arg.arg) + else: + raise NotImplementedError("Type not handled: %s" % symbol) + + + def update_engine_from_cpu(self): + """Updates CPU values according to @cpu instance""" + + for symbol in self.symbols: + if isinstance(symbol, m2_expr.ExprId): + if hasattr(self.cpu, symbol.name): + value = m2_expr.ExprInt(getattr(self.cpu, symbol.name), + symbol.size) + self.symbols.symbols_id[symbol] = value + else: + raise NotImplementedError("Type not handled: %s" % symbol) diff --git a/miasm2/jitter/jitcore_python.py b/miasm2/jitter/jitcore_python.py index e29c81c0..eced9cd2 100644 --- a/miasm2/jitter/jitcore_python.py +++ b/miasm2/jitter/jitcore_python.py @@ -2,43 +2,7 @@ import miasm2.jitter.jitcore as jitcore import miasm2.expression.expression as m2_expr import miasm2.jitter.csts as csts from miasm2.expression.simplifications import expr_simp -from miasm2.ir.symbexec import symbexec - - -################################################################################ -# Util methods for Python jitter # -################################################################################ - -def update_cpu_from_engine(cpu, exec_engine): - """Updates @cpu instance according to new CPU values - @cpu: JitCpu instance - @exec_engine: symbexec instance""" - - for symbol in exec_engine.symbols: - if isinstance(symbol, m2_expr.ExprId): - if hasattr(cpu, symbol.name): - value = exec_engine.symbols.symbols_id[symbol] - if not isinstance(value, m2_expr.ExprInt): - raise ValueError("A simplification is missing: %s" % value) - - setattr(cpu, symbol.name, value.arg.arg) - else: - raise NotImplementedError("Type not handled: %s" % symbol) - - -def update_engine_from_cpu(cpu, exec_engine): - """Updates CPU values according to @cpu instance - @cpu: JitCpu instance - @exec_engine: symbexec instance""" - - for symbol in exec_engine.symbols: - if isinstance(symbol, m2_expr.ExprId): - if hasattr(cpu, symbol.name): - value = m2_expr.ExprInt(getattr(cpu, symbol.name), - symbol.size) - exec_engine.symbols.symbols_id[symbol] = value - else: - raise NotImplementedError("Type not handled: %s" % symbol) +from miasm2.jitter.emulatedsymbexec import EmulatedSymbExec ################################################################################ @@ -51,50 +15,14 @@ class JitCore_Python(jitcore.JitCore): def __init__(self, ir_arch, bs=None): super(JitCore_Python, self).__init__(ir_arch, bs) - self.symbexec = None 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" - - symbols_init = {r:m2_expr.ExprInt(0, size=r.size) - for r in self.ir_arch.arch.regs.all_regs_ids_no_alias} - self.symbexec = symbexec(self.ir_arch, symbols_init, - func_read = self.func_read, - func_write = self.func_write) - - def func_read(self, expr_mem): - """Memory read wrapper for symbolic execution - @expr_mem: ExprMem""" - - addr = expr_mem.arg.arg.arg - size = expr_mem.size / 8 - value = self.cpu.get_mem(addr, size) - - return m2_expr.ExprInt(int(value[::-1].encode("hex"), 16), - expr_mem.size) - - def func_write(self, symb_exec, dest, data): - """Memory read wrapper for symbolic execution - @symb_exec: symbexec instance - @dest: ExprMem instance - @data: Expr instance""" - - # Get the content to write - data = expr_simp(data) - if not isinstance(data, m2_expr.ExprInt): - raise NotImplementedError("A simplification is missing: %s" % data) - to_write = data.arg.arg - - # Format information - addr = dest.arg.arg.arg - size = data.size / 8 - content = hex(to_write).replace("0x", "").replace("L", "") - content = "0" * (size * 2 - len(content)) + content - content = content.decode("hex")[::-1] - - # Write in VmMngr context - self.cpu.set_mem(addr, content) + self.symbexec.reset_regs() def jitirblocs(self, label, irblocs): """Create a python function corresponding to an irblocs' group. @@ -110,29 +38,27 @@ class JitCore_Python(jitcore.JitCore): # Keep current location in irblocs cur_label = label - loop = True # 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 loop is True: + while True: # Get the current bloc - loop = False for irb in irblocs: if irb.label == cur_label: - loop = True break - - # Irblocs must end with returning an ExprInt instance - assert(loop is not False) + else: + raise RuntimeError("Irblocs must end with returning an " + "ExprInt instance") # Refresh CPU values according to @cpu instance - update_engine_from_cpu(cpu, exec_engine) + exec_engine.update_engine_from_cpu() # Execute current ir bloc for ir, line in zip(irb.irs, irb.lines): @@ -143,7 +69,7 @@ class JitCore_Python(jitcore.JitCore): # Log registers values if self.log_regs: - update_cpu_from_engine(cpu, exec_engine) + exec_engine.update_cpu_from_engine() cpu.dump_gpregs() # Log instruction @@ -152,7 +78,7 @@ class JitCore_Python(jitcore.JitCore): # Check for memory exception if (vmmngr.get_exception() != 0): - update_cpu_from_engine(cpu, exec_engine) + exec_engine.update_cpu_from_engine() return line.offset # Eval current instruction (in IR) @@ -160,14 +86,14 @@ class JitCore_Python(jitcore.JitCore): # Check for memory exception which do not update PC if (vmmngr.get_exception() & csts.EXCEPT_DO_NOT_UPDATE_PC != 0): - update_cpu_from_engine(cpu, exec_engine) + 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 - update_cpu_from_engine(cpu, exec_engine) + exec_engine.update_cpu_from_engine() # Manage resulting address if isinstance(ad, m2_expr.ExprInt): @@ -190,7 +116,5 @@ class JitCore_Python(jitcore.JitCore): # Get Python function corresponding to @label fc_ptr = self.lbl2jitbloc[label] - self.cpu = cpu - # Execute the function return fc_ptr(cpu, vmmngr) diff --git a/miasm2/jitter/jitload.py b/miasm2/jitter/jitload.py index 9a4b4ff4..aeb917d8 100644 --- a/miasm2/jitter/jitload.py +++ b/miasm2/jitter/jitload.py @@ -9,6 +9,7 @@ from miasm2.core.utils import * from miasm2.core.bin_stream import bin_stream_vm from miasm2.ir.ir2C import init_arch_C from miasm2.core.interval import interval +from miasm2.jitter.emulatedsymbexec import EmulatedSymbExec hnd = logging.StreamHandler() hnd.setFormatter(logging.Formatter("[%(levelname)s]: %(message)s")) @@ -201,11 +202,13 @@ class jitter: self.vm = VmMngr.Vm() self.cpu = jcore.JitCpu() - - self.bs = bin_stream_vm(self.vm) 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": @@ -443,3 +446,12 @@ class jitter: 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 diff --git a/test/ir/symbexec.py b/test/ir/symbexec.py index 1d87b470..9165fccb 100644 --- a/test/ir/symbexec.py +++ b/test/ir/symbexec.py @@ -7,7 +7,8 @@ import unittest class TestSymbExec(unittest.TestCase): def test_ClassDef(self): - from miasm2.expression.expression import ExprInt32, ExprId, ExprMem, ExprCompose + from miasm2.expression.expression import ExprInt32, ExprId, ExprMem, \ + ExprCompose, ExprAff from miasm2.arch.x86.sem import ir_x86_32 from miasm2.ir.symbexec import symbexec @@ -52,6 +53,9 @@ class TestSymbExec(unittest.TestCase): self.assertEqual(set(e.modified()), set(e.symbols)) self.assertRaises( KeyError, e.symbols.__getitem__, ExprMem(ExprInt32(100))) + self.assertEqual(e.apply_expr(id_eax), addr0) + self.assertEqual(e.apply_expr(ExprAff(id_eax, addr9)), addr9) + self.assertEqual(e.apply_expr(id_eax), addr9) if __name__ == '__main__': testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSymbExec) diff --git a/test/jitter/jitload.py b/test/jitter/jitload.py new file mode 100644 index 00000000..283298db --- /dev/null +++ b/test/jitter/jitload.py @@ -0,0 +1,49 @@ +from pdb import pm + +from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE +from miasm2.analysis.machine import Machine +from miasm2.expression.expression import ExprId, ExprInt32, ExprInt64, ExprAff, \ + ExprMem + +# Initial data: from 'example/samples/x86_32_sc.bin' +data = "8d49048d5b0180f90174058d5bffeb038d5b0189d8c3".decode("hex") + +# Init jitter +myjit = Machine("x86_32").jitter() +myjit.init_stack() + +run_addr = 0x40000000 +myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data) + +# Sentinelle called on terminate +def code_sentinelle(jitter): + jitter.run = False + jitter.pc = 0 + return True + +myjit.push_uint32_t(0x1337beef) +myjit.add_breakpoint(0x1337beef, code_sentinelle) + +# Run +myjit.init_run(run_addr) +myjit.continue_run() + +# Check end +assert myjit.run is False + +# Check resulting state / accessors +assert myjit.cpu.EAX == 0 +assert myjit.cpu.ECX == 4 + +# Check eval_expr +eax = ExprId("RAX", 64)[:32] +imm0, imm4, imm4_64 = ExprInt32(0), ExprInt32(4), ExprInt64(4) +memdata = ExprMem(ExprInt32(run_addr), len(data) * 8) +assert myjit.eval_expr(eax) == imm0 +## Due to ExprAff construction, imm4 is "promoted" to imm4_64 +assert myjit.eval_expr(ExprAff(eax, imm4)) == imm4_64 +assert myjit.eval_expr(eax) == imm4 +## Changes must be passed on myjit.cpu instance +assert myjit.cpu.EAX == 4 +## Memory +assert myjit.eval_expr(memdata).arg.arg == int(data[::-1].encode("hex"), 16) diff --git a/test/test_all.py b/test/test_all.py index 9d7c1256..adee5f2d 100644 --- a/test/test_all.py +++ b/test/test_all.py @@ -242,6 +242,12 @@ testset += RegressionTest(["depgraph.py"], base_dir="analysis", (14, 1), (15, 1))) for fname in fnames]) +## Jitter +for script in ["jitload.py", + ]: + testset += RegressionTest([script], base_dir="jitter") + + # Examples class Example(Test): """Examples specificities: |