def optimize(self, blk: mblock_t, ins: minsn_t) -> bool: # optimizer_log.info("Trying to optimize {0}".format(format_minsn_t(ins))) for ins_optimizer in self.instruction_optimizers: self._last_optimizer_tried = ins_optimizer new_ins = ins_optimizer.get_optimized_instruction(blk, ins) if new_ins is not None: if not check_ins_mop_size_are_ok(new_ins): if check_ins_mop_size_are_ok(ins): main_logger.error( "Invalid optimized instruction: {0} (original was {1})" .format(format_minsn_t(new_ins), format_minsn_t(ins))) else: main_logger.error( "Invalid original instruction : {0} (original was {1})" .format(format_minsn_t(new_ins), format_minsn_t(ins))) else: ins.swap(new_ins) self.optimizer_usage_info[ins_optimizer.name] += 1 if self.generate_z3_code: try: log_z3_instructions(new_ins, ins) except KeyError: pass return True self.analyzer.analyze(blk, ins) return False
def eval_instruction(self, blk: mblock_t, ins: minsn_t, environment: Union[None, MicroCodeEnvironment] = None, raise_exception: bool = False) -> bool: try: if environment is None: environment = self.global_environment emulator_log.info( "Evaluating microcode instruction : '{0}'".format( format_minsn_t(ins))) if ins is None: return False self._eval_instruction_and_update_environment( blk, ins, environment) return True except EmulationException as e: emulator_log.warning( "Can't evaluate instruction: '{0}': {1}".format( format_minsn_t(ins), e)) if raise_exception: raise e except Exception as e: emulator_log.warning( "Error during evaluation of: '{0}': {1}".format( format_minsn_t(ins), e)) if raise_exception: raise e 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 get_optimized_instruction(self, blk: mblock_t, ins: minsn_t) -> Union[None, minsn_t]: if blk is not None: self.cur_maturity = blk.mba.maturity if self.cur_maturity not in self.maturities: return None tmp = minsn_to_ast(ins) if tmp is None: return None all_matchs = self.pattern_storage.get_matching_rule_pattern_info(tmp) for rule_pattern_info in all_matchs: try: new_ins = rule_pattern_info.rule.check_pattern_and_replace( rule_pattern_info.pattern, tmp) if new_ins is not None: self.rules_usage_info[rule_pattern_info.rule.name] += 1 optimizer_logger.info("Rule {0} matched:".format( rule_pattern_info.rule.name)) optimizer_logger.info(" orig: {0}".format( format_minsn_t(ins))) optimizer_logger.info(" new : {0}".format( format_minsn_t(new_ins))) return new_ins except RuntimeError as e: optimizer_logger.error( "Error during rule {0} for instruction {1}: {2}".format( rule_pattern_info.rule, format_minsn_t(ins), e)) return None
def func(self, blk: mblock_t, ins: minsn_t) -> bool: self.log_info_on_input(blk, ins) try: optimization_performed = self.optimize(blk, ins) if not optimization_performed: optimization_performed = ins.for_all_insns( self.instruction_visitor) if optimization_performed: ins.optimize_solo() if blk is not None: blk.mark_lists_dirty() blk.mba.verify(True) return optimization_performed except RuntimeError as e: optimizer_logger.error( "RuntimeError while optimizing ins {0} with {1}: {2}".format( format_minsn_t(ins), self._last_optimizer_tried, e)) except D810Exception as e: optimizer_logger.error( "D810Exception while optimizing ins {0} with {1}: {2}".format( format_minsn_t(ins), self._last_optimizer_tried, e)) return False
def fix_successor(self, fake_loop_block: mblock_t, pred: mblock_t, pred_comparison_values: List[int]) -> bool: if len(pred_comparison_values) == 0: return False jmp_ins = fake_loop_block.tail compared_value = jmp_ins.r.nnn.value jmp_taken = False jmp_not_taken = False dst_serial = None if jmp_ins.opcode == m_jz: jmp_taken = all([ possible_value == compared_value for possible_value in pred_comparison_values ]) jmp_not_taken = all([ possible_value != compared_value for possible_value in pred_comparison_values ]) elif jmp_ins.opcode == m_jnz: jmp_taken = all([ possible_value != compared_value for possible_value in pred_comparison_values ]) jmp_not_taken = all([ possible_value == compared_value for possible_value in pred_comparison_values ]) # TODO: handles other jumps cases if jmp_taken: unflat_logger.info( "It seems that '{0}' is always taken when coming from {1}: {2}" .format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values)) dst_serial = jmp_ins.d.b if jmp_not_taken: unflat_logger.info( "It seems that '{0}' is never taken when coming from {1}: {2}". format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values)) dst_serial = fake_loop_block.serial + 1 if dst_serial is None: unflat_logger.debug("Jump seems legit '{0}' from {1}: {2}".format( format_minsn_t(jmp_ins), pred.serial, pred_comparison_values)) return False dump_microcode_for_debug( self.mba, self.log_dir, "{0}_before_fake_jump".format(self.cur_maturity_pass)) unflat_logger.info( "Making pred {0} with value {1} goto {2} ({3})".format( pred.serial, pred_comparison_values, dst_serial, format_minsn_t(jmp_ins))) dump_microcode_for_debug( self.mba, self.log_dir, "{0}_after_fake_jump".format(self.cur_maturity_pass)) return change_1way_block_successor(pred, dst_serial)
def sort_predecessors(self, blk): # this function sorts the blk predecessors into three list: # - A list of predecessors where the jump is always taken # - A list of predecessors where the jump is never taken # - A list of predecessors where we don't know pred_jmp_always_taken = [] pred_jmp_never_taken = [] pred_jmp_unk = [] op_compared = mop_t(blk.tail.l) blk_preset_list = [x for x in blk.predset] for pred_serial in blk_preset_list: cmp_variable_tracker = MopTracker([op_compared], max_nb_block=100, max_path=1000) cmp_variable_tracker.reset() pred_blk = blk.mba.get_mblock(pred_serial) pred_histories = cmp_variable_tracker.search_backward( pred_blk, pred_blk.tail) pred_values = get_all_possibles_values(pred_histories, [op_compared]) pred_values = [x[0] for x in pred_values] unflat_logger.info( "Pred {0} has {1} possible path ({2} different cst): {3}". format(pred_blk.serial, len(pred_values), len(set(pred_values)), pred_values)) if None in pred_values: pred_jmp_unk.append(pred_blk) continue is_jmp_always_taken, is_jmp_never_taken = self.is_jump_taken( blk, pred_values) if is_jmp_always_taken and is_jmp_never_taken: # this should never happen unflat_logger.error( "It seems that I am stupid: '{0}' is always taken and not taken when coming from {1}: {2}" .format(format_minsn_t(blk.tail), pred_blk.serial, pred_values)) pred_jmp_unk.append(pred_blk) continue if is_jmp_always_taken: unflat_logger.info( "It seems that '{0}' is always taken when coming from {1}: {2}" .format(format_minsn_t(blk.tail), pred_blk.serial, pred_values)) pred_jmp_always_taken.append(pred_blk) if is_jmp_never_taken: unflat_logger.info( "It seems that '{0}' is never taken when coming from {1}: {2}" .format(format_minsn_t(blk.tail), pred_blk.serial, pred_values)) pred_jmp_never_taken.append(pred_blk) return pred_jmp_always_taken, pred_jmp_never_taken, pred_jmp_unk
def simplify(self, ins): if ins.opcode not in [m_add, m_sub]: return None self.formatted_ins = format_minsn_t(ins) self.add_non_cst_mop_list = [] self.add_cst_mop_list = [] self.sub_non_cst_mop_list = [] self.sub_cst_mop_list = [] self.add_mop(0, ins.l) if ins.opcode == m_add: self.add_mop(0, ins.r) else: self.add_mop(1, ins.r) self._is_instruction_simplified = False final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list = self.do_simplification( ) if not self._is_instruction_simplified: return None simplified_ins = self.create_new_chain(ins, final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list) return simplified_ins
def _eval_call(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]: # TODO: implement emulator_log.warning( "Evaluation of {0} not implemented: bypassing".format( format_minsn_t(ins))) return None
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 get_optimized_instruction(self, blk: mblock_t, ins: minsn_t): if blk is not None: self.cur_maturity = blk.mba.maturity # if self.cur_maturity not in self.maturities: # return None for rule in self.rules: if self.cur_maturity not in rule.maturities: continue try: new_ins = rule.check_and_replace(blk, ins) if new_ins is not None: self.rules_usage_info[rule.name] += 1 optimizer_logger.info("Rule {0} matched:".format(rule.name)) optimizer_logger.info(" orig: {0}".format(format_minsn_t(ins))) optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins))) return new_ins except RuntimeError as e: optimizer_logger.error("Runtime error during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e)) except D810Exception as e: optimizer_logger.error("D810Exception during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e)) return None
def _eval_control_flow_instruction( self, ins: minsn_t, environment: MicroCodeEnvironment) -> bool: if ins.opcode not in CONTROL_FLOW_OPCODES: return False cur_blk = environment.cur_blk if cur_blk is None: raise EmulationException( "Can't evaluate control flow instruction with null block: '{0}'" .format(format_minsn_t(ins))) next_blk_serial = self._eval_conditional_jump(ins, environment) if next_blk_serial is not None: next_blk = cur_blk.mba.get_mblock(next_blk_serial) next_ins = next_blk.head environment.set_next_flow(next_blk, next_ins) return True if ins.opcode == m_goto: next_blk_serial = self._get_blk_serial(ins.l) elif ins.opcode == m_jtbl: left_value = self.eval(ins.l, environment) cases = ins.r.c # Initialize to default case next_blk_serial = [x for x in cases.targets][-1] for possible_values, target_block_serial in zip( cases.values, cases.targets): for test_value in possible_values: if left_value == test_value: next_blk_serial = target_block_serial break elif ins.opcode == m_ijmp: ijmp_dest_ea = self.eval(ins.d, environment) dest_block_serials = get_block_serials_by_address( environment.cur_blk.mba, ijmp_dest_ea) if len(dest_block_serials) == 0: raise EmulationIndirectJumpException( "No blocks found at address {0:x}".format(ijmp_dest_ea), ijmp_dest_ea, dest_block_serials) if len(dest_block_serials) > 1: raise EmulationIndirectJumpException( "Multiple blocks at address {0:x}: {1}".format( ijmp_dest_ea, dest_block_serials), ijmp_dest_ea, dest_block_serials) next_blk_serial = dest_block_serials[0] if next_blk_serial is None: return False next_blk = cur_blk.mba.get_mblock(next_blk_serial) next_ins = next_blk.head environment.set_next_flow(next_blk, next_ins) return True
def log_z3_instructions(original_ins: minsn_t, new_ins: minsn_t): if not Z3_INSTALLED: raise D810Z3Exception("Z3 is not installed") orig_mba_tree = minsn_to_ast(original_ins) new_mba_tree = minsn_to_ast(new_ins) if orig_mba_tree is None or new_mba_tree is None: return None orig_leaf_list = orig_mba_tree.get_leaf_list() new_leaf_list = new_mba_tree.get_leaf_list() var_def_list = rename_leafs(orig_leaf_list + new_leaf_list) z3_file_logger.info("print('Testing: {0} == {1}')".format( format_minsn_t(original_ins), format_minsn_t(new_ins))) for var_def in var_def_list: z3_file_logger.info("{0}".format(var_def)) removed_xdu = "{0}".format(orig_mba_tree).replace("xdu", "") z3_file_logger.info("original_expr = {0}".format(removed_xdu)) removed_xdu = "{0}".format(new_mba_tree).replace("xdu", "") z3_file_logger.info("new_expr = {0}".format(removed_xdu)) z3_file_logger.info("prove(original_expr == new_expr)\n")
def analyze_instruction(self, blk, ins): if self.cur_maturity not in self.maturities: return None formatted_ins = str(format_minsn_t(ins)) if formatted_ins in self.cur_ins_guessed: return False tmp = minsn_to_ast(ins) if tmp is None: return False is_good_candidate = self.check_if_possible_pattern(tmp) if is_good_candidate: self.cur_ins_guessed[self.cur_index] = formatted_ins self.cur_index = (self.cur_index + 1) % self.max_index return is_good_candidate
def optimize(self, blk: mblock_t) -> bool: if not is_conditional_jump(blk): return False left_ast = mop_to_ast(blk.tail.l) right_ast = mop_to_ast(blk.tail.r) for rule in self.rules: try: new_ins = rule.check_pattern_and_replace(blk, blk.tail, left_ast, right_ast) if new_ins: optimizer_logger.info("Rule {0} matched:".format(rule.name)) optimizer_logger.info(" orig: {0}".format(format_minsn_t(blk.tail))) optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins))) if new_ins.opcode == m_goto: make_2way_block_goto(blk, new_ins.d.b) else: change_2way_block_conditional_successor(blk, new_ins.d.b) blk.make_nop(blk.tail) blk.insert_into_block(new_ins, blk.tail) return True except RuntimeError as e: optimizer_logger.error("Error during rule {0} for instruction {1}: {2}" .format(rule, format_minsn_t(blk.tail), e)) return False
def analyze(self, blk: mblock_t, ins: minsn_t): if blk is not None: self.cur_maturity = blk.mba.maturity if self.cur_maturity not in self.maturities: return None for rule in self.rules: try: rule.analyze_instruction(blk, ins) except RuntimeError: optimizer_logger.error( "error during rule {0} for instruction {1}".format( rule, format_minsn_t(ins))) return None
def set_cur_flow(self, cur_blk: mblock_t, cur_ins: minsn_t): self.cur_blk = cur_blk self.cur_ins = cur_ins self.next_blk = cur_blk if self.cur_ins is None: self.next_blk = self.cur_blk.mba.get_mblock(self.cur_blk.serial + 1) self.next_ins = self.next_blk.head else: self.next_ins = self.cur_ins.next if self.next_ins is None: self.next_blk = self.cur_blk.mba.get_mblock( self.cur_blk.serial + 1) self.next_ins = self.next_blk.head emulator_log.debug("Setting next block {0} and next ins {1}".format( self.next_blk.serial, format_minsn_t(self.next_ins)))
def simplify(self, ins): self.res_mop_size = ins.d.size if ins.opcode != self.opcode: return None self.formatted_ins = format_minsn_t(ins) self.non_cst_mop_list = [] self.cst_mop_list = [] self.add_mop(ins.l) self.add_mop(ins.r) self._is_instruction_simplified = False final_mop_list = self.do_simplification() if not self._is_instruction_simplified: return None return self.create_new_chain(ins, final_mop_list)
def analyze_blk(self, blk: mblock_t) -> int: if (blk.tail is None) or blk.tail.opcode not in JMP_OPCODE_HANDLED: return 0 if blk.tail.r.t != mop_n: return 0 unflat_logger.info( "Checking if block {0} can be simplified: {1}".format( blk.serial, format_minsn_t(blk.tail))) pred_jmp_always_taken, pred_jmp_never_taken, pred_jmp_unk = self.sort_predecessors( blk) unflat_logger.info( "Block {0} has {1} preds: {2} always jmp, {3} never jmp, {4} unk". format(blk.serial, blk.npred(), len(pred_jmp_always_taken), len(pred_jmp_never_taken), len(pred_jmp_unk))) nb_change = 0 if len(pred_jmp_always_taken) > 0: dump_microcode_for_debug( self.mba, self.log_dir, "{0}_{1}_before_jmp_always_fix".format(self.cur_maturity_pass, blk.serial)) for pred_blk in pred_jmp_always_taken: new_jmp_block, new_default_block = duplicate_block(blk) make_2way_block_goto(new_jmp_block, blk.tail.d.b) update_blk_successor(pred_blk, blk.serial, new_jmp_block.serial) dump_microcode_for_debug( self.mba, self.log_dir, "{0}_{1}_after_jmp_always_fix".format(self.cur_maturity_pass, blk.serial)) nb_change += len(pred_jmp_always_taken) if len(pred_jmp_never_taken) > 0: dump_microcode_for_debug( self.mba, self.log_dir, "{0}_{1}_before_jmp_never_fix".format(self.cur_maturity_pass, blk.serial)) for pred_blk in pred_jmp_never_taken: new_jmp_block, new_default_block = duplicate_block(blk) make_2way_block_goto(new_jmp_block, blk.serial + 1) update_blk_successor(pred_blk, blk.serial, new_jmp_block.serial) dump_microcode_for_debug( self.mba, self.log_dir, "{0}_{1}_after_jmp_never_fix".format(self.cur_maturity_pass, blk.serial)) nb_change += len(pred_jmp_never_taken) return nb_change
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 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 analyze_blk(self, blk: mblock_t) -> int: if (blk.tail is None) or blk.tail.opcode not in FAKE_LOOP_OPCODES: return 0 if blk.get_reginsn_qty() != 1: return 0 if blk.tail.r.t != mop_n: return 0 unflat_logger.info("Checking if block {0} is fake loop: {1}".format( blk.serial, format_minsn_t(blk.tail))) op_compared = mop_t(blk.tail.l) blk_preset_list = [x for x in blk.predset] nb_change = 0 for pred_serial in blk_preset_list: cmp_variable_tracker = MopTracker([op_compared], max_nb_block=100, max_path=1000) cmp_variable_tracker.reset() pred_blk = blk.mba.get_mblock(pred_serial) pred_histories = cmp_variable_tracker.search_backward( pred_blk, pred_blk.tail) father_is_resolvable = all([ father_history.is_resolved() for father_history in pred_histories ]) if not father_is_resolvable: return 0 pred_values = get_all_possibles_values(pred_histories, [op_compared]) pred_values = [x[0] for x in pred_values] if None in pred_values: unflat_logger.info( "Some path are not resolved, can't fix jump") return 0 unflat_logger.info( "Pred {0} has {1} possible path ({2} different cst): {3}". format(pred_blk.serial, len(pred_values), len(set(pred_values)), pred_values)) if self.fix_successor(blk, pred_blk, pred_values): nb_change += 1 return nb_change
def minsn_to_ast(instruction: minsn_t) -> Union[None, AstNode, AstLeaf]: try: if instruction.opcode in MINSN_TO_AST_FORBIDDEN_OPCODES: # To avoid error 50278 return None ins_mop = mop_t() ins_mop.create_from_insn(instruction) if instruction.opcode == m_mov: tmp = AstNode(m_mov, mop_to_ast(ins_mop)) tmp.mop = ins_mop tmp.dest_size = instruction.d.size tmp.ea = instruction.ea tmp.dst_mop = instruction.d return tmp tmp = mop_to_ast(ins_mop) tmp.dst_mop = instruction.d return tmp except RuntimeError as e: logger.error("Error while transforming instruction {0}: {1}".format( format_minsn_t(instruction), e)) return None
def _eval_instruction( self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]: if ins is None: return None is_flow_instruction = self._eval_control_flow_instruction( ins, environment) if is_flow_instruction: return None call_helper_res = self._eval_call_helper(ins, environment) if call_helper_res is not None: return call_helper_res if ins.opcode == m_call: return self._eval_call(ins, environment) elif ins.opcode == m_icall: return self._eval_call(ins, environment) res_mask = AND_TABLE[ins.d.size] if ins.opcode == m_ldx: return self._eval_load(ins, environment) elif ins.opcode == m_stx: return self._eval_store(ins, environment) elif ins.opcode == m_mov: return (self.eval(ins.l, environment)) & res_mask elif ins.opcode == m_neg: return (-self.eval(ins.l, environment)) & res_mask elif ins.opcode == m_lnot: return self.eval(ins.l, environment) != 0 elif ins.opcode == m_bnot: return (self.eval(ins.l, environment) ^ res_mask) & res_mask elif ins.opcode == m_xds: left_value_signed = unsigned_to_signed( self.eval(ins.l, environment), ins.l.size) return signed_to_unsigned(left_value_signed, ins.d.size) & res_mask elif ins.opcode == m_xdu: return (self.eval(ins.l, environment)) & res_mask elif ins.opcode == m_low: return (self.eval(ins.l, environment)) & res_mask elif ins.opcode == m_add: return (self.eval(ins.l, environment) + self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_sub: return (self.eval(ins.l, environment) - self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_mul: return (self.eval(ins.l, environment) * self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_udiv: return (self.eval(ins.l, environment) // self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_sdiv: return (self.eval(ins.l, environment) // self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_umod: return (self.eval(ins.l, environment) % self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_smod: return (self.eval(ins.l, environment) % self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_or: return (self.eval(ins.l, environment) | self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_and: return (self.eval(ins.l, environment) & self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_xor: return (self.eval(ins.l, environment) ^ self.eval(ins.r, environment)) & res_mask elif ins.opcode == m_shl: return (self.eval(ins.l, environment) << self.eval( ins.r, environment)) & res_mask elif ins.opcode == m_shr: return (self.eval(ins.l, environment) >> self.eval( ins.r, environment)) & res_mask elif ins.opcode == m_sar: res_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) >> self.eval( ins.r, environment) return signed_to_unsigned(res_signed, ins.d.size) & res_mask elif ins.opcode == m_cfadd: tmp = get_add_cf(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size) return tmp & res_mask elif ins.opcode == m_ofadd: tmp = get_add_of(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size) return tmp & res_mask elif ins.opcode == m_sets: left_value_signed = unsigned_to_signed( self.eval(ins.l, environment), ins.l.size) res = 1 if left_value_signed < 0 else 0 return res & res_mask elif ins.opcode == m_seto: left_value_signed = unsigned_to_signed( self.eval(ins.l, environment), ins.l.size) right_value_signed = unsigned_to_signed( self.eval(ins.r, environment), ins.r.size) sub_overflow = get_sub_of(left_value_signed, right_value_signed, ins.l.size) return sub_overflow & res_mask elif ins.opcode == m_setnz: res = 1 if self.eval(ins.l, environment) != self.eval( ins.r, environment) else 0 return res & res_mask elif ins.opcode == m_setz: res = 1 if self.eval(ins.l, environment) == self.eval( ins.r, environment) else 0 return res & res_mask elif ins.opcode == m_setae: res = 1 if self.eval(ins.l, environment) >= self.eval( ins.r, environment) else 0 return res & res_mask elif ins.opcode == m_setb: res = 1 if self.eval(ins.l, environment) < self.eval( ins.r, environment) else 0 return res & res_mask elif ins.opcode == m_seta: res = 1 if self.eval(ins.l, environment) > self.eval( ins.r, environment) else 0 return res & res_mask elif ins.opcode == m_setbe: res = 1 if self.eval(ins.l, environment) <= self.eval( ins.r, environment) else 0 return res & res_mask elif ins.opcode == m_setg: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) res = 1 if left_value > right_value else 0 return res & res_mask elif ins.opcode == m_setge: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) res = 1 if left_value >= right_value else 0 return res & res_mask elif ins.opcode == m_setl: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) res = 1 if left_value < right_value else 0 return res & res_mask elif ins.opcode == m_setle: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) res = 1 if left_value <= right_value else 0 return res & res_mask elif ins.opcode == m_setp: res = get_parity_flag(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size) return res & res_mask raise EmulationException( "Unsupported instruction opcode '{0}': '{1}'".format( opcode_to_string(ins.opcode), format_minsn_t(ins)))
def _eval_conditional_jump( self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]: if ins.opcode not in CONDITIONAL_JUMP_OPCODES: return None if ins.opcode == m_jtbl: # This is not handled the same way return None cur_blk = environment.cur_blk direct_child_serial = cur_blk.serial + 1 if ins.opcode == m_jcnd: jump_taken = self.eval(ins.l, environment) != 0 elif ins.opcode == m_jnz: jump_taken = self.eval(ins.l, environment) != self.eval( ins.r, environment) elif ins.opcode == m_jz: jump_taken = self.eval(ins.l, environment) == self.eval( ins.r, environment) elif ins.opcode == m_jae: jump_taken = self.eval(ins.l, environment) >= self.eval( ins.r, environment) elif ins.opcode == m_jb: jump_taken = self.eval(ins.l, environment) < self.eval( ins.r, environment) elif ins.opcode == m_ja: jump_taken = self.eval(ins.l, environment) > self.eval( ins.r, environment) elif ins.opcode == m_jbe: jump_taken = self.eval(ins.l, environment) <= self.eval( ins.r, environment) elif ins.opcode == m_jg: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) jump_taken = left_value > right_value elif ins.opcode == m_jge: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) jump_taken = left_value >= right_value elif ins.opcode == m_jl: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) jump_taken = left_value < right_value elif ins.opcode == m_jle: left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size) jump_taken = left_value <= right_value else: # This should never happen raise EmulationException( "Unhandled conditional jump: '{0}'".format( format_minsn_t(ins))) return self._get_blk_serial( ins.d) if jump_taken else direct_child_serial