angr / angrop

BSD 2-Clause "Simplified" License
606 stars 71 forks source link

Getting "TypeError: reduce() of empty sequence with no initial value" #20

Closed fmagin closed 2 years ago

fmagin commented 7 years ago

npFoxitReaderPlugin.zip I am trying to generate a ROP chain in the attached dll and angrop fails with the following type error, with the code:

import angr, angrop
proj = angr.Project("npFoxitReaderPlugin.dll")
rop = proj.analyses.ROP()
rop.find_gadgets_single_threaded()

The TypeError also happens with find_gadgets() but with multiple threads it continues anyway and the error gets hidden in the info output

TypeError                                 Traceback (most recent call last)
<ipython-input-6-5663c5ee5dc3> in <module>()
----> 1 rop.find_gadgets_single_threaded()

/home/fmagin/gits/angr-dev/angrop/angrop/rop.py in find_gadgets_single_threaded(self)
    158         _set_global_gadget_analyzer(self._gadget_analyzer)
    159         for _, addr in enumerate(self._addresses_to_check_with_caching()):
--> 160             gadget = _global_gadget_analyzer.analyze_gadget(addr)
    161             if gadget is not None:
    162                 if isinstance(gadget, RopGadget):

/home/fmagin/gits/angr-dev/angrop/angrop/gadget_analyzer.py in analyze_gadget(self, addr)
     56             symbolic_state = self._test_symbolic_state.copy()
     57             symbolic_state.ip = addr
---> 58             symbolic_p = rop_utils.step_to_unconstrained_successor(self.project, state=symbolic_state)
     59 
     60             l.debug("... analyzing rop potential of block")

/home/fmagin/gits/angr-dev/angrop/angrop/rop_utils.py in step_to_unconstrained_successor(project, state, max_steps, allow_simprocedures)
    207         state.options.add(angr.options.BYPASS_UNSUPPORTED_SYSCALL)
    208 
--> 209         succ = project.factory.successors(state)
    210         if len(succ.flat_successors) + len(succ.unconstrained_successors) != 1:
    211             raise RopException("Does not get to a single successor")

/home/fmagin/gits/angr-dev/angr/angr/factory.py in successors(self, state, addr, jumpkind, inline, default_engine, engines, **kwargs)
     75         for engine in engines:
     76             if engine.check(state, inline=inline, **kwargs):
---> 77                 r = engine.process(state, inline=inline,**kwargs)
     78                 if r.processed:
     79                     break

/home/fmagin/gits/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, opt_level, **kwargs)
     99                 traceflags=traceflags,
    100                 thumb=thumb,
--> 101                 opt_level=opt_level)
    102 
    103     def _check(self, state, *args, **kwargs):

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

/home/fmagin/gits/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, opt_level)
    144 
    145             try:
--> 146                 self._handle_irsb(state, successors, irsb, skip_stmts, last_stmt, whitelist)
    147             except SimReliftException as e:
    148                 state = e.state

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/engine.py in _handle_irsb(self, state, successors, irsb, skip_stmts, last_stmt, whitelist)
    216                 state.scratch.stmt_idx = stmt_idx
    217                 state._inspect('statement', BP_BEFORE, statement=stmt_idx)
--> 218                 self._handle_statement(state, successors, stmt)
    219                 state._inspect('statement', BP_AFTER)
    220             except UnsupportedDirtyError:

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/engine.py in _handle_statement(self, state, successors, stmt)
    323 
    324         # process it!
--> 325         s_stmt = translate_stmt(stmt, state)
    326         if s_stmt is not None:
    327             state.history.extend_actions(s_stmt.actions)

/home/fmagin/gits/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:

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/statements/base.py in process(self)
     18         """
     19         # this is where we would choose between different analysis modes
---> 20         self._execute()
     21 
     22     def _execute(self):

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/statements/wrtmp.py in _execute(self)
      4     def _execute(self):
      5         # get data and track data reads
----> 6         data = self._translate_expr(self.stmt.data)
      7         self.state.scratch.store_tmp(self.stmt.tmp, data.expr, data.reg_deps(), data.tmp_deps(),
      8                                      action_holder=self.actions

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/statements/base.py in _translate_expr(self, expr)
     25     def _translate_expr(self, expr):
     26         """Translates an IRExpr into a SimIRExpr."""
---> 27         e = translate_expr(expr, self.state)
     28         self._record_expr(e)
     29         return e

/home/fmagin/gits/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 

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/expressions/base.py in process(self)
     34 
     35         # this should change when additional analyses are implemented
---> 36         self._execute()
     37 
     38         self._post_process()

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/expressions/ccall.py in _execute(self)
     21             try:
     22                 func = getattr(ccall, self._expr.callee.name)
---> 23                 self.expr, retval_constraints = func(self.state, *s_args)
     24                 self._constraints.extend(retval_constraints)
     25             except SimCCallError:

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/ccall.py in x86g_calculate_eflags_c(state, cc_op, cc_dep1, cc_dep2, cc_ndep)
    900 
    901 def x86g_calculate_eflags_c(state, cc_op, cc_dep1, cc_dep2, cc_ndep):
--> 902     return pc_calculate_rdata_c(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform='X86')
    903 
    904 def x86g_check_fldcw(state, fpucw):

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/ccall.py in pc_calculate_rdata_c(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform)
    786         return state.se.BVV(0, state.arch.bits), [ ] # TODO: actual constraints
    787 
--> 788     rdata_all = pc_calculate_rdata_all_WRK(state, cc_op,cc_dep1,cc_dep2,cc_ndep, platform=platform)
    789 
    790     if isinstance(rdata_all, tuple):

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/ccall.py in pc_calculate_rdata_all_WRK(state, cc_op, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform)
    453     if cc_str in [ 'G_CC_OP_ADCB', 'G_CC_OP_ADCW', 'G_CC_OP_ADCL', 'G_CC_OP_ADCQ' ]:
    454         l.debug("cc_str: ADC")
--> 455         return pc_actions_ADC(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    456 
    457     if cc_str in [ 'G_CC_OP_SUBB', 'G_CC_OP_SUBW', 'G_CC_OP_SUBL', 'G_CC_OP_SUBQ' ]:

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/ccall.py in pc_actions_ADC(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform)
    324     zf = calc_zerobit(state, res)
    325     sf = res[nbits - 1]
--> 326     of = ((arg_l ^ arg_r ^ -1) & (arg_l ^ res))[nbits-1]
    327 
    328     return pc_make_rdata(data[platform]['size'], cf, pf, af, zf, sf, of, platform=platform)

/home/fmagin/gits/angr-dev/claripy/claripy/operations.py in _op(*args)
     56         #pylint:disable=too-many-nested-blocks
     57         if name in simplifiers:
---> 58             simp = _handle_annotations(simplifiers[name](*fixed_args), args)
     59             if simp is not None:
     60                 return simp

/home/fmagin/gits/angr-dev/claripy/claripy/operations.py in bitwise_xor_simplifier(a, b)
    426         return tuple([ arg for arg in args if arg in unique_args ])
    427 
--> 428     return _flatten_simplifier('__xor__', _flattening_filter, a, b)
    429 
    430 def bitwise_or_simplifier(a, b):

/home/fmagin/gits/angr-dev/claripy/claripy/operations.py in _flatten_simplifier(op_name, filter_func, *args)
    392     ))
    393     if filter_func: new_args = filter_func(new_args)
--> 394     return next(a for a in args if isinstance(a, ast.Base)).make_like(op_name, new_args)
    395 
    396 def bitwise_add_simplifier(a, b):

/home/fmagin/gits/angr-dev/claripy/claripy/ast/bits.py in make_like(self, *args, **kwargs)
     18     def make_like(self, *args, **kwargs):
     19         if 'length' not in kwargs: kwargs['length'] = self.length
---> 20         return Base.make_like(self, *args, **kwargs)
     21 
     22     def size(self):

/home/fmagin/gits/angr-dev/claripy/claripy/ast/base.py in make_like(self, *args, **kwargs)
    304         if 'uninitialized' not in kwargs: kwargs['uninitialized'] = self._uninitialized
    305         if 'symbolic' not in kwargs and self.op in all_operations: kwargs['symbolic'] = self.symbolic
--> 306         return type(self)(*args, **kwargs)
    307 
    308     def _rename(self, new_name):

/home/fmagin/gits/angr-dev/claripy/claripy/ast/base.py in __new__(cls, op, args, **kwargs)
    132             for eb in eager_backends:
    133                 try:
--> 134                     r = operations._handle_annotations(eb._abstract(eb.call(op, args)), args)
    135                     if r is not None:
    136                         return r

/home/fmagin/gits/angr-dev/claripy/claripy/backends/__init__.py in call(self, op, args)
    197         if op in self._op_raw:
    198             # the raw ops don't get the model, cause, for example, Z3 stuff can't take it
--> 199             obj = self._op_raw[op](*converted)
    200         elif not op.startswith("__"):
    201             l.debug("backend has no operation %s", op)

/home/fmagin/gits/angr-dev/claripy/claripy/backends/backend_concrete.py in _op_xor(*args)
     52     @staticmethod
     53     def _op_xor(*args):
---> 54         return reduce(operator.__xor__, args)
     55     @staticmethod
     56     def _op_and(*args):

TypeError: reduce() of empty sequence with no initial value
fmagin commented 7 years ago

The first issue seems to be that the call() method in https://github.com/angr/claripy/blob/3c05b8ebb256842ff7fb4de14a2b268e948cc419/claripy/backends/__init__.py#L189-L199 does not enforce that args actually contain anything, but I that still leaves the question why it even gets called without any args

fmagin commented 7 years ago

After wrapping a try catch around the first statement of the trace and printing the addresses with errors I got the following list:

failed_addrs = [0x10006b4c, 0x10006b4e, 0x1000a14f, 0x1000a3bf, 0x1000aac7, 0x1000ac81,0x1000ad5f ,0x1000bb05 ,0x1000c691, 0x1002f25e, 0x1002f260]
failed_blocks = [proj.factory.block(addr) for addr in failed_addrs]
for block in blocks:
    block.pp()
    print("")
0x10006b4c: mov dh, 4
0x10006b4e: adc bh, bh
0x10006b50: adc eax, 0x10036208
0x10006b55: pop edi
0x10006b56: ret 

0x10006b4e: adc bh, bh
0x10006b50: adc eax, 0x10036208
0x10006b55: pop edi
0x10006b56: ret 

0x1000a14f: adc bh, bh
0x1000a151: adc eax, 0x10036314
0x1000a156: pop esi
0x1000a157: ret 

0x1000a3bf: adc bh, bh
0x1000a3c1: adc eax, 0x100361e0
0x1000a3c6: pop edi
0x1000a3c7: pop esi
0x1000a3c8: pop ebx
0x1000a3c9: leave   
0x1000a3ca: ret 

0x1000aac7: adc bh, bh
0x1000aac9: adc eax, 0x100361b4
0x1000aace: pop edi
0x1000aacf: pop esi
0x1000aad0: ret 

0x1000ac81: adc bh, bh
0x1000ac83: salc    
0x1000ac84: pop esi
0x1000ac85: ret 

0x1000ad5f: adc bh, bh
0x1000ad61: adc eax, 0x10036320
0x1000ad66: pop ebp
0x1000ad67: ret 

0x1000bb05: adc bh, bh
0x1000bb07: adc eax, 0x10036200
0x1000bb0c: pop esi
0x1000bb0d: ret 

0x1000c691: adc bh, bh
0x1000c693: adc eax, 0x1003616c
0x1000c698: ret

So the obvious thing is that they all contain an adc bh, bh instruction which in hindsight makes sense due to this section of the stack trace

/home/fmagin/gits/angr-dev/angr/angr/engines/vex/ccall.py in pc_actions_ADC(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform)
    324     zf = calc_zerobit(state, res)
    325     sf = res[nbits - 1]
--> 326     of = ((arg_l ^ arg_r ^ -1) & (arg_l ^ res))[nbits-1]
    327 
    328     return pc_make_rdata(data[platform]['size'], cf, pf, af, zf, sf, of, platform=platform)

I don't understand yet why exactly this is an issue but my guess is that because in operations.py

if filter_func: new_args = filter_func(new_args)

leads to new_args being empty after applying the filter function this leads to issues further down.

github-actions[bot] commented 2 years ago

This issue has been marked as stale because it has no recent activity. Please comment or add the pinned tag to prevent this issue from being closed.

github-actions[bot] commented 2 years ago

This issue has been closed due to inactivity.