Newer
Older
#include <memory>
#include "interfaces/STARSTypes.h"
#include "interfaces/STARSIDATypes.h"
#include "base/SMPDataFlowAnalysis.h"
#include "base/SMPInstr.h"
#include "interfaces/abstract/all.h"
clc5q
committed
#include <pro.h>
#include <nalt.hpp>
#include <ua.hpp>
#include <xref.hpp>
clc5q
committed
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 {
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
#pragma GCC diagnostic ignored "-Wclass-memaccess"
(void) memset(&InitOp, 0, sizeof(op_t));
#pragma GCC diagnostic pop
InitOp.dtyp = global_STARS_program->GetSTARS_ISA_dtyp();
#else
InitOp.dtype = global_STARS_program->GetSTARS_ISA_dtyp();
#endif
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
// 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)
#else
this->STARSfeatures = NewInsn.get_canon_feature(PH);
#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) {
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 ((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) {
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#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()
clc5q
committed
STARSOpndTypePtr STARS_IDA_Instruction_t::MakeRegOpnd(STARS_regnum_t RegNum, bool DefaultToMachineWidth) {
op_t TempOp;
this->InitOperand(TempOp);
TempOp.type = o_reg;
clc5q
committed
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;
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;
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;
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;
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;
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;
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?
FarBranch = (NULL == TargetChunk) || (CurrChunk->startEA != TargetChunk->startEA);
FarBranch = (NULL == TargetChunk) || (CurrChunk->start_ea != TargetChunk->start_ea);
}
} // 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;
}
Clark Coleman
committed
// 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();
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;
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;
}
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
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()