def mark_boundaries(self, var_1, var_2): key = (var_1.conc_addr, var_2.conc_addr) if key in self.bounds_marked: return self.bounds_marked.add(key) diff = var_2.conc_addr - var_1.conc_addr self.solver.add(claripy.SLE(var_1.sym_addr + diff, var_2.sym_addr)) self.stack.unsafe_constraints.append( claripy.SLT(var_1.sym_addr + diff, var_2.sym_addr))
def get_filter_constraints(self, filter_opts): res = list() for opt in filter_opts: if opt["type"] == "limit_symbol": claripy_obj = self.symbols[opt["symbol_id"]].claripy_obj expr = claripy.And(claripy.SGE(claripy_obj, int(opt["min"])), claripy.SLE(claripy_obj, int(opt["max"]))) res.append(expr) return res
def check_symbol_range(state, range_symbols): for sid in range_symbols: symbol, rmin, rmax = range_symbols[sid] if not state.solver.satisfiable(extra_constraints=[ claripy.And( claripy.SGE(symbol, rmin), claripy.SLE(symbol, rmax) ) ]): return False return True
def sym_link(self, stack, solver): solver.add(stack.sym_size >= stack.conc_size) solver.add(stack.sym_size % (stack.arch.bytes) == 0) stack.unsafe_constraints.append(stack.sym_size > stack.conc_size) first = stack.variables[stack.addr_list[0]] solver.add(first.sym_addr >= (first.conc_addr + stack.conc_size) - stack.sym_size) var_list = list(stack) for var, next_var in zip(var_list, var_list[1:] + [None]): var.sym_link(solver, stack) stack.unsafe_constraints.extend(var.unsafe_constraints) if var.conc_addr % (stack.arch.bytes) == 0: solver.add(var.sym_addr % (stack.arch.bytes) == 0) if var.special: # We're one of the args that needs to stay fixed relative somewhere pass elif next_var is None or next_var.special_bottom: # If we're the last free-floating variable, set a solid bottom solver.add(var.sym_addr <= var.conc_addr) if var.size is not None: solver.add( claripy.SLE(var.sym_addr, var.sym_addr + var.size)) solver.add(var.sym_addr + var.size <= next_var.sym_addr) stack.unsafe_constraints.append( var.sym_addr + var.size < next_var.sym_addr) else: # Otherwise we're one of the free-floating variables solver.add(var.sym_addr <= var.sym_addr + var.size) stack.unsafe_constraints.append( var.sym_addr + var.size < next_var.sym_addr) if self.safe: solver.add(var.sym_addr + var.size == next_var.sym_addr) else: solver.add(var.sym_addr + var.size <= next_var.sym_addr)
def claripy_ast_from_ail_condition(self, condition) -> claripy.ast.Base: # Unpack a condition all the way to the leaves if isinstance(condition, claripy.ast.Base): # pylint:disable=isinstance-second-argument-not-valid-type return condition def _op_with_unified_size(op, conv, operand0, operand1): # ensure operand1 is of the same size as operand0 if isinstance(operand1, ailment.Expr.Const): # amazing - we do the eazy thing here return op(conv(operand0), operand1.value) if operand1.bits == operand0.bits: return op(conv(operand0), conv(operand1)) # extension is required assert operand1.bits < operand0.bits operand1 = ailment.Expr.Convert(None, operand1.bits, operand0.bits, False, operand1) return op(conv(operand0), conv(operand1)) _mapping = { 'LogicalAnd': lambda expr, conv: claripy.And(conv(expr.operands[0]), conv(expr.operands[1])), 'LogicalOr': lambda expr, conv: claripy.Or(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpEQ': lambda expr, conv: conv(expr.operands[0]) == conv(expr.operands[1]), 'CmpNE': lambda expr, conv: conv(expr.operands[0]) != conv(expr.operands[1]), 'CmpLE': lambda expr, conv: conv(expr.operands[0]) <= conv(expr.operands[1]), 'CmpLEs': lambda expr, conv: claripy.SLE(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpLT': lambda expr, conv: conv(expr.operands[0]) < conv(expr.operands[1]), 'CmpLTs': lambda expr, conv: claripy.SLT(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpGE': lambda expr, conv: conv(expr.operands[0]) >= conv(expr.operands[1]), 'CmpGEs': lambda expr, conv: claripy.SGE(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpGT': lambda expr, conv: conv(expr.operands[0]) > conv(expr.operands[1]), 'CmpGTs': lambda expr, conv: claripy.SGT(conv(expr.operands[0]), conv(expr.operands[1])), 'Add': lambda expr, conv: conv(expr.operands[0]) + conv(expr.operands[1]), 'Sub': lambda expr, conv: conv(expr.operands[0]) - conv(expr.operands[1]), 'Mul': lambda expr, conv: conv(expr.operands[0]) * conv(expr.operands[1]), 'Not': lambda expr, conv: claripy.Not(conv(expr.operand)), 'Xor': lambda expr, conv: conv(expr.operands[0]) ^ conv(expr.operands[1]), 'And': lambda expr, conv: conv(expr.operands[0]) & conv(expr.operands[1]), 'Or': lambda expr, conv: conv(expr.operands[0]) | conv(expr.operands[1]), 'Shr': lambda expr, conv: _op_with_unified_size(claripy.LShR, conv, expr.operands[0], expr.operands[1]), 'Shl': lambda expr, conv: _op_with_unified_size(operator.lshift, conv, expr.operands[0], expr.operands[1]), 'Sar': lambda expr, conv: _op_with_unified_size(operator.rshift, conv, expr.operands[0], expr.operands[1]), } if isinstance(condition, (ailment.Expr.Load, ailment.Expr.DirtyExpression, ailment.Expr.BasePointerOffset, ailment.Expr.ITE, ailment.Stmt.Call)): var = claripy.BVS('ailexpr_%s' % repr(condition), condition.bits, explicit_name=True) self._condition_mapping[var.args[0]] = condition return var elif isinstance(condition, ailment.Expr.Register): var = claripy.BVS('ailexpr_%s-%d' % (repr(condition), condition.idx), condition.bits, explicit_name=True) self._condition_mapping[var.args[0]] = condition return var elif isinstance(condition, ailment.Expr.Convert): # convert is special. if it generates a 1-bit variable, it should be treated as a BVS if condition.to_bits == 1: var_ = self.claripy_ast_from_ail_condition(condition.operands[0]) name = 'ailcond_Conv(%d->%d, %s)' % (condition.from_bits, condition.to_bits, repr(var_)) var = claripy.BoolS(name, explicit_name=True) else: var_ = self.claripy_ast_from_ail_condition(condition.operands[0]) name = 'ailexpr_Conv(%d->%d, %s)' % (condition.from_bits, condition.to_bits, repr(var_)) var = claripy.BVS(name, condition.to_bits, explicit_name=True) self._condition_mapping[var.args[0]] = condition return var elif isinstance(condition, ailment.Expr.Const): var = claripy.BVV(condition.value, condition.bits) return var elif isinstance(condition, ailment.Expr.Tmp): l.warning("Left-over ailment.Tmp variable %s.", condition) if condition.bits == 1: var = claripy.BoolV('ailtmp_%d' % condition.tmp_idx) else: var = claripy.BVS('ailtmp_%d' % condition.tmp_idx, condition.bits, explicit_name=True) self._condition_mapping[var.args[0]] = condition return var lambda_expr = _mapping.get(condition.verbose_op, None) if lambda_expr is None: raise NotImplementedError("Unsupported AIL expression operation %s. Consider implementing." % condition.op) r = lambda_expr(condition, self.claripy_ast_from_ail_condition) if r is NotImplemented: r = claripy.BVS("ailexpr_%r" % condition, condition.bits, explicit_name=True) self._condition_mapping[r.args[0]] = condition else: # don't lose tags r = r.annotate(TagsAnnotation(**condition.tags)) return r
def run(self, _, length): # pylint: disable=W0221 bvs = claripy.BVS("Random.nextInt", 32) cs = claripy.And(claripy.SGE(bvs, 0), claripy.SLE(bvs, length)) self.state.add_constraints(cs) return bvs
def constrain_variables(self, func, solver, stack): self.offsets = self.funcdata[func.addr] self.bounds_marked = set() self.stack = stack self.solver = solver # do some sanity checking first top = min(self.offsets) for addr in stack.addr_list: if addr < top: raise Exception("Provided vars miss an access (off the top!)") base_addr = addr while base_addr not in self.offsets: base_addr -= 1 this_offset = addr - base_addr if this_offset >= self.offsets[base_addr][0]: raise Exception( "Provided vars miss an access (between the cracks!)") i = 0 while i < len(stack.addr_list): addr = stack.addr_list[i] if addr in self.offsets: if i != 0 and self.offsets[stack.addr_list[ i - 1]][0] + stack.addr_list[i - 1] > addr: raise Exception("Provided vars have an overlap!") i += 1 continue stack.merge_up(i) # standard stuff stack.alloc_op.apply_constraints(solver) solver.add(stack.alloc_op.symval == -stack.sym_size) for op in stack.dealloc_ops: op.apply_constraints(solver) solver.add(op.symval == 0) solver.add(stack.sym_size % stack.arch.bytes == 0) solver.add(claripy.SGE(stack.sym_size, stack.conc_size)) stack.unsafe_constraints.append( claripy.SGT(stack.sym_size, stack.conc_size)) stack.unsafe_constraints.append( claripy.SGE(stack.sym_size, stack.conc_size * 2)) stack.unsafe_constraints.append( claripy.SLT(stack.sym_size, stack.conc_size * 3)) # loop through variables, add the important constraints! i = 0 while i < len(stack.addr_list): addr = stack.addr_list[i] var = stack.variables[addr] var.size = self.offsets[addr][0] fix = self.offsets[addr][1] if fix == 'TOP': var.special_top = True elif fix == 'BOTTOM': var.special_bottom = True align = self.offsets[addr][2] if align != 1: solver.add(var.sym_addr % align == 0) var.sym_link( solver, stack) # this hooks up the constrains to actual immediates # also the top/bottom fixing happens in there if i != 0: prev_var = stack.variables[stack.addr_list[i - 1]] self.mark_boundaries(prev_var, var) if i != len(stack.addr_list) - 1: next_var = stack.variables[stack.addr_list[i + 1]] self.mark_boundaries(var, next_var) # ew. ew ew ew ew ew ew!!! diff = next_var.conc_addr - var.conc_addr solver.add(claripy.SLT(var.sym_addr, var.sym_addr + diff)) if i == 0: solver.add(claripy.SLE(-stack.sym_size, var.sym_addr)) i += 1
def claripy_ast_from_ail_condition(self, condition): # Unpack a condition all the way to the leaves if isinstance(condition, claripy.ast.Base): return condition _mapping = { 'LogicalAnd': lambda expr, conv: claripy.And(conv(expr.operands[0]), conv(expr.operands[1])), 'LogicalOr': lambda expr, conv: claripy.Or(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpEQ': lambda expr, conv: conv(expr.operands[0]) == conv(expr.operands[1] ), 'CmpNE': lambda expr, conv: conv(expr.operands[0]) != conv(expr.operands[1] ), 'CmpLE': lambda expr, conv: conv(expr.operands[0]) <= conv(expr.operands[1] ), 'CmpLEs': lambda expr, conv: claripy.SLE(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpLT': lambda expr, conv: conv(expr.operands[0]) < conv(expr.operands[1]), 'CmpLTs': lambda expr, conv: claripy.SLT(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpGE': lambda expr, conv: conv(expr.operands[0]) >= conv(expr.operands[1] ), 'CmpGEs': lambda expr, conv: claripy.SGE(conv(expr.operands[0]), conv(expr.operands[1])), 'CmpGT': lambda expr, conv: conv(expr.operands[0]) > conv(expr.operands[1]), 'CmpGTs': lambda expr, conv: claripy.SGT(conv(expr.operands[0]), conv(expr.operands[1])), 'Add': lambda expr, conv: conv(expr.operands[0]) + conv(expr.operands[1]), 'Sub': lambda expr, conv: conv(expr.operands[0]) - conv(expr.operands[1]), 'Not': lambda expr, conv: claripy.Not(conv(expr.operand)), 'Xor': lambda expr, conv: conv(expr.operands[0]) ^ conv(expr.operands[1]), 'And': lambda expr, conv: conv(expr.operands[0]) & conv(expr.operands[1]), 'Or': lambda expr, conv: conv(expr.operands[0]) | conv(expr.operands[1]), 'Shr': lambda expr, conv: claripy.LShR(conv(expr.operands[0]), expr. operands[1].value) } if isinstance(condition, (ailment.Expr.Load, ailment.Expr.DirtyExpression, ailment.Expr.BasePointerOffset)): var = claripy.BVS('ailexpr_%s' % repr(condition), condition.bits, explicit_name=True) self._condition_mapping[var] = condition return var elif isinstance(condition, ailment.Expr.Register): var = claripy.BVS('ailexpr_%s-%d' % (repr(condition), condition.idx), condition.bits, explicit_name=True) self._condition_mapping[var] = condition return var elif isinstance(condition, ailment.Expr.Convert): # convert is special. if it generates a 1-bit variable, it should be treated as a BVS if condition.to_bits == 1: var_ = self.claripy_ast_from_ail_condition( condition.operands[0]) name = 'ailcond_Conv(%d->%d, %s)' % ( condition.from_bits, condition.to_bits, repr(var_)) var = claripy.BoolS(name, explicit_name=True) else: var_ = self.claripy_ast_from_ail_condition( condition.operands[0]) name = 'ailexpr_Conv(%d->%d, %s)' % ( condition.from_bits, condition.to_bits, repr(var_)) var = claripy.BVS(name, condition.to_bits, explicit_name=True) self._condition_mapping[var] = condition return var elif isinstance(condition, ailment.Expr.Const): var = claripy.BVV(condition.value, condition.bits) return var elif isinstance(condition, ailment.Expr.Tmp): l.warning("Left-over ailment.Tmp variable %s.", condition) if condition.bits == 1: var = claripy.BoolV('ailtmp_%d' % condition.tmp_idx) else: var = claripy.BVS('ailtmp_%d' % condition.tmp_idx, condition.bits) self._condition_mapping[var] = condition return var lambda_expr = _mapping.get(condition.verbose_op, None) if lambda_expr is None: raise NotImplementedError( "Unsupported AIL expression operation %s. Consider implementing." % condition.op) r = lambda_expr(condition, self.claripy_ast_from_ail_condition) if r is NotImplemented: r = claripy.BVS("ailexpr_%r" % condition, condition.bits, explicit_name=True) self._condition_mapping[r] = condition return r
def __init__(self): build_tree({ "id": 1, "block": "B1", "children": [ { "id": 2, "block": "B2", "children": [] }, { "id": 3, "block": "B4", "children": [] } ] }, TreePrunerTests.t1, 0) TreePrunerTests.l1 = build_identity_dict({"B1", "B2", "B4"}) build_tree({ "id": 9, "block": "B7", "children": [ { "id": 1, "block": "B5", "children": [ { "id": 2, "block": "B3", "children": [ { "id": 4, "block": "B2", "children": [] }, { "id": 5, "block": "B1", "children": [ { "id": 6, "block": "B6", "children": [] } ] } ] }, { "id": 3, "block": "B4", "children": [ { "id": 7, "block": "B3", "children": [ { "id": 8, "block": "B1", "children": [] } ] }, { "id": 11, "block": "B8", "children": [] } ] } ] }, { "id": 10, "block": "B9", "children": [] } ] }, TreePrunerTests.t2, 0) TreePrunerTests.l2 = build_identity_dict({"B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9"}) build_tree({ "id": 1, "block": "B1", "children": [ { "id": 2, "block": "B2", "children": [] }, { "id": 3, "block": "B1", "children": [ { "id": 4, "block": "B2", "children": [] }, { "id": 5, "block": "B1", "children": [ { "id": 6, "block": "B2", "children": [] }, { "id": 7, "block": "B1", "children": [ { "id": 8, "block": "B2", "children": [] }, { "id": 9, "block": "B1", "children": [] } ] } ] } ] } ] }, TreePrunerTests.t3, 0) TreePrunerTests.l3 = build_identity_dict({"B1", "B2"}) s1 = claripy.BVS('s1', 32) build_tree({ "id": 1, "block": "B1", "children": [ { "id": 2, "block": "B2", "children": [], "constraints": [ claripy.SGT(s1, 0) ] }, { "id": 3, "block": "B4", "children": [], "constraints": [ claripy.SLE(s1, 0) ] } ] }, TreePrunerTests.t4, 0) TreePrunerTests.l4 = build_identity_dict({"B1", "B2", "B4"}) TreePrunerTests.s4 = { 's1': Symbol('s1', '', s1, 1, "", "UNKNOWN") } build_tree({ "id": 1, "block": "B1", "children": [ { "id": 2, "block": "B3", "constraints": [ claripy.SLE(s1, 10) ], "children": [] }, { "id": 3, "block": "B2", "constraints": [ claripy.SGT(s1, 10) ], "children": [ { "id": 4, "block": "B4", "constraints": [ claripy.SGT(s1, 10), claripy.SLE(s1, 20) ], "children": [] }, { "id": 5, "block": "B5", "constraints": [ claripy.SGT(s1, 20) ], "children": [] } ] } ] }, TreePrunerTests.t5, 0) TreePrunerTests.l5 = build_identity_dict({"B1", "B2", "B3", "B4", "B5"}) TreePrunerTests.s5 = { 's1': Symbol('s1', '', s1, 1, "", "UNKNOWN") } build_tree({ "id": 1, "block": "B1", "children": [ { "id": 2, "block": "B2", "children": [] }, { "id": 3, "block": "B1", "children": [ { "id": 4, "block": "B2", "children": [] }, { "id": 5, "block": "B1", "children": [ { "id": 6, "block": "B2", "children": [] }, { "id": 7, "block": "B1", "children": [ { "id": 8, "block": "B2", "children": [] }, { "id": 9, "block": "B1", "children": [] } ] } ] } ] } ] }, TreePrunerTests.t6, 0) TreePrunerTests.l6 = build_identity_dict({"B1", "B2"})