about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorserpilliere <serpilliere@users.noreply.github.com>2016-02-16 16:36:43 +0100
committerserpilliere <serpilliere@users.noreply.github.com>2016-02-16 16:36:43 +0100
commit91e9abd906c0a9f5b43bad5b9789ffa1b054f6fe (patch)
tree30f32695bc995a4cd56f902313aad3a1d4a411f4
parent2cf69707481ba4b0dd163b49d99bc9a021162944 (diff)
parent052c02757c8c7aecabb9d86c30dfd672e46ccfa4 (diff)
downloadmiasm-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.py14
-rw-r--r--miasm2/jitter/emulatedsymbexec.py83
-rw-r--r--miasm2/jitter/jitcore_python.py106
-rw-r--r--miasm2/jitter/jitload.py16
-rw-r--r--test/ir/symbexec.py6
-rw-r--r--test/jitter/jitload.py49
-rw-r--r--test/test_all.py6
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: