# Buggy code
def _hook_register_write(self, state):
    reg_write_offset = state.inspect.reg_write_offset

    if reg_write_offset == state.arch.sp_offset:
        # it's updating stack pointer. skip
        return

    reg_write_expr = state.inspect.reg_write_expr
    reg_write_length = len(reg_write_expr) // 8

    # annotate it
    # reg_write_expr = reg_write_expr.annotate(VariableSourceAnnotation.from_state(state))

    state.inspect.reg_write_expr = reg_write_expr

    existing_vars = self.variable_manager[self.func_addr].find_variables_by_stmt(
        state.scratch.bbl_addr, state.scratch.stmt_idx, "register"
    )
    if not existing_vars:
        # create the variable
        variable = SimRegisterVariable(
            reg_write_offset,
            reg_write_length,
            ident=self.variable_manager[self.func_addr].next_variable_ident("register"),
            region=self.func_addr,
        )
        var_offset = self._normalize_register_offset(reg_write_offset)
        self.register_region.set_variable(var_offset, variable)
        # record this variable in variable manager
        self.variable_manager[self.func_addr].set_variable(
            "register", var_offset, variable
        )
        self.variable_manager[self.func_addr].write_to(
            variable, 0, self._codeloc_from_state(state)
        )

    # is it writing a pointer to a stack variable into the register?
    # e.g. lea eax, [ebp-0x40]
    stack_offset = self._addr_to_stack_offset(reg_write_expr)
    if stack_offset is not None:
        # it is!
        # unfortunately we don't know the size. We use size None for now.

        if stack_offset not in self.stack_region:
            lea_size = 1
            new_var = SimStackVariable(
                stack_offset,
                lea_size,
                base="bp",
                ident=self.variable_manager[self.func_addr].next_variable_ident(
                    "stack"
                ),
                region=self.func_addr,
            )
            self.stack_region.add_variable(stack_offset, new_var)

            # record this variable in variable manager
            self.variable_manager[self.func_addr].add_variable(
                "stack", stack_offset, new_var
            )

        base_offset = self.stack_region.get_base_addr(stack_offset)
        assert base_offset is not None
        for var in self.stack_region.get_variables_by_offset(stack_offset):
            self.variable_manager[self.func_addr].reference_at(
                var, stack_offset - base_offset, self._codeloc_from_state(state)
            )


# Traceback (ClaripyOperationError)
In [8]: proj.analyses.VariableRecovery(x)
WARNING | 2019-02-03 17:53:52,086 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_33]|Reg 68, 4B>, <0x4022c8[ir_35]|Reg 68, 4B>}.
WARNING | 2019-02-03 17:53:52,088 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_36]|Reg 40, 4B>, <0x4022c8[ir_28]|Reg 40, 4B>}.
WARNING | 2019-02-03 17:53:52,089 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_37]|Reg 44, 4B>, <0x4022c8[ir_29]|Reg 44, 4B>}.
WARNING | 2019-02-03 17:53:52,089 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_30]|Reg 48, 4B>, <0x4022c8[ir_38]|Reg 48, 4B>}.
WARNING | 2019-02-03 17:53:52,090 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_39]|Reg 52, 4B>, <0x4022c8[ir_31]|Reg 52, 4B>}.
WARNING | 2019-02-03 17:53:52,094 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_15]|Reg 32, 4B>, <0x4022c8[ir_40]|Reg 32, 4B>}.
WARNING | 2019-02-03 17:53:52,098 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_41]|Reg 8, 4B>, <0x4022c8[ir_32]|Reg 8, 4B>}.
WARNING | 2019-02-03 17:53:52,102 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_42]|Reg 152, 4B>, <0x4022c8[ir_34]|Reg 152, 4B>}.
---------------------------------------------------------------------------
ClaripyOperationError                     Traceback (most recent call last)
<ipython-input-8-de8d6084dd49> in <module>
----> 1 proj.analyses.VariableRecovery(x)

~/angr-dev/angr/angr/analyses/analysis.py in __call__(self, *args, **kwargs)
107
108         oself._show_progressbar = show_progressbar
--> 109         oself.__init__(*args, **kwargs)
110         return oself
111

~/angr-dev/angr/angr/analyses/variable_recovery/variable_recovery.py in __init__(self, func, max_iterations)
402         self._node_iterations = defaultdict(int)
403
--> 404         self._analyze()
405
406     #

~/angr-dev/angr/angr/analyses/forward_analysis.py in _analyze(self)
555             # We have a base graph to follow. Just handle the current job.
556
--> 557             self._analysis_core_graph()
558
559         self._post_analysis()

~/angr-dev/angr/angr/analyses/forward_analysis.py in _analysis_core_graph(self)
578                 break
579
--> 580             changed, output_state = self._run_on_node(n, job_state)
581
582             # output state of node n is input state for successors to node n

~/angr-dev/angr/angr/analyses/variable_recovery/variable_recovery.py in _run_on_node(self, node, state)
467                                                      addr=node.addr,
468                                                      size=node.size,
--> 469                                                      opt_level=0  # disable the optimization in order to have
470                                                                   # instruction-level analysis results
471                                                      )

~/angr-dev/angr/angr/factory.py in successors(self, *args, **kwargs)
47         """
48
---> 49         return self.project.engines.successors(*args, **kwargs)
50
51     def blank_state(self, **kwargs):

~/angr-dev/angr/angr/engines/hub.py in successors(self, state, addr, jumpkind, default_engine, procedure_engine, engines, **kwargs)
126         for engine in engines:
127             if engine.check(state, **kwargs):
--> 128                 r = engine.process(state, **kwargs)
129                 if r.processed:
130                     return r

~/angr-dev/angr/angr/engines/vex/engine.py in process(self, state, irsb, skip_stmts, last_stmt, whitelist, inline, force_addr, insn_bytes, size, num_inst, traceflags, thumb, extra_stop_points, opt_level, **kwargs)
140                 thumb=thumb,
141                 extra_stop_points=extra_stop_points,
--> 142                 opt_level=opt_level)
143
144     def _check(self, state, *args, **kwargs):

~/angr-dev/angr/angr/engines/engine.py in process(***failed resolving arguments***)
53         successors = new_state._inspect_getattr('sim_successors', successors)
54         try:
---> 55             self._process(new_state, successors, *args, **kwargs)
56         except SimException:
57             if o.EXCEPTION_HANDLING not in old_state.options:

~/angr-dev/angr/angr/engines/vex/engine.py in _process(self, state, successors, irsb, skip_stmts, last_stmt, whitelist, insn_bytes, size, num_inst, traceflags, thumb, extra_stop_points, opt_level)
191
192             try:
--> 193                 self._handle_irsb(state, successors, irsb, skip_stmts, last_stmt, whitelist)
194             except SimReliftException as e:
195                 state = e.state

~/angr-dev/angr/angr/engines/vex/engine.py in _handle_irsb(self, state, successors, irsb, skip_stmts, last_stmt, whitelist)
270                 state.scratch.stmt_idx = stmt_idx
271                 state._inspect('statement', BP_BEFORE, statement=stmt_idx)
--> 272                 cont = self._handle_statement(state, successors, stmt)
273                 state._inspect('statement', BP_AFTER)
274                 if not cont:

~/angr-dev/angr/angr/engines/vex/engine.py in _handle_statement(self, state, successors, stmt)
378
379         # process it!
--> 380         s_stmt = translate_stmt(stmt, state)
381         if s_stmt is not None:
382             state.history.extend_actions(s_stmt.actions)

~/angr-dev/angr/angr/engines/vex/statements/__init__.py in translate_stmt(stmt, state)
27         stmt_class = globals()[stmt_name]
28         s = stmt_class(stmt, state)
---> 29         s.process()
30         return s
31     else:

~/angr-dev/angr/angr/engines/vex/statements/base.py in process(self)
39         """
40         # this is where we would choose between different analysis modes
---> 41         self._execute()
42
43     def _execute(self):

~/angr-dev/angr/angr/engines/vex/statements/wrtmp.py in _execute(self)
8     def _execute(self):
9         # get data and track data reads
---> 10         data = self._translate_expr(self.stmt.data)
11         self.state.scratch.store_tmp(self.stmt.tmp, data.expr, data.reg_deps(), data.tmp_deps(),
12                                      action_holder=self.actions

~/angr-dev/angr/angr/engines/vex/statements/base.py in _translate_expr(self, expr)
46     def _translate_expr(self, expr):
47         """Translates an IRExpr into a SimIRExpr."""
---> 48         e = translate_expr(expr, self.state)
49         self._record_expr(e)
50         return e

~/angr-dev/angr/angr/engines/vex/expressions/__init__.py in translate_expr(expr, state)
12     l.debug("Processing expression %s", expr_name)
13     e = expr_class(expr, state)
---> 14     e.process()
15     return e
16

~/angr-dev/angr/angr/engines/vex/expressions/base.py in process(self)
38
39         # this should change when additional analyses are implemented
---> 40         self._execute()
41
42         self._post_process()

~/angr-dev/angr/angr/engines/vex/expressions/geti.py in _execute(self)
18
19         # get it!
---> 20         self.expr = self.state.registers.load(self.offset, size)
21
22         if self.type.startswith('Ity_F'):

~/angr-dev/angr/angr/storage/memory.py in load(self, addr, size, condition, fallback, add_constraints, action, endness, inspect, disable_actions, ret_on_segv)
802
803             elif self.category == 'reg':
--> 804                 self.state._inspect('reg_read', BP_AFTER, reg_read_expr=r)
805                 r = self.state._inspect_getattr("reg_read_expr", r)
806

~/angr-dev/angr/angr/sim_state.py in _inspect(self, *args, **kwargs)
257     def _inspect(self, *args, **kwargs):
258         if self.has_plugin('inspect'):
--> 259             self.inspect.action(*args, **kwargs)
260
261     def _inspect_getattr(self, attr, default_value):

~/angr-dev/angr/angr/state_plugins/inspect.py in action(self, event_type, when, **kwargs)
246             if bp.check(self.state, when):
247                 l.debug("... FIRE")
--> 248                 bp.fire(self.state)
249
250     def make_breakpoint(self, event_type, *args, **kwargs):

~/angr-dev/angr/angr/state_plugins/inspect.py in fire(self, state)
198                            "make modifications, then exit this shell to resume your analysis.")
199         else:
--> 200             self.action(state)
201
202     def __repr__(self):

~/angr-dev/angr/angr/analyses/variable_recovery/variable_recovery.py in _hook_register_read(self, state)
144         reg_read_length = state.inspect.reg_read_length
145
--> 146         if reg_read_offset == state.arch.sp_offset and reg_read_length == state.arch.bytes:
147             # TODO: make sure the sp is not overwritten by something that we are not tracking
148             return

~/angr-dev/claripy/claripy/ast/base.py in __bool__(self)
643         constraint solve. This caused tons of issues.
644         """
--> 645         raise ClaripyOperationError('testing Expressions for truthiness does not do what you want, as these expressions can be symbolic')
646
647     def structurally_match(self, o):

ClaripyOperationError: testing Expressions for truthiness does not do what you want, as these expressions can be symbolic

# Fixed code
def _hook_register_write(self, state):
    reg_write_offset = state.inspect.reg_write_offset
    if isinstance(reg_write_offset, claripy.ast.BV):
        if reg_write_offset.multivalued:
            # Multi-valued register offsets are not supported
            l.warning("Multi-valued register offsets are not supported.")
            return
        reg_write_offset = state.solver.eval(reg_write_offset)

    if reg_write_offset == state.arch.sp_offset:
        # it's updating stack pointer. skip
        return

    reg_write_expr = state.inspect.reg_write_expr
    reg_write_length = len(reg_write_expr) // 8

    # annotate it
    # reg_write_expr = reg_write_expr.annotate(VariableSourceAnnotation.from_state(state))

    state.inspect.reg_write_expr = reg_write_expr

    existing_vars = self.variable_manager[self.func_addr].find_variables_by_stmt(
        state.scratch.bbl_addr, state.scratch.stmt_idx, "register"
    )
    if not existing_vars:
        # create the variable
        variable = SimRegisterVariable(
            reg_write_offset,
            reg_write_length,
            ident=self.variable_manager[self.func_addr].next_variable_ident("register"),
            region=self.func_addr,
        )
        var_offset = self._normalize_register_offset(reg_write_offset)
        self.register_region.set_variable(var_offset, variable)
        # record this variable in variable manager
        self.variable_manager[self.func_addr].set_variable(
            "register", var_offset, variable
        )
        self.variable_manager[self.func_addr].write_to(
            variable, 0, self._codeloc_from_state(state)
        )

    # is it writing a pointer to a stack variable into the register?
    # e.g. lea eax, [ebp-0x40]
    stack_offset = self._addr_to_stack_offset(reg_write_expr)
    if stack_offset is not None:
        # it is!
        # unfortunately we don't know the size. We use size None for now.

        if stack_offset not in self.stack_region:
            lea_size = 1
            new_var = SimStackVariable(
                stack_offset,
                lea_size,
                base="bp",
                ident=self.variable_manager[self.func_addr].next_variable_ident(
                    "stack"
                ),
                region=self.func_addr,
            )
            self.stack_region.add_variable(stack_offset, new_var)

            # record this variable in variable manager
            self.variable_manager[self.func_addr].add_variable(
                "stack", stack_offset, new_var
            )

        base_offset = self.stack_region.get_base_addr(stack_offset)
        assert base_offset is not None
        for var in self.stack_region.get_variables_by_offset(stack_offset):
            self.variable_manager[self.func_addr].reference_at(
                var, stack_offset - base_offset, self._codeloc_from_state(state)
            )