about summary refs log tree commit diff stats
path: root/trace_symbols.py
diff options
context:
space:
mode:
authorTheofilos Augoustis <theofilos.augoustis@gmail.com>2023-11-07 17:11:49 +0100
committerTheofilos Augoustis <theofilos.augoustis@gmail.com>2023-11-07 17:11:49 +0100
commitca7044cdc7fe99d8065594d455b7f41505f796ab (patch)
tree4d0aaa4ccf98ccaf6d213d7487b2464561d3caa9 /trace_symbols.py
parent6f01367f9c8ad4c3d641cc63dbb1a3977ff4ec56 (diff)
downloadfocaccia-ca7044cdc7fe99d8065594d455b7f41505f796ab.tar.gz
focaccia-ca7044cdc7fe99d8065594d455b7f41505f796ab.zip
Implement symbolic tracing in trace_symbols.py using Angr
Diffstat (limited to '')
-rw-r--r--trace_symbols.py110
1 files changed, 110 insertions, 0 deletions
diff --git a/trace_symbols.py b/trace_symbols.py
new file mode 100644
index 0000000..c16cd6e
--- /dev/null
+++ b/trace_symbols.py
@@ -0,0 +1,110 @@
+import angr
+import argparse
+import claripy as cp
+import sys
+
+from angr.exploration_techniques import Symbion
+from arch import x86
+from gen_trace import record_trace
+from lldb_target import LLDBConcreteTarget
+
+def print_state(state: angr.SimState, file=sys.stdout):
+    """Print a program state in a fancy way."""
+    print('-' * 80, file=file)
+    print(f'State at {hex(state.addr)}:', file=file)
+    print('-' * 80, file=file)
+    for reg in x86.regnames:
+        try:
+            val = state.regs.__getattr__(reg.lower())
+            print(f'{reg} = {val}', file=file)
+        except angr.SimConcreteRegisterError: pass
+        except angr.SimConcreteMemoryError: pass
+        except AttributeError: pass
+        except KeyError: pass
+
+    # Print some of the stack
+    print('\nStack:', file=file)
+    try:
+        rbp = state.regs.rbp
+        stack_size = 0xc
+        stack_mem = state.memory.load(rbp - stack_size, stack_size)
+        print(stack_mem, file=file)
+        stack = state.solver.eval(stack_mem, cast_to=bytes)
+        print(' '.join(f'{b:02x}' for b in stack[::-1]), file=file)
+    except angr.SimConcreteMemoryError:
+        print('<unable to read memory>', file=file)
+    print('-' * 80, file=file)
+
+def symbolize_state(state: angr.SimState,
+                    exclude: list[str] = ['PC', 'RBP', 'RSP']) \
+        -> angr.SimState:
+    """Create a copy of a SimState and replace most of it with symbolic
+    values.
+
+    Leaves pc, rbp, and rsp concrete by default. This can be configured with
+    the `exclude` parameter.
+
+    :return: A symbolized SymState object.
+    """
+    state = state.copy()
+
+    stack_size = 0xc
+    symb_stack = cp.BVS('stack', stack_size * 8)
+    state.memory.store(state.regs.rbp - stack_size, symb_stack)
+
+    _exclude = set(exclude)
+    for reg in x86.regnames:
+        if reg not in _exclude:
+            symb_val = cp.BVS(reg, 64)
+            try:
+                state.regs.__setattr__(reg.lower(), symb_val)
+            except AttributeError:
+                pass
+    return state
+
+def parse_args():
+    prog = argparse.ArgumentParser()
+    prog.add_argument('binary', type=str)
+    return prog.parse_args()
+
+def main():
+    args = parse_args()
+    binary = args.binary
+
+    conc_log = open('concrete.log', 'w')
+    symb_log = open('symbolic.log', 'w')
+
+    # Generate a program trace from a real execution
+    trace = record_trace(binary)
+    print(f'Found {len(trace)} trace points.')
+
+    target = LLDBConcreteTarget(binary)
+    proj = angr.Project(binary,
+                        concrete_target=target,
+                        use_sim_procedures=False)
+
+    entry_state = proj.factory.entry_state()
+    entry_state.options.add(angr.options.SYMBION_KEEP_STUBS_ON_SYNC)
+    entry_state.options.add(angr.options.SYMBION_SYNC_CLE)
+
+    for cur_inst, next_inst in zip(trace[0:-1], trace[1:]):
+        symbion = proj.factory.simgr(entry_state)
+        symbion.use_technique(Symbion(find=[cur_inst]))
+
+        conc_exploration = symbion.run()
+        conc_state = conc_exploration.found[0]
+
+        # Start symbolic execution with the concrete ('truth') state and try
+        # to reach the next instruction in the trace
+        simgr = proj.factory.simgr(symbolize_state(conc_state))
+        symb_exploration = simgr.explore(find=next_inst)
+        if len(symb_exploration.found) == 0:
+            print(f'Symbolic execution can\'t reach address {hex(next_inst)}'
+                  f' from {hex(cur_inst)}. Exiting.')
+            exit(1)
+
+        print_state(conc_state, file=conc_log)
+        print_state(symb_exploration.found[0], file=symb_log)
+
+if __name__ == "__main__":
+    main()