Skip to content
Snippets Groups Projects
Commit 774b13ee authored by Clark Coleman's avatar Clark Coleman
Browse files

Merge branch 'SPARKwork' into 'master'

Merge old SPARK changes into master.

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