def write(self, offset, expr): """ Write @expr at @offset @offset: integer (in bytes) @expr: Expr instance value """ assert expr.size % 8 == 0 assert offset <= self._mask for index in xrange(expr.size / 8): # Wrap write: # @32[EAX+0xFFFFFFFF] is ok and will write at 0xFFFFFFFF, 0, 1, 2 request_offset = (offset + index) & self._mask # XXX TODO: only little endian here self._offset_to_expr[request_offset] = (index, expr) tmp = self.expr_simp(expr[index * 8: (index + 1) * 8]) # Special case: Simplify slice of pointer (simplification is ok # here, as we won't store the simplified expression) if tmp.is_slice() and tmp.arg.is_mem() and tmp.start % 8 == 0: new_ptr = self.expr_simp(tmp.arg.arg + ExprInt(tmp.start / 8, tmp.arg.arg.size)) tmp = ExprMem(new_ptr, tmp.stop - tmp.start) # Test if write to original value if tmp.is_mem(): src_ptr, src_off = get_expr_base_offset(tmp.arg) if src_ptr == self.base and src_off == request_offset: del self._offset_to_expr[request_offset]
def write(self, offset, expr): """ Write @expr at @offset @offset: integer (in bytes) @expr: Expr instance value """ assert expr.size % 8 == 0 assert offset <= self._mask for index in xrange(expr.size / 8): # Wrap write: # @32[EAX+0xFFFFFFFF] is ok and will write at 0xFFFFFFFF, 0, 1, 2 request_offset = (offset + index) & self._mask # XXX TODO: only little endian here self._offset_to_expr[request_offset] = (index, expr) tmp = self.expr_simp(expr[index * 8:(index + 1) * 8]) # Special case: Simplify slice of pointer (simplification is ok # here, as we won't store the simplified expression) if tmp.is_slice() and tmp.arg.is_mem() and tmp.start % 8 == 0: new_ptr = self.expr_simp(tmp.arg.ptr + ExprInt(tmp.start / 8, tmp.arg.ptr.size)) tmp = ExprMem(new_ptr, tmp.stop - tmp.start) # Test if write to original value if tmp.is_mem(): src_ptr, src_off = get_expr_base_offset(tmp.ptr) if src_ptr == self.base and src_off == request_offset: del self._offset_to_expr[request_offset]
def test_lb(self): """Test LB executon""" # LB Rn,(Rm) exec_instruction("LB R1, (R2)", [(ExprId("R2", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0x10, 32), 8), ExprInt(0xF0, 8))], [(ExprId("R1", 32), ExprInt(0xFFFFFFF0, 32))]) # LB Rn[0-7],disp7(TP) exec_instruction("LB R7, 0x3(TP)", [(ExprId("TP", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0x13, 32), 8), ExprInt(0xF0, 8))], [(ExprId("R7", 32), ExprInt(0xFFFFFFF0, 32))]) # LB Rn,disp16(Rm) exec_instruction( "LB R10, 0xF800(R2)", [(ExprId("R2", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0xFFFFF810, 32), 8), ExprInt(0x4, 8))], [(ExprId("R10", 32), ExprInt(0x4, 32))]) exec_instruction( "LB R10, 0xF800(R2)", [(ExprId("R2", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0xFFFFF810, 32), 8), ExprInt(0xFE, 8))], [(ExprId("R10", 32), ExprInt(0xFFFFFFFE, 32))])
def _resolve_mem_parts(self, expr): """For a given ExprMem @expr, get known/unknown parts from the store. @expr: ExprMem instance Return a list of (known, value) where known is a bool representing if the value has been resolved from the store or not. """ # Extract known parts in symbols assert expr.size % 8 == 0 ptr = expr.ptr known = [] ptrs = [] for index in xrange(expr.size / 8): offset = self.expr_simp(ptr + ExprInt(index, ptr.size)) ptrs.append(offset) mem = ExprMem(offset, 8) known.append(mem in self.symbols) reads = merge_ptr_read(known, ptrs) out = [] for is_known, ptr_value, size in reads: mem = ExprMem(ptr_value, size) if is_known: mem = self.symbols.read(mem) out.append((is_known, mem)) return out
def test_sw(self): """Test SW execution""" # SW Rn,(Rm) exec_instruction( "SW R1, (R2)", [(ExprId("R1", 32), ExprInt(0x28071010, 32)), (ExprId("R2", 32), ExprInt(0x10, 32))], [(ExprMem(ExprInt(0x10, 32), 32), ExprInt(0x28071010, 32))]) # SW Rn,disp7.align4(SP) exec_instruction( "SW R1, 4(SP)", [(ExprId("R1", 32), ExprInt(0x28071010, 32)), (ExprId("SP", 32), ExprInt(0x10, 32))], [(ExprMem(ExprInt(0x14, 32), 32), ExprInt(0x28071010, 32))]) # SW Rn,disp7.align4(TP) exec_instruction( "SW R1, 12(TP)", [(ExprId("R1", 32), ExprInt(0x28071010, 32)), (ExprId("TP", 32), ExprInt(0x10, 32))], [(ExprMem(ExprInt(0x1c, 32), 32), ExprInt(0x28071010, 32))]) # SW Rn,disp16(Rm) exec_instruction( "SW R10, 0xF800(R2)", [(ExprId("R10", 32), ExprInt(0xABC7, 32)), (ExprId("R2", 32), ExprInt(0x10, 32))], [(ExprMem(ExprInt(0xFFFFF810, 32), 32), ExprInt(0xABC7, 32))]) # SW Rn,(abs24.align4) exec_instruction( "SW R10, (0x1010)", [(ExprId("R10", 32), ExprInt(0xABC7, 32))], [(ExprMem(ExprInt(0x1010, 32), 32), ExprInt(0xABC7, 32))])
def test_lh(self): """Test lh execution""" # LH Rn,(Rm) exec_instruction( "LH R1, (R2)", [(ExprId("R2", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0x10, 32), 16), ExprInt(0xF517, 16))], [(ExprId("R1", 32), ExprInt(0xFFFFF517, 32))]) # LH Rn[0-7],disp7.align2(TP) exec_instruction( "LH R1, 0x18(R2)", [(ExprId("R2", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0x28, 32), 16), ExprInt(0xF517, 16))], [(ExprId("R1", 32), ExprInt(0xFFFFF517, 32))]) # LH Rn,disp16(Rm) exec_instruction( "LH R9, 0xF000(R2)", [(ExprId("R2", 32), ExprInt(0x42, 32)), (ExprMem(ExprInt(0xFFFFF042, 32), 16), ExprInt(0x10, 16))], [(ExprId("R9", 32), ExprInt(0x10, 32))]) exec_instruction( "LH R9, 0xF000(R2)", [(ExprId("R2", 32), ExprInt(0x42, 32)), (ExprMem(ExprInt(0xFFFFF042, 32), 16), ExprInt(0xABCD, 16))], [(ExprId("R9", 32), ExprInt(0xFFFFABCD, 32))])
def mem_read(self, expr_mem): if not expr_mem.ptr.is_int(): return expr_mem dst_addr = int(expr_mem.ptr) # Split access in atomic accesses out = [] for addr in xrange(dst_addr, dst_addr + (expr_mem.size / 8)): if addr in self.dse_memory_range: # Symbolize memory access out.append(self.dse_memory_to_expr(addr)) continue atomic_access = ExprMem(ExprInt(addr, expr_mem.ptr.size), 8) if atomic_access in self.symbols: out.append( super(EmulatedSymbExec, self).mem_read(atomic_access)) else: # Get concrete value atomic_access = ExprMem(ExprInt(addr, expr_mem.ptr.size), 8) out.append(super(ESETrackModif, self).mem_read(atomic_access)) if len(out) == 1: # Trivial case (optimization) return out[0] # Simplify for constant merging (ex: {ExprInt(1, 8), ExprInt(2, 8)}) return self.expr_simp(ExprCompose(*out))
def manage_mem(self, expr, state, cache, level): ptr = self.apply_expr_on_state_visit_cache(expr.arg, state, cache, level+1) ret = ExprMem(ptr, expr.size) ret = self.get_mem_state(ret) if ret.is_mem() and not ret.arg.is_int() and ret.arg == ptr: ret = exprid_top(expr) assert expr.size == ret.size return ret
def test_bsetm(self): """Test BSETM execution""" # BSETM (Rm),imm3 exec_instruction("BSETM (R1), 1", [(ExprId("R1", 32), ExprInt(0x28, 32)), (ExprMem(ExprInt(0x28, 32), 8), ExprInt(0x1, 8))], [(ExprMem(ExprInt(0x28, 32), 8), ExprInt(0x3, 8))])
def test_tas(self): """Test TAS execution""" # TAS Rn,(Rm) exec_instruction("TAS R0, (R1)", [(ExprId("R1", 32), ExprInt(0x28, 32)), (ExprMem(ExprInt(0x28, 32), 8), ExprInt(0x2, 8))], [(ExprId("R0", 32), ExprInt(0x2, 32)), (ExprMem(ExprInt(0x28, 32), 8), ExprInt(0x1, 8))])
def ldp(ir, instr, arg1, arg2, arg3): e = [] addr, updt = get_mem_access(arg3) e.append(ExprAff(arg1, ExprMem(addr, arg1.size))) e.append( ExprAff(arg2, ExprMem(addr + ExprInt(arg1.size / 8, addr.size), arg2.size))) if updt: e.append(updt) return e, []
def simp_mem(e_s, expr): "Common simplifications on ExprMem" # @32[x?a:b] => x?@32[a]:@32[b] if expr.arg.is_cond(): cond = expr.arg ret = ExprCond(cond.cond, ExprMem(cond.src1, expr.size), ExprMem(cond.src2, expr.size)) return ret return expr
def simp_mem(_, expr): """ Common simplifications on ExprMem: @32[x?a:b] => x?@32[a]:@32[b] """ if expr.ptr.is_cond(): cond = expr.ptr ret = ExprCond(cond.cond, ExprMem(cond.src1, expr.size), ExprMem(cond.src2, expr.size)) return ret return expr
def reduce_op_field(self, node, _): """Reduce field operator (Struct or Union)""" if not node.expr.is_op('field'): return None assert len(node.args) == 2 out = [] assert isinstance(node.args[1].expr, ExprId) field = node.args[1].expr.name src, src_type = node.args[0].info struct_dst = src_type if isinstance(struct_dst, ObjCStruct): found = False for name, objtype, offset, _ in struct_dst.fields: if name != field: continue expr = src + ExprInt(offset, src.size) if isinstance(objtype, ObjCArray): # Case 4 pass elif isinstance(objtype, (ObjCStruct, ObjCUnion)): # Case 1 pass else: # Case 2 expr = ExprMem(expr, objtype.size * 8) assert not found found = True out = (expr, objtype) elif isinstance(struct_dst, ObjCUnion): found = False for name, objtype, offset, _ in struct_dst.fields: if name != field: continue expr = src + ExprInt(offset, src.size) if isinstance(objtype, ObjCArray): # Case 4 pass elif isinstance(objtype, (ObjCStruct, ObjCUnion)): # Case 1 pass else: # Case 2 expr = ExprMem(expr, objtype.size * 8) assert not found found = True out = (expr, objtype) else: raise NotImplementedError("unknown ObjC") assert found return out
def test_swcp(self): """Test SWCP execution""" # SWCP CRn,(Rm) exec_instruction( "SWCP C1, (R2)", [(ExprId("C1", 32), ExprInt(0x28071010, 32)), (ExprId("R2", 32), ExprInt(0x11, 32))], [(ExprMem(ExprInt(0x10, 32), 32), ExprInt(0x28071010, 32))]) # SWCP CRn,disp16(Rm) exec_instruction( "SWCP C10, 0xF800(R2)", [(ExprId("C10", 32), ExprInt(0xABC7, 32)), (ExprId("R2", 32), ExprInt(0x11, 32))], [(ExprMem(ExprInt(0xFFFFF810, 32), 32), ExprInt(0xABC7, 32))])
def test(): struct_lookup = StructLookup() ptr = ExprId('ECX') int4 = ExprInt(4, 32) tests = [ (ptr, StructLookup.FIELD_A_PTR), (ptr + int4, StructLookup.FIELD_A_PTR), (ptr + int4 * int4, StructLookup.FIELD_A_PTR), (ExprMem(ptr), StructLookup.FIELD_A), (ExprMem(ptr + int4 * int4), StructLookup.FIELD_A), ] for expr_in, result in tests: assert struct_lookup.reduce(expr_in).info == result
def test_lmcp(self): """Test LMCP execution""" # LMCP CRn[0-15],(Rm) exec_instruction( "LMCP C1, (R2)", [(ExprId("R2", 32), ExprInt(0x10, 32)), (ExprMem(ExprInt(0x10, 32), 32), ExprInt(0xABCD, 32))], [(ExprId("C1", 32), ExprInt(0xABCD, 32))]) # LMCP CRn[0-15],disp16(Rm) exec_instruction( "LMCP C9, 0xF000(R2)", [(ExprId("R2", 32), ExprInt(0x17, 32)), (ExprMem(ExprInt(0xFFFFF010, 32), 32), ExprInt(0x10, 32))], [(ExprId("C9", 32), ExprInt(0x10, 32))])
def strh(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append(ExprAssign(ExprMem(addr, 16), arg1[:16])) if updt: e.append(updt) return e, []
def ldrs_size(ir, instr, arg1, arg2, size): e = [] addr, updt = get_mem_access(arg2) e.append(ExprAssign(arg1, ExprMem(addr, size).signExtend(arg1.size))) if updt: e.append(updt) return e, []
def l_str(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append(ExprAssign(ExprMem(addr, arg1.size), arg1)) if updt: e.append(updt) return e, []
def manage_mem(self, expr, state, cache, level): ptr = self.apply_expr_on_state_visit_cache(expr.arg, state, cache, level + 1) ret = ExprMem(ptr, expr.size) ret = self.get_mem_state(ret) assert expr.size == ret.size return ret
def strb(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append(ExprAff(ExprMem(addr, 8), arg1[:8])) if updt: e.append(updt) return e, []
def emulbloc(self, irb, step=False): """ Symbolic execution of the @irb on the current state @irb: IRBlock instance @step: display intermediate steps """ assignblks = [] for index, assignblk in enumerate(irb.irs): new_assignblk = {} links = {} for dst, src in assignblk.iteritems(): src = self.propag_expr_cst(src) if dst.is_mem(): ptr = dst.arg ptr = self.propag_expr_cst(ptr) dst = ExprMem(ptr, dst.size) new_assignblk[dst] = src for arg in assignblk.instr.args: new_arg = self.propag_expr_cst(arg) links[new_arg] = arg self.cst_propag_link[(irb.label, index)] = links self.eval_ir(assignblk) assignblks.append(AssignBlock(new_assignblk, assignblk.instr)) self.ir_arch.blocks[irb.label] = IRBlock(irb.label, assignblks)
def ldr(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append(ExprAff(arg1, ExprMem(addr, arg1.size))) if updt: e.append(updt) return e, []
def reduce_op_memberof(self, node, _): """Reduce -> operator""" if not node.expr.is_op('->'): return None assert len(node.args) == 2 out = [] assert isinstance(node.args[1].expr, ExprId) field = node.args[1].expr.name src, src_type = node.args[0].info assert isinstance(src_type, (ObjCPtr, ObjCArray)) struct_dst = src_type.objtype assert isinstance(struct_dst, ObjCStruct) found = False for name, objtype, offset, _ in struct_dst.fields: if name != field: continue expr = src + ExprInt(offset, src.size) if isinstance(objtype, (ObjCArray, ObjCStruct, ObjCUnion)): pass else: expr = ExprMem(expr, objtype.size * 8) assert not found found = True out = (expr, objtype) assert found return out
def test_btstm(self): """Test BTSTM execution""" # BTSTM R0,(Rm),imm3 exec_instruction("BTSTM R0, (R1), 1", [(ExprId("R1", 32), ExprInt(0x28, 32)), (ExprMem(ExprInt(0x28, 32), 8), ExprInt(0x2, 8))], [(ExprId("R0", 32), ExprInt(0x2, 32))])
def ldaxrb(ir, instr, arg1, arg2): # TODO XXX no memory lock implemented assert arg2.is_op('preinc') assert len(arg2.args) == 1 ptr = arg2.args[0] e = [] e.append(ExprAssign(arg1, ExprMem(ptr, 8).zeroExtend(arg1.size))) return e, []
def stlxrb(ir, instr, arg1, arg2, arg3): assert arg3.is_op('preinc') assert len(arg3.args) == 1 ptr = arg3.args[0] e = [] e.append(ExprAssign(ExprMem(ptr, 8), arg2[:8])) # TODO XXX here, force update success e.append(ExprAssign(arg1, ExprInt(0, arg1.size))) return e, []
def test_smcpi(self): """Test SMCPI execution""" # SMCPI CRn[0-15],(Rm+) exec_instruction( "SMCPI C1, (R2+)", [(ExprId("C1", 32), ExprInt(0x28071010, 32)), (ExprId("R2", 32), ExprInt(0x17, 32))], [(ExprMem(ExprInt(0x10, 32), 32), ExprInt(0x28071010, 32)), (ExprId("R2", 32), ExprInt(0x1F, 32))])
def eval_exprmem(self, expr, **kwargs): """[DEV]: Evaluate an ExprMem using the current state This function first evaluate the memory pointer value. Override 'mem_read' to modify the effective memory accesses """ ptr = self.eval_expr_visitor(expr.ptr, **kwargs) mem = ExprMem(ptr, expr.size) ret = self.mem_read(mem) return ret
def test_lwcpi(self): """Test LWCPI execution""" # LWCPI CRn[0-15],(Rm+) exec_instruction( "LWCPI C1, (R2+)", [(ExprId("R2", 32), ExprInt(0x11, 32)), (ExprMem(ExprInt(0x10, 32), 32), ExprInt(0xABCD, 32))], [(ExprId("C1", 32), ExprInt(0xABCD, 32)), (ExprId("R2", 32), ExprInt(0x15, 32))])
def propagate(self, ssa, head): defuse = SSADefUse.from_ssa(ssa) to_replace = {} node_to_reg = {} for node in defuse.nodes(): lbl, index, reg = node src = defuse.get_node_target(node) if expr_has_call(src): continue if src.is_op('Phi'): continue if reg.is_mem(): continue to_replace[reg] = src node_to_reg[node] = reg modified = False for node, reg in node_to_reg.iteritems(): for successor in defuse.successors(node): if not self.propagation_allowed(ssa, to_replace, node, successor): continue loc_a, index_a, reg_a = node loc_b, index_b, reg_b = successor block = ssa.graph.blocks[loc_b] replace = {reg_a: to_replace[reg_a]} # Replace assignblks = list(block) assignblk = block[index_b] out = {} for dst, src in assignblk.iteritems(): if src.is_op('Phi'): out[dst] = src continue if src.is_mem(): ptr = src.ptr ptr = ptr.replace_expr(replace) new_src = ExprMem(ptr, src.size) else: new_src = src.replace_expr(replace) if dst.is_id(): new_dst = dst elif dst.is_mem(): ptr = dst.ptr ptr = ptr.replace_expr(replace) new_dst = ExprMem(ptr, dst.size) else: new_dst = dst.replace_expr(replace) if not (new_dst.is_id() or new_dst.is_mem()): new_dst = dst if src != new_src or dst != new_dst: modified = True out[new_dst] = new_src out = AssignBlock(out, assignblk.instr) assignblks[index_b] = out new_block = IRBlock(block.loc_key, assignblks) ssa.graph.blocks[block.loc_key] = new_block return modified