From 9c6dfd676ed49c84fd01d7fc2ec0e88e5efbed4c Mon Sep 17 00:00:00 2001 From: nemerle Date: Tue, 10 May 2016 14:02:26 +0200 Subject: [PATCH] Move JMP processing from Function class Add MarkAsSwitchCase command --- include/Procedure.h | 5 -- src/FollowControlFlow.cpp | 31 +++++++++ src/FollowControlFlow.h | 19 ++++++ src/parser.cpp | 132 ++++++++++++++++++-------------------- 4 files changed, 112 insertions(+), 75 deletions(-) diff --git a/include/Procedure.h b/include/Procedure.h index 62ef0d6..40769ef 100644 --- a/include/Procedure.h +++ b/include/Procedure.h @@ -232,7 +232,6 @@ public: void markImpure(); void findImmedDom(); void process_operands(ICODE &pIcode, STATE *pstate); - bool process_JMP(ICODE &pIcode, STATE *pstate, CALL_GRAPH *pcallGraph); bool process_CALL(ICODE &pIcode, CALL_GRAPH *pcallGraph, STATE *pstate); void freeCFG(); void codeGen(QIODevice & fs); @@ -285,10 +284,6 @@ protected: bool findDerivedSeq(derSeq &derivedGi); bool nextOrderGraph(derSeq &derivedGi); void addOutEdgesForConditionalJump(BB* pBB, int next_ip, LLInst *ll); - -private: - bool decodeIndirectJMP(ICODE &pIcode, STATE *pstate, CALL_GRAPH *pcallGraph); - bool decodeIndirectJMP2(ICODE &pIcode, STATE *pstate, CALL_GRAPH *pcallGraph); }; typedef std::list FunctionListType; diff --git a/src/FollowControlFlow.cpp b/src/FollowControlFlow.cpp index 5c2d87d..569eefc 100644 --- a/src/FollowControlFlow.cpp +++ b/src/FollowControlFlow.cpp @@ -16,3 +16,34 @@ bool FollowControlFlow::execute(CommandContext *ctx) FollowCtrl(*scanned_func,proj.callGraph, &m_start_state); return false; } + +QString MarkAsSwitchCase::instanceDescription() const +{ + return name() + QString(" 0x%1 -> 0x%2 ; case %3") + .arg(m_src_addr,8,16,QChar('0')) + .arg(m_dst_addr,8,16,QChar('0')) + .arg(m_case_label); + +} + +bool MarkAsSwitchCase::execute(CommandContext * ctx) +{ + //TODO: record code/data referneces in project for navigation UI purposes ? + auto switch_insn = ctx->m_func->Icode.labelSrch(m_src_addr); + if(switch_insn==ctx->m_func->Icode.end()) { + ctx->recordFailure(this,QString("switch instruction @ 0x%1 not found in procedure's instructions ?") + .arg(m_src_addr,8,16,QChar('0'))); + return false; + } + auto insn = ctx->m_func->Icode.labelSrch(m_dst_addr); + if(insn==ctx->m_func->Icode.end()) { + ctx->recordFailure(this,QString("switch target instruction 0x%1 not found in procedure's instructions ?") + .arg(m_dst_addr,8,16,QChar('0'))); + return false; + } + insn->ll()->caseEntry = m_case_label; + insn->ll()->setFlags(CASE); + switch_insn->ll()->caseTbl2.push_back( m_dst_addr ); + return true; + +} diff --git a/src/FollowControlFlow.h b/src/FollowControlFlow.h index fb34b80..7478d5e 100644 --- a/src/FollowControlFlow.h +++ b/src/FollowControlFlow.h @@ -16,5 +16,24 @@ public: QString instanceDescription() const override; bool execute(CommandContext *ctx) override; }; +// mark instruction at address m_dst_addr as a case m_case_label of switch located at m_src_addr +class MarkAsSwitchCase : public Command +{ + uint32_t m_src_addr; + uint32_t m_dst_addr; + int m_case_label; +public: + MarkAsSwitchCase(uint32_t src_addr,uint32_t dst_addr,int lab) : + Command("Mark as switch case",eFunction), + m_src_addr(src_addr), + m_dst_addr(dst_addr), + m_case_label(lab) + {} + // Command interface +public: + QString instanceDescription() const override; + bool execute(CommandContext *ctx) override; + +}; #endif // FOLLOWCONTROLFLOW_H diff --git a/src/parser.cpp b/src/parser.cpp index 5f46362..00961aa 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -32,7 +32,8 @@ using namespace std; //static void FollowCtrl (Function * pProc, CALL_GRAPH * pcallGraph, STATE * pstate); static void setBits(int16_t type, uint32_t start, uint32_t len); static void process_MOV(LLInst &ll, STATE * pstate); -static SYM * lookupAddr (LLOperand *pm, STATE * pstate, int size, uint16_t duFlag); +static bool process_JMP(const PtrFunction &func,ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGraph); +static SYM * lookupAddr (LLOperand *pm, STATE * pstate, int size, uint16_t duFlag); //void interactDis(Function * initProc, int ic); /* Returns the size of the string pointed by sym and delimited by delim. @@ -277,13 +278,13 @@ void FollowCtrl(Function &func,CALL_GRAPH * pcallGraph, STATE *pstate) /* Next icode. Note: not the same as GetLastIcode() because of the call to FollowCtrl() */ pIcode = func.Icode.GetIcode(ip); /* do the jump path */ - done = func.process_JMP (*pIcode, pstate, pcallGraph); + done = process_JMP(func.shared_from_this(),*pIcode, pstate, pcallGraph); break; } /*** Jumps ***/ case iJMP: case iJMPF: /* Returns true if we've run into a loop */ - done = func.process_JMP (*pIcode, pstate, pcallGraph); + done = process_JMP (func.shared_from_this(),*pIcode, pstate, pcallGraph); break; /*** Calls ***/ @@ -401,7 +402,7 @@ bool Function::followAllTableEntries(JumpTable &table, uint32_t cs, ICODE& pIcod } return true; } -bool Function::decodeIndirectJMP(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGraph) +static bool decodeIndirectJMP(const PtrFunction &func,ICODE & pIcode, STATE *pstate) { PROG &prog(Project::get()->prog); // mov cx,NUM_CASES @@ -418,9 +419,9 @@ bool Function::decodeIndirectJMP(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pca // jmp word ptr [bx+2*NUM_CASES] static const llIcode match_seq[] = {iMOV,iMOV,iMOV,iCMP,iJE,iADD,iLOOP,iJMP,iJMP}; - if(Icode.size()<8) + if(func->Icode.size()<8) return false; - if(&Icode.back()!=&pIcode) // not the last insn in the list skip it + if(&func->Icode.back()!=&pIcode) // not the last insn in the list skip it return false; if(pIcode.ll()->src().regi != INDEX_BX) { return false; @@ -428,7 +429,7 @@ bool Function::decodeIndirectJMP(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pca // find address-wise predecessors of the icode std::deque matched; QMap addrmap; - for(ICODE & ic : Icode) { + for(ICODE & ic : func->Icode) { addrmap[ic.ll()->GetLlLabel()] = ⁣ } auto iter = addrmap.find(pIcode.ll()->GetLlLabel()); @@ -478,16 +479,12 @@ bool Function::decodeIndirectJMP(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pca STATE StCopy = *pstate; uint32_t jump_target_location = table_addr + num_cases*2 + i*2; StCopy.IP = cs + *(uint16_t *)(prog.image()+jump_target_location); - iICODE last_current_insn = (++Icode.rbegin()).base(); - Project::get()->addCommand(shared_from_this(),new FollowControlFlow(StCopy)); - ++last_current_insn; - last_current_insn->ll()->caseEntry = i; - last_current_insn->ll()->setFlags(CASE); - pIcode.ll()->caseTbl2.push_back( last_current_insn->ll()->GetLlLabel() ); + Project::get()->addCommand(func,new FollowControlFlow(StCopy)); + Project::get()->addCommand(func,new MarkAsSwitchCase(pIcode.ll()->label,StCopy.IP,i)); } return true; } -bool Function::decodeIndirectJMP2(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGraph) +static bool decodeIndirectJMP2(const PtrFunction &func,ICODE & pIcode, STATE *pstate) { PROG &prog(Project::get()->prog); // mov cx,NUM_CASES @@ -508,9 +505,9 @@ bool Function::decodeIndirectJMP2(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pc // jmp word ptr [bx+2*NUM_CASES] static const llIcode match_seq[] = {iMOV,iMOV,iMOV,iCMP,iJNE,iMOV,iCMP,iJE,iADD,iLOOP,iJMP,iJMP}; - if(Icode.size()<12) + if(func->Icode.size()<12) return false; - if(&Icode.back() != &pIcode) // not the last insn in the list skip it + if(&func->Icode.back() != &pIcode) // not the last insn in the list skip it return false; if(pIcode.ll()->src().regi != INDEX_BX) { return false; @@ -518,7 +515,7 @@ bool Function::decodeIndirectJMP2(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pc // find address-wise predecessors of the icode std::deque matched; QMap addrmap; - for(ICODE & ic : Icode) { + for(ICODE & ic : func->Icode) { addrmap[ic.ll()->GetLlLabel()] = ⁣ } auto iter = addrmap.find(pIcode.ll()->GetLlLabel()); @@ -564,41 +561,19 @@ bool Function::decodeIndirectJMP2(ICODE & pIcode, STATE *pstate, CALL_GRAPH * pc STATE StCopy = *pstate; uint32_t jump_target_location = table_addr + num_cases*4 + i*2; StCopy.IP = cs + *(uint16_t *)(prog.image()+jump_target_location); - iICODE last_current_insn = (++Icode.rbegin()).base(); - Project::get()->addCommand(shared_from_this(),new FollowControlFlow(StCopy)); - ++last_current_insn; - last_current_insn->ll()->caseEntry = i; - last_current_insn->ll()->setFlags(CASE); - pIcode.ll()->caseTbl2.push_back( last_current_insn->ll()->GetLlLabel() ); + Project::get()->addCommand(func,new FollowControlFlow(StCopy)); + Project::get()->addCommand(func,new MarkAsSwitchCase(pIcode.ll()->label,StCopy.IP,i)); } return true; } -bool Function::process_JMP (ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGraph) -{ +/* Look for switch() stmt. idiom of the form + * JMP uint16_t ptr word_offset[rBX | rSI | rDI] */ +static bool decodeIndirectJMP0(const PtrFunction &func,ICODE & pIcode, STATE *pstate) { + const static uint8_t i2r[4] = {rSI, rDI, rBP, rBX}; + + uint32_t seg = (pIcode.ll()->src().seg)? pIcode.ll()->src().seg: rDS; PROG &prog(Project::get()->prog); - static uint8_t i2r[4] = {rSI, rDI, rBP, rBX}; - ICODE _Icode; - uint32_t cs, offTable, endTable; - uint32_t i, k, seg, target; - - if (pIcode.ll()->testFlags(I)) - { - if (pIcode.ll()->getOpcode() == iJMPF) - pstate->setState( rCS, LH(prog.image() + pIcode.ll()->label + 3)); - pstate->IP = pIcode.ll()->src().getImm2(); - int64_t i = pIcode.ll()->src().getImm2(); - if (i < 0) - { - exit(1); - } - - /* Return true if jump target is already parsed */ - return Icode.alreadyDecoded(i); - } - /* We've got an indirect JMP - look for switch() stmt. idiom of the form - * JMP uint16_t ptr word_offset[rBX | rSI | rDI] */ - seg = (pIcode.ll()->src().seg)? pIcode.ll()->src().seg: rDS; /* Ensure we have a uint16_t offset & valid seg */ if (pIcode.ll()->match(iJMP) and (pIcode.ll()->testFlags(WORD_OFF)) and @@ -608,8 +583,9 @@ bool Function::process_JMP (ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGra pIcode.ll()->src().regi == INDEX_BX)) { - offTable = ((uint32_t)(uint16_t)pstate->r[seg] << 4) + pIcode.ll()->src().off; - + uint32_t offTable = ((uint32_t)(uint16_t)pstate->r[seg] << 4) + pIcode.ll()->src().off; + uint32_t endTable; + uint32_t i; /* Firstly look for a leading range check of the form:- * CMP {BX | SI | DI}, immed * JA | JAE | JB | JBE @@ -630,10 +606,10 @@ bool Function::process_JMP (ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGra /* Now do some heuristic pruning. Look for ptrs. into the table * and for addresses that don't appear to point to valid code. */ - cs = (uint32_t)(uint16_t)pstate->r[rCS] << 4; + uint32_t cs = (uint32_t)(uint16_t)pstate->r[rCS] << 4; for (i = offTable; i < endTable; i += 2) { - target = cs + LH(&prog.image()[i]); + uint32_t target = cs + LH(&prog.image()[i]); if (target < endTable and target >= offTable) endTable = target; else if (target >= (uint32_t)prog.cbImage) @@ -642,10 +618,10 @@ bool Function::process_JMP (ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGra for (i = offTable; i < endTable; i += 2) { - target = cs + LH(&prog.image()[i]); + uint32_t target = cs + LH(&prog.image()[i]); /* Be wary of 00 00 as code - it's probably data */ - if (not (prog.image()[target] or prog.image()[target+1]) or - scan(target, _Icode)) + ICODE _Icode; + if (not (prog.image()[target] or prog.image()[target+1]) or scan(target, _Icode)!=NO_ERR) endTable = i; } @@ -661,39 +637,55 @@ bool Function::process_JMP (ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGra setBits(BM_DATA, offTable, endTable - offTable); pIcode.ll()->setFlags(SWITCH); - //pIcode.ll()->caseTbl2.numEntries = (endTable - offTable) / 2; + uint32_t k; for (i = offTable, k = 0; i < endTable; i += 2) { StCopy = *pstate; StCopy.IP = cs + LH(&prog.image()[i]); - iICODE last_current_insn = (++Icode.rbegin()).base(); - //ip = Icode.size(); - - Project::get()->addCommand(shared_from_this(),new FollowControlFlow(StCopy)); - - ++last_current_insn; - last_current_insn->ll()->caseEntry = k++; - last_current_insn->ll()->setFlags(CASE); - pIcode.ll()->caseTbl2.push_back( last_current_insn->ll()->GetLlLabel() ); - + Project::get()->addCommand(func,new FollowControlFlow(StCopy)); + Project::get()->addCommand(func,new MarkAsSwitchCase(pIcode.ll()->label,StCopy.IP,k++)); } return true; } } - if(decodeIndirectJMP(pIcode,pstate,pcallGraph)) { + return false; +} +static bool process_JMP(const PtrFunction &func,ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGraph) +{ + PROG &prog(Project::get()->prog); + + if (pIcode.ll()->testFlags(I)) + { + if (pIcode.ll()->getOpcode() == iJMPF) + pstate->setState( rCS, LH(prog.image() + pIcode.ll()->label + 3)); + pstate->IP = pIcode.ll()->src().getImm2(); + int64_t i = pIcode.ll()->src().getImm2(); + if (i < 0) + { + exit(1); + } + + /* Return true if jump target is already parsed */ + return func->Icode.alreadyDecoded(i); + } + /* We've got an indirect JMP */ + if(decodeIndirectJMP0(func,pIcode,pstate)) { return true; } - if(decodeIndirectJMP2(pIcode,pstate,pcallGraph)) { + if(decodeIndirectJMP(func,pIcode,pstate)) { + return true; + } + if(decodeIndirectJMP2(func,pIcode,pstate)) { return true; } /* Can't do anything with this jump */ - flg |= PROC_IJMP; - flg &= ~TERMINATES; + func->flg |= PROC_IJMP; + func->flg &= ~TERMINATES; // TODO: consider adding a new user-interactive command ResolveControlFlowFailure ? - interactDis(shared_from_this(), Icode.size()-1); + interactDis(func, func->Icode.size()-1); return true; }