#include <memory> #include <assert.h> #include "interfaces/idapro/all.h" #include "interfaces/STARSTypes.h" #include "interfaces/STARSIDATypes.h" #include "base/SMPDataFlowAnalysis.h" #include "base/SMPInstr.h" #include "interfaces/SMPDBInterface.h" #include "interfaces/abstract/all.h" #if 0 #include <pro.h> #include <nalt.hpp> #include <ua.hpp> #include <xref.hpp> #endif using namespace std; static uint32_t UseMacros[STARS_UA_MAXOP] = {STARS_CF_USE1, STARS_CF_USE2, STARS_CF_USE3, STARS_CF_USE4, STARS_CF_USE5, STARS_CF_USE6}; static uint32_t DefMacros[STARS_UA_MAXOP] = {STARS_CF_CHG1, STARS_CF_CHG2, STARS_CF_CHG3, STARS_CF_CHG4, STARS_CF_CHG5, STARS_CF_CHG6}; STARS_InstructionID_t STARS_IDA_Instruction_t::GetNextInstructionID(void) const { STARS_ea_t addr = this->m_id.GetIDWithinFile(); SMPInstr TempInst(addr); if (!TempInst.FillCmd()) { return STARS_BADADDR; } STARS_ea_t next_addr = addr + TempInst.GetSize(); return STARS_InstructionID_t(next_addr, this->m_id.GetFileNum()); } STARS_InstructionID_t STARS_IDA_Instruction_t::GetTargetInstructionID(void) const { assert(NULL != this->STARScmd.Operands[0]); STARS_ea_t TargetAddr = this->STARScmd.Operands[0]->GetAddr(); return STARS_InstructionID_t(TargetAddr, this->m_id.GetFileNum()); } void STARS_IDA_Instruction_t::InitOperand(op_t &InitOp) const { #if 0 InitOp.n = 0; InitOp.type = o_void; InitOp.offb = 0; InitOp.offo = 0; InitOp.flags = 0; InitOp.set_showed(); // NOTE: InitOp.dtyp field is initialized in IDAP_run() to 32 or 64 bits. InitOp.reg = R_none; InitOp.value = 0; InitOp.addr = 0; InitOp.specval = 0; InitOp.specflag1 = 0; InitOp.specflag2 = 0; InitOp.specflag3 = 0; InitOp.specflag4 = 0; #else // not 0 #if __GNUC__ >= 8 #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif (void) memset(&InitOp, 0, sizeof(op_t)); #if __GNUC__ >= 8 #pragma GCC diagnostic pop #endif #if (IDA_SDK_VERSION < 700) InitOp.dtyp = global_STARS_program->GetSTARS_ISA_dtyp(); #else InitOp.dtype = global_STARS_program->GetSTARS_ISA_dtyp(); #endif #if IDA_SDK_VERSION < 680 InitOp.set_showed(); #else InitOp.set_shown(); #endif #endif return; } // end of STARS_IDA_Instruction_t::InitOperand() // Get instruction info by address from IDA Pro. bool STARS_IDA_Instruction_t::STARS_GetCmd(void) { bool success = true; int InstrLen = 0; this->VoidOpndsPtr = nullptr; op_t TempOp; this->InitOperand(TempOp); this->VoidOpndsPtr = dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); // Fill cmd structure with disassembly of instr #if (IDA_SDK_VERSION < 700) InstrLen = decode_insn(m_id.GetIDWithinFile()); // Copy cmd fields to member STARScmd. this->STARScmd.itype = cmd.itype; this->STARScmd.size = cmd.size; this->STARScmd.auxpref = cmd.auxpref; this->STARScmd.segpref = cmd.segpref; this->STARScmd.insnpref = cmd.insnpref; this->STARScmd.flags = cmd.flags; // Get the canonical features into member STARSfeatures. this->STARSfeatures = cmd.get_canon_feature(); #else insn_t NewInsn; InstrLen = ::decode_insn(&NewInsn, m_id.GetIDWithinFile()); // Copy cmd fields to member STARScmd. this->STARScmd.itype = NewInsn.itype; this->STARScmd.size = NewInsn.size; this->STARScmd.auxpref = NewInsn.auxpref; this->STARScmd.segpref = NewInsn.segpref; this->STARScmd.insnpref = NewInsn.insnpref; this->STARScmd.flags = NewInsn.flags; // Get the canonical features into member STARSfeatures. #if (IDA_SDK_VERSION < 750) this->STARSfeatures = NewInsn.get_canon_feature(); #else this->STARSfeatures = NewInsn.get_canon_feature(PH); #endif #endif if (0 >= InstrLen) { SMP_msg("ERROR: decode_insn failed at %ullx in file %u\n", (unsigned long long) m_id.GetIDWithinFile(), m_id.GetFileNum()); this->STARScmd.size = 0; for (int i = 0; i < STARS_UA_MAXOP; ++i) { this->STARScmd.Operands.push_back(nullptr); this->STARScmd.Operands[i] = this->MakeVoidOpnd(); } success = false; return success; } for (int i = 0; i < STARS_UA_MAXOP; ++i) { #if (IDA_SDK_VERSION < 700) this->STARScmd.Operands.push_back(std::make_shared<STARS_IDA_op_t>(cmd.Operands[i])); #else this->STARScmd.Operands.push_back(std::make_shared<STARS_IDA_op_t>(NewInsn.ops[i])); #endif assert(dynamic_pointer_cast<STARS_IDA_op_t>(this->STARScmd.Operands[i]) != nullptr); dynamic_pointer_cast<STARS_IDA_op_t>(this->STARScmd.Operands[i])->SetSpecFlag4(0); #ifdef __EA64__ if (global_STARS_program->GetSTARS_ISA_Bitwidth() == 64) { // Copy the cmd.rex prefix into the op_t.specflag4 field for each operand // that has a SIB byte. dynamic_pointer_cast<STARS_IDA_op_t>(this->STARScmd.Operands[i])->SetSpecFlag4(this->STARScmd.rex); char Flag4Value = this->STARScmd.Operands[i]->GetSpecFlag4(); if (this->STARScmd.Operands[i]->IsMemOp() && (Flag4Value & STARS_REX_R)) { // Only various register types can have the registger extension, e.g. RBX becomes R11 with extension bit. Flag4Value -= STARS_REX_R; dynamic_pointer_cast<STARS_IDA_op_t>(this->STARScmd.Operands[i])->SetSpecFlag4(Flag4Value); } } #endif // See comments on STARS_VEXPR and STARS_VSIB in SMPDataFlowAnalysis.h. // These bits do not (as of IDA Pro 6.4) conflict with cmd.rex bits. #if (IDA_SDK_VERSION < 700) if ((cmd.auxpref & aux_vexpr) != 0) { #else if ((NewInsn.auxpref & aux_vexpr) != 0) { #endif dynamic_pointer_cast<STARS_IDA_op_t>(this->STARScmd.Operands[i])->SetBitInSpecFlag4(STARS_VEXPR); } switch (this->STARScmd.itype) { case NN_vgatherdps: case NN_vgatherdpd: case NN_vgatherqps: case NN_vgatherqpd: case NN_vpgatherdd: case NN_vpgatherdq: case NN_vpgatherqd: case NN_vpgatherqq: dynamic_pointer_cast<STARS_IDA_op_t>(this->STARScmd.Operands[i])->SetBitInSpecFlag4(STARS_VSIB); default: ; } } // end for all operands // Simplify the operand encoding so that identical operands don't appear to be different. for (std::size_t i = 0; i < STARS_UA_MAXOP; ++i) { this->GetOpnd(i)->CleanOpndEncoding(); } return success; } // end of STARS_IDA_Instruction_t::STARS_GetCmd() bool STARS_IDA_Instruction_t::Has64BitOperands(void) { #ifdef __EA64__ return (((this->STARScmd.auxpref & aux_use64) != 0) && ((this->STARScmd.rex & REX_W) != 0 || (((this->STARScmd.auxpref & aux_natop) != 0) && this->OpcodeDefaultsTo64BitOperands()))); // 64-bit segment, rex.w or insns-64 #else return false; #endif } bool STARS_IDA_Instruction_t::Uses32BitAddressing(void) const { int Temp = this->STARScmd.auxpref & (aux_use32 | aux_use64 | aux_natad); return (Temp == (aux_natad | aux_use32)) || (Temp == 0) || (Temp == aux_use64); } bool STARS_IDA_Instruction_t::IsUseOpnd(std::size_t OpndNum) const { return (this->GetInstFeatures() & UseMacros[OpndNum]); } bool STARS_IDA_Instruction_t::IsDefOpnd(std::size_t OpndNum) const { return (this->GetInstFeatures() & DefMacros[OpndNum]); } // set the USE bit void STARS_IDA_Instruction_t::SetOpUsed(std::size_t OpndNum) { this->STARSfeatures |= UseMacros[OpndNum]; return; } // reset the USE bit void STARS_IDA_Instruction_t::SetOpNotUsed(std::size_t OpndNum) { this->STARSfeatures &= (~UseMacros[OpndNum]); return; } // set the DEF bit void STARS_IDA_Instruction_t::SetOpDefed(std::size_t OpndNum) { this->STARSfeatures |= DefMacros[OpndNum]; return; } // reset the DEF bit void STARS_IDA_Instruction_t::SetOpNotDefed(std::size_t OpndNum) { this->STARSfeatures &= (~DefMacros[OpndNum]); return; } // Fix up IDA Pro IMUL instruction by removing operand 1 void STARS_IDA_Instruction_t::RemoveIDAOp1ForIMUL(void) { this->STARScmd.Operands[1] = this->STARScmd.Operands[2]; this->STARScmd.Operands[2] = this->VoidOpndsPtr; return; } STARSOpndTypePtr STARS_IDA_Instruction_t::MakeVoidOpnd(void) const { return this->VoidOpndsPtr; } // end of STARS_IDA_Instruction_t::MakeVoidOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeImmediateOpnd(STARS_uval_t value) const { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_imm; TempOp.value = value; return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeVoidOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeRegOpnd(STARS_regnum_t RegNum, bool DefaultToMachineWidth) { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_reg; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(RegNum, (DefaultToMachineWidth && (global_STARS_program->GetSTARS_ISA_Bitwidth() == 64))); #else TempOp.dtype = GetRegDtyp(RegNum, (DefaultToMachineWidth && (global_STARS_program->GetSTARS_ISA_Bitwidth() == 64))); #endif TempOp.reg = RegNum; return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeRegOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeFloatingPointRegOpnd(STARS_regnum_t RegNum) { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_fpreg; if (RegNum < STARS_x86_R_st0) { // X86 encodes STARS_x86_R_st0 as register 0, STARS_x86_R_st1 as register 1, with o_fpreg flag indicating adjustment. // We want to encode using the STARS_regnum_t enumeration alone. RegNum = (STARS_regnum_t) (((int) STARS_x86_R_st0) + ((int) RegNum)); } TempOp.reg = RegNum; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(RegNum, false); #else TempOp.dtype = GetRegDtyp(RegNum, false); #endif return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeFloatingPointRegOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeMMXRegOpnd(STARS_regnum_t RegNum) { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_mmxreg; TempOp.reg = RegNum; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(RegNum, false); #else TempOp.dtype = GetRegDtyp(RegNum, false); #endif return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeMMXRegOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeXMMRegOpnd(STARS_regnum_t RegNum) { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_xmmreg; TempOp.reg = RegNum; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(RegNum, false); #else TempOp.dtype = GetRegDtyp(RegNum, false); #endif return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeXMMRegOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeYMMRegOpnd(STARS_regnum_t RegNum) { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_ymmreg; TempOp.reg = RegNum; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(RegNum, false); #else TempOp.dtype = GetRegDtyp(RegNum, false); #endif return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeYMMRegOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeNearPointerOpnd(STARS_ea_t TargetAddr) const { op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_near; TempOp.addr = TargetAddr; return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } STARSOpndTypePtr STARS_IDA_Instruction_t::MakeMemPhraseOpnd(STARS_regnum_t BaseRegNum, STARS_regnum_t IndexRegNum, uint16_t ScaleFactor) { // TODO: Construct SIB byte when IndexRegNum is used. op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_phrase; TempOp.reg = BaseRegNum; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(BaseRegNum, this->Has64BitOperands()); #else TempOp.dtype = GetRegDtyp(BaseRegNum, this->Has64BitOperands()); #endif return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeMemPhraseOpnd() STARSOpndTypePtr STARS_IDA_Instruction_t::MakeMemDisplacementOpnd(STARS_regnum_t BaseRegNum, STARS_regnum_t IndexRegNum, uint16_t ScaleFactor, STARS_ea_t offset) { // TODO: Construct SIB byte when IndexRegNum is used. op_t TempOp; this->InitOperand(TempOp); TempOp.type = o_displ; TempOp.reg = BaseRegNum; TempOp.addr = (ea_t) offset; #if (IDA_SDK_VERSION < 700) TempOp.dtyp = GetRegDtyp(BaseRegNum, this->Has64BitOperands()); #else TempOp.dtype = GetRegDtyp(BaseRegNum, this->Has64BitOperands()); #endif return dynamic_pointer_cast<STARS_op_t>(std::make_shared<STARS_IDA_op_t>(TempOp)); } // end of STARS_IDA_Instruction_t::MakeMemDisplacementOpnd() bool STARS_IDA_Instruction_t::IsBranchToFarChunk(SMPInstr *CurrInst, STARS_ea_t &TargetAddr) { bool FarBranch = false; set<DefOrUse, LessDefUse>::iterator CurrUse; func_t *CurrChunk = ::get_fchunk(this->m_id.GetIDWithinFile()); if (nullptr != CurrChunk) { // CurrInst is not an orphan for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) { STARSOpndTypePtr JumpTarget = CurrUse->GetOp(); if (JumpTarget->IsNearPointer() || JumpTarget->IsFarPointer()) { // Branches to a code address TargetAddr = JumpTarget->GetAddr(); // stdclib sometimes has jumps to zero and calls to zero. These are dead code. if ((0 != TargetAddr) && (STARS_BADADDR != TargetAddr)) { func_t *TargetChunk = ::get_fchunk(TargetAddr); // Is target address within the same chunk as the branch? #if (IDA_SDK_VERSION < 700) FarBranch = (NULL == TargetChunk) || (CurrChunk->startEA != TargetChunk->startEA); #else FarBranch = (NULL == TargetChunk) || (CurrChunk->start_ea != TargetChunk->start_ea); #endif } } } // end for all USEs } return FarBranch; } // end of STARS_IDA_Instruction_t::IsBranchToFarChunk() bool STARS_IDA_Instruction_t::IsPushFromFixedCall(void) const { STARS_ea_t TargetAddr = this->STARScmd.Operands[0]->GetAddr(); bool PushOfInternalAddress = global_STARS_program->AreInstIDsInSameFunction(this->m_id.GetIDWithinFile(), TargetAddr); return PushOfInternalAddress; } STARS_InstructionID_Set_t STARS_IDA_Instruction_t::GetReferencedInstructionIDs(bool &success) { assert(false); return STARS_InstructionID_Set_t(); } // Get inst IDs of jump targets, call targets, etc., including for analyzeable indirect calls and jumps; success = false otherwise STARS_InstructionID_Set_t STARS_IDA_Instruction_t::GetTargetedInstructionIDs(bool &success) { STARS_InstructionID_Set_t TargetIDSet; // Use code xrefs to find non-fallthrough code targets. SMP_xref_t InstXrefs; for (bool ok = InstXrefs.SMP_first_from(this->m_id.GetIDWithinFile(), XREF_FAR); ok; ok = InstXrefs.SMP_next_from()) { if (!InstXrefs.GetIscode()) break; // no need to go on to data xrefs assert(fl_F != InstXrefs.GetType()); // should not be ordinary fall-through STARS_ea_t TargetAddr = InstXrefs.GetTo(); assert(STARS_BADADDR != TargetAddr); STARS_InstructionID_t TargetID(TargetAddr); pair<STARS_InstructionID_Set_t::iterator, bool> InsertResult = TargetIDSet.insert(TargetID); assert(InsertResult.second); } success = (!TargetIDSet.empty()); return TargetIDSet; } // return inst ID addr for fall-through from this inst STARS_ea_t STARS_IDA_Instruction_t::GetFallThroughInstID(void) { STARS_ea_t FallThroughAddr = STARS_BADADDR; SMP_xref_t InstXrefs; for (bool ok = InstXrefs.SMP_first_from(this->m_id.GetIDWithinFile(), XREF_ALL); ok; ok = InstXrefs.SMP_next_from()) { if (!InstXrefs.GetIscode()) break; // no need to go on to data xrefs if (fl_F == InstXrefs.GetType()) { // ordinary fall-through FallThroughAddr = InstXrefs.GetTo(); break; } } if (STARS_BADADDR == FallThroughAddr) { // We could try to use SMPBasicBlock::FindCallInstFallThrough() here // to audit the completeness of the Xrefs. ; } return FallThroughAddr; } // end of STARS_IDA_Instruction_t::GetFallThroughInstID() // Analyze the indirect jump at IndirJumpInst, put switch table info in TableInfo if available, return false otherwise. // Note: The TableInfo.FollowNodeNum field must be determined by later analysis. bool STARS_IDA_Instruction_t::AnalyzeSwitchStatement(SMPInstr *IndirJumpInst, struct SwitchTableInfo &TableInfo) { bool success = false; assert(NULL != IndirJumpInst); assert(INDIR_JUMP == IndirJumpInst->GetDataFlowType()); STARS_ea_t IndirJumpAddr = IndirJumpInst->GetAddr(); #if (IDA_SDK_VERSION < 700) switch_info_ex_t SwitchInfo; if (get_switch_info_ex(IndirJumpAddr, &SwitchInfo, sizeof(SwitchInfo)) > 0) { #else switch_info_t SwitchInfo; if (get_switch_info(&SwitchInfo, IndirJumpAddr) > 0) { #endif casevec_t CaseVector; eavec_t TargetAddrsVector; #if (IDA_SDK_VERSION < 700) success = ::calc_switch_cases(IndirJumpAddr, &SwitchInfo, &CaseVector, &TargetAddrsVector); #else success = ::calc_switch_cases(&CaseVector, &TargetAddrsVector, IndirJumpAddr, SwitchInfo); #endif if (success) { success = (CaseVector.size() == TargetAddrsVector.size()); // sanity check } if (success) { // Transfer data into TableInfo. TableInfo.FollowNodeNum = SMP_BLOCKNUM_UNINIT; TableInfo.IDomBlockNum = SMP_BLOCKNUM_UNINIT; TableInfo.IndirJumpBlockNum = IndirJumpInst->GetBlock()->GetNumber(); TableInfo.DefaultJumpAddr = SwitchInfo.defjump; // will be STARS_BADADDR if no default case if (STARS_BADADDR != TableInfo.DefaultJumpAddr) { // We have a default case // Guard against bad disassembly by making sure the default case addr is in the function. if (! IndirJumpInst->GetBlock()->GetFunc()->IsInstIDInFunc(TableInfo.DefaultJumpAddr)) { success = false; SMP_msg("ERROR: AnalyzeSwitchStatement: Switch default case addr %llx not in func %s\n", (uint64_t)TableInfo.DefaultJumpAddr, IndirJumpInst->GetBlock()->GetFunc()->GetFuncName()); return success; } SMPBasicBlock *DefaultCaseBlock = IndirJumpInst->GetBlock()->GetFunc()->GetBlockFromInstAddr(TableInfo.DefaultJumpAddr); assert(NULL != DefaultCaseBlock); int DefaultBlockNum = DefaultCaseBlock->GetNumber(); assert(SMP_BLOCKNUM_UNINIT != DefaultBlockNum); TableInfo.DefaultCaseBlockNum = DefaultBlockNum; } else { TableInfo.DefaultCaseBlockNum = SMP_BLOCKNUM_UNINIT; } std::size_t IndexLimit = CaseVector.size(); for (std::size_t Index = 0; Index < IndexLimit; ++Index) { std::vector<STARS_sval_t> CurrCaseValues; std::size_t CaseIndexLimit = CaseVector[Index].size(); for (std::size_t CaseIndex = 0; CaseIndex < CaseIndexLimit; ++CaseIndex) { // Can have case 2, case 4, case 7 etc. all map to one target block CurrCaseValues.push_back((STARS_sval_t) CaseVector[Index].at(CaseIndex)); } TableInfo.IndexValue.push_back(CurrCaseValues); STARS_ea_t TargetAddr = (STARS_ea_t) TargetAddrsVector[Index]; // Translate TargetAddr to a block number. if (STARS_BADADDR == TargetAddr) { success = false; break; } int TargetBlockNum = IndirJumpInst->GetBlock()->GetFunc()->GetBlockNumFromInstAddr(TargetAddr); if (SMP_BLOCKNUM_UNINIT == TargetBlockNum) { success = false; break; } TableInfo.CaseBlockNums.push_back(TargetBlockNum); } // end for Index = 0 to IndexLimit } // end if success } // end if get_switch_info_ex() > 0 return success; } // end of STARS_IDA_Instruction_t::AnalyzeSwitchStatement()