diff options
| author | Theofilos Augoustis <theofilos.augoustis@gmail.com> | 2025-10-23 11:06:17 +0000 |
|---|---|---|
| committer | Theofilos Augoustis <theofilos.augoustis@gmail.com> | 2025-11-06 17:20:13 +0000 |
| commit | 10aecdf0a8ed8823a44a0fe0fa271e0458241c58 (patch) | |
| tree | c1bede0fd373da4c6a578e5e1ec16a93b0c96377 | |
| parent | 18e084b4cfc1ee5c963cae351b84668325bb43c7 (diff) | |
| download | focaccia-10aecdf0a8ed8823a44a0fe0fa271e0458241c58.tar.gz focaccia-10aecdf0a8ed8823a44a0fe0fa271e0458241c58.zip | |
Enable forced mode to push through errors (in hopes of tracing even partially-unsupported programs)
| -rw-r--r-- | src/focaccia/symbolic.py | 49 | ||||
| -rwxr-xr-x | src/focaccia/tools/capture_transforms.py | 7 |
2 files changed, 36 insertions, 20 deletions
diff --git a/src/focaccia/symbolic.py b/src/focaccia/symbolic.py index f47bbb3..3ace906 100644 --- a/src/focaccia/symbolic.py +++ b/src/focaccia/symbolic.py @@ -64,7 +64,8 @@ def eval_symbol(symbol: Expr, conc_state: ReadableProgramState) -> int: # but ExprLocs are disallowed by the # ConcreteStateWrapper if not isinstance(res, ExprInt): - raise Exception(f'{res} from symbol {symbol} is not an instance of ExprInt but only ExprInt can be evaluated') + raise Exception(f'{res} from symbol {symbol} is not an instance of ExprInt' + f' but only ExprInt can be evaluated') return int(res) class Instruction: @@ -382,9 +383,9 @@ class SymbolicTransform: try: return Instruction.from_string(text, arch, offset=0, length=length) except Exception as err: - warn(f'[In SymbolicTransform.from_json] Unable to parse' - f' instruction string "{text}": {err}.') - return None + # Note: from None disables chaining in traceback + raise ValueError(f'[In SymbolicTransform.from_json] Unable to parse' + f' instruction string "{text}": {err}.') from None arch = supported_architectures[data['arch']] start_addr = int(data['from_addr']) @@ -410,10 +411,10 @@ class SymbolicTransform: try: return [inst.length, inst.to_string()] except Exception as err: - warn(f'[In SymbolicTransform.to_json] Unable to serialize' - f' "{inst}" as string: {err}. This instruction will not' - f' be serialized.') - return None + # Note: from None disables chaining in traceback + raise Exception(f'[In SymbolicTransform.to_json] Unable to serialize' + f' "{inst}" as string: {err}. This instruction will not' + f' be serialized.') from None instrs = [encode_inst(inst) for inst in self.instructions] instrs = [inst for inst in instrs if inst is not None] @@ -584,8 +585,7 @@ def run_instruction(instr: miasm_instr, loc = lifter.add_instr_to_ircfg(instr, ircfg, None, False) assert(isinstance(loc, Expr) or isinstance(loc, LocKey)) except NotImplementedError as err: - warn(f'[WARNING] Unable to lift instruction {instr}: {err}. Skipping.') - return None, {} # Create an empty transform for the instruction + raise Exception(f'Unable to lift instruction {instr}: {err}. Skipping.') from None # Execute instruction symbolically new_pc, modified = execute_location(loc) @@ -690,8 +690,7 @@ class SymbolicTracer: raise Exception() return True - def trace(self, - start_addr: int | None = None) -> Trace[SymbolicTransform]: + def trace(self, start_addr: int | None = None) -> Trace[SymbolicTransform]: """Execute a program and compute state transformations between executed instructions. @@ -718,7 +717,7 @@ class SymbolicTracer: except: err = sys.exc_info()[1] - # Try to get the LLDB disassembly instead to simplify debugging + # Try to recovery by using the LLDB disassembly instead try: alt_disas = target.get_disassembly(pc) instr = Instruction.from_string(alt_disas, ctx.arch, pc, @@ -726,14 +725,22 @@ class SymbolicTracer: info(f'Disassembled instruction {instr} at {hex(pc)}') instr = instr.instr except: - warn(f'Unable to disassemble instruction {hex(pc)}: {err}.' - f' Skipping.') - target.step() - continue + if self.force: + warn(f'Unable to disassemble instruction {hex(pc)}: {err}.' + f' Skipping.') + target.step() + continue + raise # forward exception # Run instruction conc_state = MiasmSymbolResolver(lldb_state, ctx.loc_db) - new_pc, modified = run_instruction(instr, conc_state, ctx.lifter) + + try: + new_pc, modified = run_instruction(instr, conc_state, ctx.lifter) + except: + if not self.force: + raise + new_pc, modified = None, {} # Create symbolic transform instruction = Instruction(instr, ctx.machine, ctx.arch, ctx.loc_db) @@ -745,7 +752,11 @@ class SymbolicTracer: strace.append(transform) if len(strace) == 0: - raise Exception(f'Unable to collect trace for instruction {instr}') + msg = f'Unable to collect trace for instruction {instr}' + if not self.force: + raise Exception(msg) + else: + warn(msg) # Predict next concrete state. # We verify the symbolic execution backend on the fly for some diff --git a/src/focaccia/tools/capture_transforms.py b/src/focaccia/tools/capture_transforms.py index fa8c92a..0adba43 100755 --- a/src/focaccia/tools/capture_transforms.py +++ b/src/focaccia/tools/capture_transforms.py @@ -26,6 +26,10 @@ def main(): help='Remote target to trace (e.g. 127.0.0.1:12345)') prog.add_argument('--log-level', help='Set the logging level') + prog.add_argument('--force', + default=False, + action='store_true', + help='Force Focaccia to continue tracing even when something goes wrong') prog.add_argument('--debug', default=False, action='store_true', @@ -43,7 +47,8 @@ def main(): logging.basicConfig(level=logging.INFO) env = TraceEnvironment(args.binary, args.args, utils.get_envp()) - tracer = SymbolicTracer(env, remote=args.remote, cross_validate=args.cross_validate) + tracer = SymbolicTracer(env, remote=args.remote, cross_validate=args.cross_validate, + force=args.force) trace = tracer.trace() with open(args.output, 'w') as file: parser.serialize_transformations(trace, file) |