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 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 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 _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 _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 _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
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)))