def _translate_bsh(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of a BSH instruction. """ assert oprnd1.size and oprnd2.size and oprnd3.size assert oprnd1.size == oprnd2.size op1_var = self._translate_src_oprnd(oprnd1) op2_var = self._translate_src_oprnd(oprnd2) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) if oprnd3.size > oprnd1.size: op1_var_zx = smtfunction.zero_extend(op1_var, oprnd3.size) op2_var_zx = smtfunction.zero_extend(op2_var, oprnd3.size) op2_var_neg_sx = smtfunction.sign_extend(-op2_var, oprnd3.size) shr = smtfunction.extract(op1_var_zx >> op2_var_neg_sx, 0, op3_var.size) shl = smtfunction.extract(op1_var_zx << op2_var_zx, 0, op3_var.size) elif oprnd3.size < oprnd1.size: shr = smtfunction.extract(op1_var >> -op2_var, 0, op3_var.size) shl = smtfunction.extract(op1_var << op2_var, 0, op3_var.size) else: shr = op1_var >> -op2_var shl = op1_var << op2_var result = smtfunction.ite(oprnd3.size, op2_var >= 0, shl, shr) return [op3_var == result] + op3_var_constrs
def test_extract(self): x = BitVec(32, "x") x0 = extract(x, 0, 8) x1 = extract(x, 8, 8) x2 = extract(x, 16, 8) x3 = extract(x, 24, 8) self.assertEqual(x0.value, "((_ extract 7 0) x)") self.assertEqual(x1.value, "((_ extract 15 8) x)") self.assertEqual(x2.value, "((_ extract 23 16) x)") self.assertEqual(x3.value, "((_ extract 31 24) x)")
def _translate_dst_register_oprnd(self, operand): """Translate destination register operand to SMT expr. """ reg_info = self._arch_alias_mapper.get(operand.name, None) parent_reg_constrs = [] if reg_info: var_base_name, offset = reg_info var_name_old = self._get_var_name(var_base_name, fresh=False) var_name_new = self._get_var_name(var_base_name, fresh=True) var_size = self._arch_regs_size[var_base_name] ret_val_old = self.make_bitvec(var_size, var_name_old) ret_val_new = self.make_bitvec(var_size, var_name_new) ret_val = smtfunction.extract(ret_val_new, offset, operand.size) if 0 < offset < var_size - 1: lower_expr_1 = smtfunction.extract(ret_val_new, 0, offset) lower_expr_2 = smtfunction.extract(ret_val_old, 0, offset) parent_reg_constrs += [lower_expr_1 == lower_expr_2] upper_expr_1 = smtfunction.extract( ret_val_new, offset + operand.size, var_size - offset - operand.size) upper_expr_2 = smtfunction.extract( ret_val_old, offset + operand.size, var_size - offset - operand.size) parent_reg_constrs += [upper_expr_1 == upper_expr_2] elif offset == 0: upper_expr_1 = smtfunction.extract( ret_val_new, offset + operand.size, var_size - offset - operand.size) upper_expr_2 = smtfunction.extract( ret_val_old, offset + operand.size, var_size - offset - operand.size) parent_reg_constrs += [upper_expr_1 == upper_expr_2] elif offset == var_size - 1: lower_expr_1 = smtfunction.extract(ret_val_new, 0, offset) lower_expr_2 = smtfunction.extract(ret_val_old, 0, offset) parent_reg_constrs += [lower_expr_1 == lower_expr_2] else: var_name_new = self._get_var_name(operand.name, fresh=True) ret_val = self.make_bitvec(operand.size, var_name_new) return ret_val, parent_reg_constrs
def _get_constrs_arithmetic_load(self, gadget): """Generate constraints for the ArithmeticLoad gadget: dst_reg <- dst_reg OP mem[src_reg + offset] """ op = self._arithmetic_ops[gadget.operation] dst = self.analyzer.get_register_expr(gadget.destination[0].name, mode="post") size = gadget.destination[0].size if isinstance(gadget.sources[1], ReilRegisterOperand): base_addr = self.analyzer.get_register_expr(gadget.sources[1].name, mode="pre") offset = gadget.sources[2].immediate addr = base_addr + offset else: addr = gadget.sources[2].immediate src1 = self.analyzer.get_register_expr(gadget.sources[0].name, mode="pre") src2 = self.analyzer.get_memory_expr(addr, size / 8) result = op(src1, src2) constrs = [] for i in reversed(xrange(0, size, 8)): bytes_exprs_1 = smtfunction.extract(result, i, 8) bytes_exprs_2 = smtfunction.extract(dst, i, 8) constrs += [bytes_exprs_1 != bytes_exprs_2] # Check all non-modified registers don't change. constrs_mod = [] for name in self._arch_info.registers_gp_base: if name not in [r.name for r in gadget.modified_registers]: var_initial = self.analyzer.get_register_expr(name, mode="pre") var_final = self.analyzer.get_register_expr(name, mode="post") constrs_mod += [var_initial != var_final] if constrs_mod: constrs_mod = [ reduce(lambda c, acc: acc | c, constrs_mod[1:], constrs_mod[0]) ] return constrs + constrs_mod
def _get_constrs_arithmetic_load(self, gadget): """Generate constraints for the ArithmeticLoad gadgets: dst_reg <- dst_reg OP mem[src_reg + offset] """ op = self._arithmetic_ops[gadget.operation] dst = self.analyzer.get_register_expr(gadget.destination[0].name, mode="post") size = gadget.destination[0].size if isinstance(gadget.sources[1], ReilRegisterOperand): base_addr = self.analyzer.get_register_expr(gadget.sources[1].name, mode="pre") offset = gadget.sources[2].immediate addr = base_addr + offset else: addr = gadget.sources[2].immediate src1 = self.analyzer.get_register_expr(gadget.sources[0].name, mode="pre") src2 = self.analyzer.get_memory_expr(addr, size // 8) result = op(src1, src2) constrs = [] for i in reversed(range(0, size, 8)): bytes_exprs_1 = smtfunction.extract(result, i, 8) bytes_exprs_2 = smtfunction.extract(dst, i, 8) constrs += [bytes_exprs_1 != bytes_exprs_2] # Check all non-modified registers don't change. constrs_mod = [] for name in self._arch_info.registers_gp_base: if name not in [r.name for r in gadget.modified_registers]: var_initial = self.analyzer.get_register_expr(name, mode="pre") var_final = self.analyzer.get_register_expr(name, mode="post") constrs_mod += [var_initial != var_final] if constrs_mod: constrs_mod = [reduce(lambda c, acc: acc | c, constrs_mod[1:], constrs_mod[0])] return constrs + constrs_mod
def _translate_dst_register_oprnd(self, operand): """Translate destination register operand to SMT expr. """ reg_info = self._arch_alias_mapper.get(operand.name, None) parent_reg_constrs = [] if reg_info: var_base_name, offset = reg_info var_name_old = self._get_var_name(var_base_name, fresh=False) var_name_new = self._get_var_name(var_base_name, fresh=True) var_size = self._arch_regs_size[var_base_name] ret_val_old = self.make_bitvec(var_size, var_name_old) ret_val_new = self.make_bitvec(var_size, var_name_new) ret_val = smtfunction.extract(ret_val_new, offset, operand.size) if 0 < offset < var_size - 1: lower_expr_1 = smtfunction.extract(ret_val_new, 0, offset) lower_expr_2 = smtfunction.extract(ret_val_old, 0, offset) parent_reg_constrs += [lower_expr_1 == lower_expr_2] upper_expr_1 = smtfunction.extract(ret_val_new, offset + operand.size, var_size - offset - operand.size) upper_expr_2 = smtfunction.extract(ret_val_old, offset + operand.size, var_size - offset - operand.size) parent_reg_constrs += [upper_expr_1 == upper_expr_2] elif offset == 0: upper_expr_1 = smtfunction.extract(ret_val_new, offset + operand.size, var_size - offset - operand.size) upper_expr_2 = smtfunction.extract(ret_val_old, offset + operand.size, var_size - offset - operand.size) parent_reg_constrs += [upper_expr_1 == upper_expr_2] elif offset == var_size-1: lower_expr_1 = smtfunction.extract(ret_val_new, 0, offset) lower_expr_2 = smtfunction.extract(ret_val_old, 0, offset) parent_reg_constrs += [lower_expr_1 == lower_expr_2] else: var_name_new = self._get_var_name(operand.name, fresh=True) ret_val = self.make_bitvec(operand.size, var_name_new) return ret_val, parent_reg_constrs
def _translate_ldm(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of a LDM instruction. """ assert oprnd1.size and oprnd3.size assert oprnd1.size == self._address_size op1_var = self._translate_src_oprnd(oprnd1) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) exprs = [] for i in reversed(range(0, oprnd3.size, 8)): exprs += [self._mem_curr[op1_var + i // 8] == smtfunction.extract(op3_var, i, 8)] return exprs + op3_var_constrs
def _translate_str(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of a STR instruction. """ assert oprnd1.size and oprnd3.size op1_var = self._translate_src_oprnd(oprnd1) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) if oprnd3.size > oprnd1.size: result = smtfunction.zero_extend(op1_var, op3_var.size) elif oprnd3.size < oprnd1.size: result = smtfunction.extract(op1_var, 0, op3_var.size) else: result = op1_var return [op3_var == result] + op3_var_constrs
def _translate_ldm(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of a LDM instruction. """ assert oprnd1.size and oprnd3.size assert oprnd1.size == self._address_size op1_var = self._translate_src_oprnd(oprnd1) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) exprs = [] for i in reversed(xrange(0, oprnd3.size, 8)): exprs += [ self._mem_curr[op1_var + i / 8] == smtfunction.extract( op3_var, i, 8) ] return exprs + op3_var_constrs
def _translate_src_register_oprnd(self, operand): """Translate source register operand to SMT expr. """ reg_info = self._arch_alias_mapper.get(operand.name, None) if reg_info: var_base_name, offset = reg_info var_size = self._arch_regs_size[var_base_name] else: var_base_name = operand.name var_size = operand.size var_name = self._get_var_name(var_base_name) ret_val = self.make_bitvec(var_size, var_name) if reg_info: ret_val = smtfunction.extract(ret_val, offset, operand.size) return ret_val
def get_register_expr(self, register_name, mode="post"): """Return a smt bit vector that represents an architectural (native) register. """ reg_info = self._arch_info.alias_mapper.get(register_name, None) if reg_info: var_base_name, offset = reg_info else: var_base_name = register_name var_name = self._get_var_name(var_base_name, mode) var_size = self._arch_info.registers_size[var_base_name] ret_val = self._translator.make_bitvec(var_size, var_name) if reg_info: ret_val = smtfunction.extract(ret_val, offset, self._arch_info.registers_size[register_name]) return ret_val
def _get_constrs_store_memory(self, gadget): """Generate constraints for the StoreMemory gadgets: mem[dst_reg + offset] <- src_reg """ if isinstance(gadget.destination[0], ReilRegisterOperand): base_addr = self.analyzer.get_register_expr( gadget.destination[0].name, mode="pre") offset = gadget.destination[1].immediate addr = base_addr + offset else: addr = gadget.destination[1].immediate src = self.analyzer.get_register_expr(gadget.sources[0].name, mode="pre") size = gadget.sources[0].size constrs = [] for i in reversed(range(0, size, 8)): bytes_exprs_1 = self.analyzer.get_memory_expr( addr + i // 8, 8 // 8) bytes_exprs_2 = smtfunction.extract(src, i, 8) constrs += [bytes_exprs_1 != bytes_exprs_2] # Check all non-modified registers don't change. constrs_mod = [] for name in self._arch_info.registers_gp_base: if name not in [r.name for r in gadget.modified_registers]: var_initial = self.analyzer.get_register_expr(name, mode="pre") var_final = self.analyzer.get_register_expr(name, mode="post") constrs_mod += [var_initial != var_final] if constrs_mod: constrs_mod = [ reduce(lambda c, acc: acc | c, constrs_mod[1:], constrs_mod[0]) ] return constrs + constrs_mod
def _translate_smul(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of an MUL instruction. """ assert oprnd1.size and oprnd2.size and oprnd3.size assert oprnd1.size == oprnd2.size op1_var = self._translate_src_oprnd(oprnd1) op2_var = self._translate_src_oprnd(oprnd2) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) if oprnd3.size > oprnd1.size: op1_var_sx = smtfunction.sign_extend(op1_var, oprnd3.size) op2_var_sx = smtfunction.sign_extend(op2_var, oprnd3.size) result = op1_var_sx * op2_var_sx elif oprnd3.size < oprnd1.size: result = smtfunction.extract(op1_var * op2_var, 0, oprnd3.size) else: result = op1_var * op2_var return [op3_var == result] + op3_var_constrs
def _translate_stm(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of a STM instruction. """ assert oprnd1.size and oprnd3.size assert oprnd3.size == self._address_size op1_var = self._translate_src_oprnd(oprnd1) op3_var = self._translate_src_oprnd(oprnd3) for i in range(0, oprnd1.size, 8): self._mem_curr[op3_var + i//8] = smtfunction.extract(op1_var, i, 8) # Memory versioning. self._mem_instance += 1 mem_old = self._mem_curr mem_new = self.make_array(self._address_size, "MEM_{}".format(self._mem_instance)) self._mem_curr = mem_new return [mem_new == mem_old]
def get_register_expr(self, register_name, mode="post"): """Return a smt bit vector that represents an architectural (native) register. """ reg_info = self._arch_info.alias_mapper.get(register_name, None) if reg_info: var_base_name, offset = reg_info else: var_base_name = register_name var_name = self._get_var_name(var_base_name, mode) var_size = self._arch_info.registers_size[var_base_name] ret_val = self._translator.make_bitvec(var_size, var_name) if reg_info: ret_val = smtfunction.extract( ret_val, offset, self._arch_info.registers_size[register_name]) return ret_val
def _translate_mod(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of an MOD instruction. """ assert oprnd1.size and oprnd2.size and oprnd3.size assert oprnd1.size == oprnd2.size op1_var = self._translate_src_oprnd(oprnd1) op2_var = self._translate_src_oprnd(oprnd2) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) if oprnd3.size > oprnd1.size: op1_var_zx = smtfunction.zero_extend(op1_var, oprnd3.size) op2_var_zx = smtfunction.zero_extend(op2_var, oprnd3.size) result = op1_var_zx.umod(op2_var_zx) elif oprnd3.size < oprnd1.size: result = smtfunction.extract(op1_var.umod(op2_var), 0, oprnd3.size) else: result = op1_var.umod(op2_var) return [op3_var == result] + op3_var_constrs
def _translate_div(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of an DIV instruction. """ assert oprnd1.size and oprnd2.size and oprnd3.size assert oprnd1.size == oprnd2.size op1_var = self._translate_src_oprnd(oprnd1) op2_var = self._translate_src_oprnd(oprnd2) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) if oprnd3.size > oprnd1.size: op1_var_zx = smtfunction.zero_extend(op1_var, oprnd3.size) op2_var_zx = smtfunction.zero_extend(op2_var, oprnd3.size) result = op1_var_zx.udiv(op2_var_zx) elif oprnd3.size < oprnd1.size: result = smtfunction.extract(op1_var.udiv(op2_var), 0, oprnd3.size) else: result = op1_var.udiv(op2_var) return [op3_var == result] + op3_var_constrs
def _translate_smod(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of an MOD instruction. """ assert oprnd1.size and oprnd2.size and oprnd3.size assert oprnd1.size == oprnd2.size op1_var = self._translate_src_oprnd(oprnd1) op2_var = self._translate_src_oprnd(oprnd2) op3_var, op3_var_constrs = self._translate_dst_oprnd(oprnd3) if oprnd3.size > oprnd1.size: op1_var_sx = smtfunction.sign_extend(op1_var, oprnd3.size) op2_var_sx = smtfunction.sign_extend(op2_var, oprnd3.size) result = op1_var_sx % op2_var_sx elif oprnd3.size < oprnd1.size: result = smtfunction.extract(op1_var % op2_var, 0, oprnd3.size) else: result = op1_var % op2_var return [op3_var == result] + op3_var_constrs
def _translate_stm(self, oprnd1, oprnd2, oprnd3): """Return a formula representation of a STM instruction. """ assert oprnd1.size and oprnd3.size assert oprnd3.size == self._address_size op1_var = self._translate_src_oprnd(oprnd1) op3_var = self._translate_src_oprnd(oprnd3) for i in xrange(0, oprnd1.size, 8): self._mem_curr[op3_var + i / 8] = smtfunction.extract( op1_var, i, 8) # Memory versioning. self._mem_instance += 1 mem_old = self._mem_curr mem_new = self.make_array(self._address_size, "MEM_{}".format(self._mem_instance)) self._mem_curr = mem_new return [mem_new == mem_old]
def _get_constrs_store_memory(self, gadget): """Generate constraints for the StoreMemory gadgets: mem[dst_reg + offset] <- src_reg """ if isinstance(gadget.destination[0], ReilRegisterOperand): base_addr = self.analyzer.get_register_expr(gadget.destination[0].name, mode="pre") offset = gadget.destination[1].immediate addr = base_addr + offset else: addr = gadget.destination[1].immediate src = self.analyzer.get_register_expr(gadget.sources[0].name, mode="pre") size = gadget.sources[0].size constrs = [] for i in reversed(range(0, size, 8)): bytes_exprs_1 = self.analyzer.get_memory_expr(addr + i // 8, 8 // 8) bytes_exprs_2 = smtfunction.extract(src, i, 8) constrs += [bytes_exprs_1 != bytes_exprs_2] # Check all non-modified registers don't change. constrs_mod = [] for name in self._arch_info.registers_gp_base: if name not in [r.name for r in gadget.modified_registers]: var_initial = self.analyzer.get_register_expr(name, mode="pre") var_final = self.analyzer.get_register_expr(name, mode="post") constrs_mod += [var_initial != var_final] if constrs_mod: constrs_mod = [reduce(lambda c, acc: acc | c, constrs_mod[1:], constrs_mod[0])] return constrs + constrs_mod