def eval(self, mop: mop_t, environment: MicroCodeEnvironment) -> Union[None, int]: if mop.t == mop_n: return mop.nnn.value elif mop.t in [mop_r, mop_S]: return environment.lookup(mop) elif mop.t == mop_d: return self._eval_instruction(mop.d, environment) elif mop.t == mop_a: if mop.a.t == mop_v: emulator_log.debug("Reading a mop_a '{0}' -> {1:x}".format( format_mop_t(mop), mop.a.g)) return mop.a.g elif mop.a.t == mop_S: emulator_log.debug("Reading a mop_a '{0}' -> {1:x}".format( format_mop_t(mop), mop.a.s.off)) return mop.a.s.off raise UnresolvedMopException( "Calling get_cst with unsupported mop type {0} - {1}: '{2}'". format(mop.t, mop.a.t, format_mop_t(mop))) elif mop.t == mop_v: mem_seg = getseg(mop.g) seg_perm = mem_seg.perm if (seg_perm & SEGPERM_WRITE) != 0: emulator_log.debug("Reading a (writable) mop_v {0}".format( format_mop_t(mop))) return environment.lookup(mop) else: memory_value = get_qword(mop.g) emulator_log.debug( "Reading a mop_v {0:x} (non writable -> return {1:x})". format(mop.g, memory_value)) return mop.g raise EmulationException("Unsupported mop type '{0}': '{1}'".format( mop_type_to_string(mop.t), format_mop_t(mop)))
def _eval_load(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]: res_mask = AND_TABLE[ins.d.size] if ins.opcode == m_ldx: load_address = self.eval(ins.r, environment) formatted_seg_register = format_mop_t(ins.l) if formatted_seg_register == "ss.2": stack_mop = mop_t() stack_mop.erase() stack_mop._make_stkvar(environment.cur_blk.mba, load_address) emulator_log.debug("Searching for stack mop {0}".format( format_mop_t(stack_mop))) stack_mop_value = environment.lookup(stack_mop) emulator_log.debug(" stack mop {0} value : {1}".format( format_mop_t(stack_mop), stack_mop_value)) return stack_mop_value & res_mask else: mem_seg = getseg(load_address) seg_perm = mem_seg.perm if (seg_perm & SEGPERM_WRITE) != 0: raise WritableMemoryReadException( "ldx {0:x} (writable -> return None)".format( load_address)) else: memory_value = get_qword(load_address) emulator_log.debug( "ldx {0:x} (non writable -> return {1:x})".format( load_address, memory_value & res_mask)) return memory_value & res_mask
def eval_mop(self, mop: mop_t, environment: Union[None, MicroCodeEnvironment] = None, raise_exception: bool = False) -> Union[None, int]: try: if environment is None: environment = self.global_environment res = self.eval(mop, environment) return res except EmulationException as e: emulator_log.warning( "Can't get constant mop value: '{0}': {1}".format( format_mop_t(mop), e)) if raise_exception: raise e else: return None except Exception as e: emulator_log.error( "Unexpected exception while computing constant mop value: '{0}': {1}" .format(format_mop_t(mop), e)) if raise_exception: raise e else: return None
def visit_mop(self, op: mop_t, op_type: int, is_target: bool): if is_target: append_mop_if_not_in_list(op, self.target_mops) else: # TODO whatever the case, in the end we will always return 0. May be this code can be better optimized. # TODO handle other special case (e.g. ldx ins, ...) if op.t == mop_S: append_mop_if_not_in_list(op, self.unresolved_ins_mops) elif op.t == mop_r: append_mop_if_not_in_list(op, self.unresolved_ins_mops) elif op.t == mop_v: append_mop_if_not_in_list(op, self.memory_unresolved_ins_mops) elif op.t == mop_a: if op.a.t == mop_v: return 0 elif op.a.t == mop_S: return 0 helper_logger.warning( "Calling visit_mop with unsupported mop type {0} - {1}: '{2}'" .format(mop_type_to_string(op.t), mop_type_to_string(op.a.t), format_mop_t(op))) return 0 elif op.t == mop_n: return 0 elif op.t == mop_d: return 0 elif op.t == mop_h: return 0 elif op.t == mop_b: return 0 else: helper_logger.warning( "Calling visit_mop with unsupported mop type {0}: '{1}'". format(mop_type_to_string(op.t), format_mop_t(op))) return 0
def check_if_possible_pattern(self, test_ast): patterns = get_possible_patterns(test_ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64) for pattern in patterns: leaf_info_list, cst_leaf_values, opcodes = pattern.get_information( ) leaf_nb_use = [ leaf_info.number_of_use for leaf_info in leaf_info_list ] if not (self.min_nb_var <= len(leaf_info_list) <= self.max_nb_var): continue if not (self.min_nb_diff_opcodes <= len(set(opcodes)) <= self.max_nb_diff_opcodes): continue if not (min(leaf_nb_use) >= 2): continue ins = pattern.mop.d self.log_info("IR: 0x{0:x} - {1}".format(ins.ea, format_minsn_t(ins))) for leaf_info in leaf_info_list: self.log_info(" {0} -> {1}".format( leaf_info.ast, format_mop_t(leaf_info.ast.mop))) self.log_info("Pattern: {0}".format(pattern)) self.log_info("AstNode: {0}\n".format(pattern.get_pattern())) return True return False
def print_info(self, detailed_info=False): formatted_mop_searched_list = [ format_mop_t(x) for x in self.searched_mop_list ] tmp = ", ".join([ "{0}={1}".format(formatted_mop, self.get_mop_constant_value(mop)) for formatted_mop, mop in zip(formatted_mop_searched_list, self.searched_mop_list) ]) logger.info("MopHistory: resolved={0}, path={1}, mops={2}".format( self.is_resolved(), self.block_serial_path, tmp)) if detailed_info: str_mop_list = "['" + "', '".join( formatted_mop_searched_list) + "']" if len(self.block_path) == 0: logger.info( "MopHistory for {0} => nothing".format(str_mop_list)) return end_blk = self.block_path[-1] end_ins = end_blk.tail if self.history[-1].ins_list: end_ins = self.history[-1].ins_list[-1] if end_ins: logger.info("MopHistory for {0} {1}.{2}".format( str_mop_list, end_blk.serial, format_minsn_t(end_ins))) else: logger.info("MopHistory for '{0}' {1}.tail".format( str_mop_list, end_blk.serial)) logger.info(" path {0}".format(self.block_serial_path)) for blk_info in self.history: for blk_ins in blk_info.ins_list: logger.info(" {0}.{1}".format(blk_info.blk.serial, format_minsn_t(blk_ins)))
def check_candidate(self, candidate): # IDA does not do constant propagation for pattern such as: # mov #0x65A4.2, r6.2 # mov #0x210F.2, r6^2.2 # jz r0.4, r6.4 # Thus, we try to detect mov to r6^2 and replace by (or #0x210F0000.4, r6.4 & 0x0000ffff.4, r6.4 # By doing that, IDA constant propagation will work again. if candidate.dst_mop.t != mop_r: return False dst_reg_name = format_mop_t(candidate.dst_mop) if dst_reg_name is None: return False if "^2" in dst_reg_name: if candidate["c_0"].mop.size != 2: return False candidate.add_constant_leaf("new_c_0", candidate["c_0"].value << 16, 4) candidate.add_constant_leaf("mask", 0xffff, 4) new_dst_reg = mop_t() new_dst_reg.make_reg(candidate.dst_mop.r - 2, 4) candidate.add_leaf("new_reg", new_dst_reg) candidate.dst_mop = new_dst_reg return True else: return False
def assign(self, mop: mop_t, value: int, auto_define=True) -> int: if mop.t == mop_r: return self._lookup_mop(mop, self.mop_r_record, value, auto_define) elif mop.t == mop_S: return self._lookup_mop(mop, self.mop_S_record, value, auto_define) raise EmulationException( "Assigning an unsupported mop type '{0}': '{1}'".format( mop_type_to_string(mop.t), format_mop_t(mop)))
def get_segment_register_indexes(mop_list: List[mop_t]) -> List[int]: # This is a very dirty and probably buggy segment_register_indexes = [] for i, mop in enumerate(mop_list): if mop.t == mop_r: formatted_mop = format_mop_t(mop) if formatted_mop in ["ds.2", "cs.2", "es.2", "ss.2"]: segment_register_indexes.append(i) return segment_register_indexes
def define(self, mop: mblock_t, value: int) -> int: if mop.t == mop_r: self.mop_r_record[mop] = value return value elif mop.t == mop_S: self.mop_S_record[mop] = value return value raise EmulationException( "Defining an unsupported mop type '{0}': '{1}'".format( mop_type_to_string(mop.t), format_mop_t(mop)))
def update_history(self, blk: mblock_t, ins_def: minsn_t) -> bool: logger.debug("Updating history with {0}.{1}".format( blk.serial, format_minsn_t(ins_def))) self.history.insert_ins_in_block(blk, ins_def, before=True) if ins_def.opcode == m_call: self.call_detected = True return False ins_mop_info = InstructionDefUseCollector() ins_def.for_all_ops(ins_mop_info) for target_mop in ins_mop_info.target_mops: resolved_mop_index = get_mop_index(target_mop, self._unresolved_mops) if resolved_mop_index != -1: logger.debug("Removing {0} from unresolved mop".format( format_mop_t(target_mop))) self._unresolved_mops.pop(resolved_mop_index) cleaned_unresolved_ins_mops = remove_segment_registers( ins_mop_info.unresolved_ins_mops) for ins_def_mop in cleaned_unresolved_ins_mops: ins_def_mop_index = get_mop_index(ins_def_mop, self._unresolved_mops) if ins_def_mop_index == -1: logger.debug("Adding {0} in unresolved mop".format( format_mop_t(ins_def_mop))) self._unresolved_mops.append(ins_def_mop) for target_mop in ins_mop_info.target_mops: resolved_mop_index = get_mop_index(target_mop, self._memory_unresolved_mops) if resolved_mop_index != -1: logger.debug("Removing {0} from memory unresolved mop".format( format_mop_t(target_mop))) self._memory_unresolved_mops.pop(resolved_mop_index) for ins_def_mem_mop in ins_mop_info.memory_unresolved_ins_mops: ins_def_mop_index = get_mop_index(ins_def_mem_mop, self._memory_unresolved_mops) if ins_def_mop_index == -1: logger.debug("Adding {0} in memory unresolved mop".format( format_mop_t(ins_def_mem_mop))) self._memory_unresolved_mops.append(ins_def_mem_mop) return True
def __str__(self): try: if self.is_constant(): return "{0}".format(self.mop.nnn.value) if self.z3_var_name is not None: return self.z3_var_name if self.ast_index is not None: return "x_{0}".format(self.ast_index) if self.mop is not None: return format_mop_t(self.mop) return self.name except RuntimeError as e: logger.info( "Error while calling __str__ on AstLeaf: {0}".format(e)) return "Error_AstLeaf"
def get_standard_and_memory_mop_lists( mop_in: mop_t) -> Tuple[List[mop_t], List[mop_t]]: if mop_in.t in [mop_r, mop_S]: return [mop_in], [] elif mop_in.t == mop_v: return [], [mop_in] elif mop_in.t == mop_d: ins_mop_info = InstructionDefUseCollector() mop_in.d.for_all_ops(ins_mop_info) return remove_segment_registers( ins_mop_info.unresolved_ins_mops ), ins_mop_info.memory_unresolved_ins_mops else: logger.warning( "Calling get_standard_and_memory_mop_lists with unsupported mop type {0}: '{1}'" .format(mop_in.t, format_mop_t(mop_in))) return [], []
def _execute_microcode(self) -> bool: if not self._is_dirty: return True formatted_mop_searched_list = "['" + "', '".join( [format_mop_t(x) for x in self.searched_mop_list]) + "']" logger.debug("Computing: {0} for path {1}".format( formatted_mop_searched_list, self.block_serial_path)) self._mc_current_environment = self._mc_initial_environment.get_copy() for blk_info in self.history: for blk_ins in blk_info.ins_list: logger.debug("Executing: {0}.{1}".format( blk_info.blk.serial, format_minsn_t(blk_ins))) if not self._mc_interpreter.eval_instruction( blk_info.blk, blk_ins, self._mc_current_environment): self._is_dirty = False return False self._is_dirty = False return True
def _lookup_mop(self, searched_mop: mop_t, mop_value_dict: Dict[mop_t, int], new_mop_value: Union[None, int] = None, auto_define=True, raise_exception=True) -> int: for known_mop, mop_value in mop_value_dict.items(): if equal_mops_ignore_size(searched_mop, known_mop): if new_mop_value is not None: mop_value_dict[searched_mop] = new_mop_value return new_mop_value return mop_value if (new_mop_value is not None) and auto_define: self.define(searched_mop, new_mop_value) return new_mop_value if raise_exception: raise EmulationException("Variable '{0}' is not defined".format( format_mop_t(searched_mop))) else: return None
def mop_to_ast_internal( mop: mop_t, ast_list: List[Union[AstNode, AstLeaf]]) -> Union[None, AstNode, AstLeaf]: if mop is None: return None if mop.t != mop_d or (mop.d.opcode not in MBA_RELATED_OPCODES): tree = AstLeaf(format_mop_t(mop)) tree.mop = mop dest_size = mop.size if mop.t != mop_d else mop.d.d.size tree.dest_size = dest_size else: left_ast = mop_to_ast_internal(mop.d.l, ast_list) right_ast = mop_to_ast_internal(mop.d.r, ast_list) dst_ast = mop_to_ast_internal(mop.d.d, ast_list) tree = AstNode(mop.d.opcode, left_ast, right_ast, dst_ast) tree.mop = mop tree.dest_size = mop.d.d.size tree.ea = mop.d.ea check_and_add_to_list(tree, ast_list) return tree
def __str__(self): return "{0} used {1} times: {2}".format(self.ast, self.number_of_use, format_mop_t(self.ast.mop))
def _get_blk_serial(mop: mop_t) -> int: if mop.t == mop_b: return mop.b raise EmulationException( "Get block serial with an unsupported mop type '{0}': '{1}'". format(mop_type_to_string(mop.t), format_mop_t(mop)))
def search_backward(self, blk: mblock_t, ins: minsn_t, avoid_list=None, must_use_pred=None, stop_at_first_duplication=False) -> List[MopHistory]: logger.debug("Searching backward (reg): {0}".format( [format_mop_t(x) for x in self._unresolved_mops])) logger.debug("Searching backward (mem): {0}".format( [format_mop_t(x) for x in self._memory_unresolved_mops])) logger.debug("Searching backward (cst): {0}".format([ "{0}: {1:x}".format(format_mop_t(x[0]), x[1]) for x in self.constant_mops ])) self.mba = blk.mba self.avoid_list = avoid_list if avoid_list else [] blk_with_multiple_pred = self.search_until_multiple_predecessor( blk, ins) if self.is_resolved(): logger.debug("MopTracker is resolved: {0}".format( self.history.block_serial_path)) self.history.unresolved_mop_list = [ x for x in self._unresolved_mops ] return [self.history] elif blk_with_multiple_pred is None: logger.debug( "MopTracker unresolved: (blk_with_multiple_pred): {0}".format( self.history.block_serial_path)) self.history.unresolved_mop_list = [ x for x in self._unresolved_mops ] return [self.history] elif self.max_nb_block != -1 and len( self.history.block_serial_path) > self.max_nb_block: logger.debug("MopTracker unresolved: (max_nb_block): {0}".format( self.history.block_serial_path)) self.history.unresolved_mop_list = [ x for x in self._unresolved_mops ] return [self.history] elif self.max_path != -1 and cur_mop_tracker_nb_path > self.max_path: logger.debug("MopTracker unresolved: (max_path: {0}".format( cur_mop_tracker_nb_path)) self.history.unresolved_mop_list = [ x for x in self._unresolved_mops ] return [self.history] elif self.call_detected: logger.debug("MopTracker unresolved: (call): {0}".format( self.history.block_serial_path)) self.history.unresolved_mop_list = [ x for x in self._unresolved_mops ] return [self.history] if stop_at_first_duplication: self.history.unresolved_mop_list = [ x for x in self._unresolved_mops ] return [self.history] logger.debug( "MopTracker creating child because multiple pred: {0}".format( self.history.block_serial_path)) possible_histories = [] if must_use_pred is not None and must_use_pred.serial in blk_with_multiple_pred.predset: new_tracker = self.get_copy() possible_histories += new_tracker.search_backward( must_use_pred, None, self.avoid_list, must_use_pred) else: for blk_pred_serial in blk_with_multiple_pred.predset: new_tracker = self.get_copy() possible_histories += new_tracker.search_backward( self.mba.get_mblock(blk_pred_serial), None, self.avoid_list, must_use_pred) return possible_histories