From 92f9928044e00dacf1f287a00bb9f76af2297f93 Mon Sep 17 00:00:00 2001 From: an7s <an7s@git.zephyr-software.com> Date: Tue, 29 Dec 2015 16:45:06 +0000 Subject: [PATCH] Support for ICFS off STARS annotation Support for module_complete and complete ICFS status field Former-commit-id: fab0cbf0e7cc86c37ea4cab01bca621aab5c874d --- .gitattributes | 1 + libIRDB/include/core/icfs.hpp | 41 +++++++++--- libIRDB/src/core/fileir.cpp | 16 ++--- libIRDB/src/core/icfs.cpp | 49 +++++++++++++-- libIRDB/test/fill_in_indtargs.cpp | 66 +++++++++++++++++--- libIRDB/test/read_variantir.cpp | 2 +- libIRDB/test/tests/ibtarget/Makefile | 14 +++-- libIRDB/test/tests/ibtarget/validate_ibts.sh | 38 +++++++++++ tools/meds2pdb/meds2pdb.cpp | 33 +++++++--- tools/transforms/Rewrite_Utility.cpp | 2 + xform/rewriter.cpp | 34 +++++++--- 11 files changed, 239 insertions(+), 57 deletions(-) create mode 100755 libIRDB/test/tests/ibtarget/validate_ibts.sh diff --git a/.gitattributes b/.gitattributes index df19ed873..9303fa063 100644 --- a/.gitattributes +++ b/.gitattributes @@ -290,6 +290,7 @@ libIRDB/test/tests/ibtarget/jmp.shared.cpp -text libIRDB/test/tests/ibtarget/jmp1.cpp -text libIRDB/test/tests/ibtarget/jmp2.cpp -text libIRDB/test/tests/ibtarget/jmp2.shared.cpp -text +libIRDB/test/tests/ibtarget/validate_ibts.sh -text libIRDB/test/unfix_calls.cpp -text libIRDB/test/unwind-pe.h -text libMEDSannotation/LICENSE.txt -text diff --git a/libIRDB/include/core/icfs.hpp b/libIRDB/include/core/icfs.hpp index 78b4f6c5a..bb9e1d5be 100644 --- a/libIRDB/include/core/icfs.hpp +++ b/libIRDB/include/core/icfs.hpp @@ -21,27 +21,50 @@ class Instruction_t; typedef std::set<Instruction_t*> InstructionSet_t; +typedef enum ICFS_Analysis_Status_t { ICFS_Analysis_Incomplete, ICFS_Analysis_Module_Complete, ICFS_Analysis_Complete } ICFS_Analysis_Status_t; + +// these must match the allowed values for type icfs_analysis_result in Postgres DB +#define ICFS_ANALYSIS_INCOMPLETE_STR "icfs_analysis_incomplete" +#define ICFS_ANALYSIS_MODULE_COMPLETE_STR "icfs_analysis_module_complete" +#define ICFS_ANALYSIS_COMPLETE_STR "icfs_analysis_complete" + // Keep track of instruction control flow sets class ICFS_t : public InstructionSet_t, public BaseObj_t { public: - ICFS_t(): BaseObj_t(NULL), is_complete(false) {} - ICFS_t(db_id_t set_id, bool p_is_complete = false) : BaseObj_t(NULL) - { - SetBaseID(set_id); - SetComplete(p_is_complete); - } + ICFS_t(): BaseObj_t(NULL), m_icfs_analysis_status(ICFS_Analysis_Incomplete) {} + ICFS_t(db_id_t p_set_id, const ICFS_Analysis_Status_t p_status = ICFS_Analysis_Incomplete); + ICFS_t(db_id_t p_set_id, const std::string); std::string WriteToDB(File_t *fid); + ICFS_t& operator=(const InstructionSet_t &p_other); void SetTargets(const InstructionSet_t &other) { InstructionSet_t::operator=(other); } - void SetComplete(bool complete) { is_complete = complete; } - bool IsComplete() const { return is_complete; } + + bool IsIncomplete() const { + return GetAnalysisStatus() == ICFS_Analysis_Incomplete; + } + + bool IsComplete() const { + return GetAnalysisStatus() == ICFS_Analysis_Complete; + } + + bool IsModuleComplete() const { + return GetAnalysisStatus() == ICFS_Analysis_Module_Complete; + } + + void SetAnalysisStatus(const ICFS_Analysis_Status_t p_status) { + m_icfs_analysis_status = p_status; + } + + ICFS_Analysis_Status_t GetAnalysisStatus() const { + return m_icfs_analysis_status; + } private: - bool is_complete; + ICFS_Analysis_Status_t m_icfs_analysis_status; }; typedef std::set<ICFS_t*> ICFSSet_t; diff --git a/libIRDB/src/core/fileir.cpp b/libIRDB/src/core/fileir.cpp index c11c14b8b..32c057c97 100644 --- a/libIRDB/src/core/fileir.cpp +++ b/libIRDB/src/core/fileir.cpp @@ -941,16 +941,9 @@ void FileIR_t::ReadAllICFSFromDB(std::map<db_id_t,Instruction_t*> &addr2instMap, while(!dbintr->IsDone()) { db_id_t icfs_id = atoi(dbintr->GetResultColumn("icfs_id").c_str()); - bool isComplete=false; - string isCompleteString=dbintr->GetResultColumn("is_complete"); - const char *isCompletestr=isCompleteString.c_str(); - if (isCompleteString.size() > 0) - { - if (isCompletestr[0] == 't' || isCompletestr[0] == 'T' || isCompletestr[0] == '1' || isCompletestr[0] == 'y' || isCompletestr[0] == 'Y') - isComplete = true; - } + string statusString=dbintr->GetResultColumn("icfs_status"); - ICFS_t* icfs = new ICFS_t(icfs_id, isComplete); + ICFS_t* icfs = new ICFS_t(icfs_id, statusString); GetAllICFS().insert(icfs); icfsMap[icfs_id] = icfs; @@ -972,8 +965,11 @@ void FileIR_t::ReadAllICFSFromDB(std::map<db_id_t,Instruction_t*> &addr2instMap, { db_id_t address_id = atoi(dbintr->GetResultColumn("address_id").c_str()); Instruction_t* instruction = addr2instMap[address_id]; - if (instruction) + if (instruction) + { icfs->insert(instruction); + } + // @todo: handle cross-file addresses // these are allowed by the DB schema but we don't yet handle them // if we encounter an unresolved address, we should mark the ICFS diff --git a/libIRDB/src/core/icfs.cpp b/libIRDB/src/core/icfs.cpp index 9f72f37d1..a81b5469e 100644 --- a/libIRDB/src/core/icfs.cpp +++ b/libIRDB/src/core/icfs.cpp @@ -24,19 +24,60 @@ using namespace libIRDB; using namespace std; +static string getICFSAnalysisStatus(const ICFS_Analysis_Status_t p_status) { + // strings must match DB definition + switch (p_status) { + case ICFS_Analysis_Incomplete: + return string("icfs_analysis_incomplete"); + break; + case ICFS_Analysis_Module_Complete: + return string("icfs_analysis_module_complete"); + break; + case ICFS_Analysis_Complete: + return string("icfs_analysis_complete"); + break; + default: + return string("icfs_analysis_incomplete"); + break; + } + + std::cerr << "error: unknown ICFS analysis status: " << p_status << std::endl; + assert(0); +} + +ICFS_t::ICFS_t(db_id_t p_set_id, const ICFS_Analysis_Status_t p_status) : BaseObj_t(NULL) +{ + SetBaseID(p_set_id); + SetAnalysisStatus(p_status); +} + +ICFS_t::ICFS_t(db_id_t p_set_id, const string p_statusString) : BaseObj_t(NULL) +{ + SetBaseID(p_set_id); + if (p_statusString == "icfs_analysis_incomplete") { + SetAnalysisStatus(ICFS_Analysis_Incomplete); + } else if (p_statusString == "icfs_analysis_module_complete") { + SetAnalysisStatus(ICFS_Analysis_Module_Complete); + } else if (p_statusString == "icfs_analysis_complete") { + SetAnalysisStatus(ICFS_Analysis_Complete); + } else { + std::cerr << "error: unknown ICFS analysis status string: " << p_statusString << std::endl; + assert(0); + } +} + string ICFS_t::WriteToDB(File_t *fid) { assert(fid); db_id_t icfs_id = GetBaseID(); - // one of the many postgres encoding of boolean values - string complete = IsComplete() ? "t" : "f"; + string analysis_status = getICFSAnalysisStatus(GetAnalysisStatus()); string q=string("insert into ") + fid->icfs_table_name + - string(" (icfs_id, is_complete) VALUES (") + + string(" (icfs_id, icfs_status) VALUES (") + string("'") + to_string(icfs_id) + string("', ") + - string("'") + complete + string("'); ") ; + string("'") + analysis_status + string("'); ") ; for (InstructionSet_t::const_iterator it = this->begin(); it != this->end(); ++it) diff --git a/libIRDB/test/fill_in_indtargs.cpp b/libIRDB/test/fill_in_indtargs.cpp index 1543dae19..a1c183f9c 100644 --- a/libIRDB/test/fill_in_indtargs.cpp +++ b/libIRDB/test/fill_in_indtargs.cpp @@ -29,12 +29,10 @@ #include <sys/mman.h> #include <sys/types.h> #include <regex.h> -// #include <elf.h> #include <ctype.h> #include <list> #include <stdio.h> - #include <exeio.h> #include "beaengine/BeaEngine.h" #include "check_thunks.hpp" @@ -253,7 +251,7 @@ void mark_jmptables(FileIR_t *firp) assert(instruction_targets.size() > 0); - ICFS_t* new_icfs = new ICFS_t(next_icfs_set_id++, true); + ICFS_t* new_icfs = new ICFS_t(next_icfs_set_id++, ICFS_Analysis_Complete); new_icfs->SetTargets(instruction_targets); firp->GetAllICFS().insert(new_icfs); @@ -264,6 +262,57 @@ void mark_jmptables(FileIR_t *firp) } } +bool allTargetsIndirectlyCalledFunctions(Instruction_t *instr) +{ + if (!instr->GetIBTargets()) + return false; + + ICFS_t *targets = instr->GetIBTargets(); + for(set<Instruction_t*>::const_iterator it=targets->begin(); + it!=targets->end(); ++it) + { + Instruction_t *insn = *it; + if (!insn->GetFunction()) + return false; + else if (!insn->GetFunction()->GetEntryPoint()) + return false; + /* NB: current API assumes only 1 entry point per function */ + else if (insn->GetFunction()->GetEntryPoint() != insn) + return false; + } + + return true; +} + +/* + pre: some ib targets may be incomplete + post: all icfs are either module_complete or complete +*/ +void patch_icfs(FileIR_t *firp) +{ + for (set<Instruction_t*>::const_iterator it=firp->GetInstructions().begin(); + it!=firp->GetInstructions().end(); + ++it) + { + Instruction_t* instr = *it; + + if(instr->GetIBTargets() && !( + instr->GetIBTargets()->IsComplete() || + instr->GetIBTargets()->IsModuleComplete()) ) + { + assert(instr->GetIBTargets()->IsIncomplete()); + + if (allTargetsIndirectlyCalledFunctions(instr)) { + cerr << "ib targets for: " << instr->getDisassembly() << " reassigned to indirectcalls node" << endl; + instr->SetIBTargets(indirect_calls); + } else { + cerr << "ib targets for: " << instr->getDisassembly() << " reassigned to hellnode" << endl; + instr->SetIBTargets(hellnode_tgts); + } + } + } +} + void mark_targets(FileIR_t *firp) { for( @@ -1431,8 +1480,6 @@ template <class T> T MAX(T a, T b) void icfs_init(FileIR_t* firp) { - - assert(firp); db_id_t max_id=0; for(ICFSSet_t::iterator it=firp->GetAllICFS().begin(); it!=firp->GetAllICFS().end(); ++it) @@ -1441,8 +1488,8 @@ void icfs_init(FileIR_t* firp) } next_icfs_set_id = max_id+1; cerr<<"Found max ICFS id=="<<max_id<<endl; - hellnode_tgts = new ICFS_t(next_icfs_set_id++, false); - indirect_calls = new ICFS_t(next_icfs_set_id++, false); + hellnode_tgts = new ICFS_t(next_icfs_set_id++, ICFS_Analysis_Module_Complete); + indirect_calls = new ICFS_t(next_icfs_set_id++, ICFS_Analysis_Module_Complete); firp->GetAllICFS().insert(hellnode_tgts); firp->GetAllICFS().insert(indirect_calls); } @@ -1496,7 +1543,6 @@ void check_for_ret(FileIR_t* const firp, Instruction_t* const insn) if(strstr(d.Instruction.Mnemonic, "ret")==NULL) return; - // already analysed by ida. if(insn->GetIBTargets() && insn->GetIBTargets()->IsComplete()) return; @@ -1704,12 +1750,14 @@ void fill_in_indtargs(FileIR_t* firp, exeio* elfiop, std::list<virtual_offset_t> mark_jmptables(firp); + patch_icfs(firp); + for(ICFSSet_t::const_iterator it=firp->GetAllICFS().begin(); it != firp->GetAllICFS().end(); ++it) { ICFS_t *icfs = *it; - cout << dec << "icfs set id: " << icfs->GetBaseID() << " #ibtargets: " << icfs->size() << endl; + cout << dec << "icfs set id: " << icfs->GetBaseID() << " #ibtargets: " << icfs->size() << " analysis status: " << icfs->GetAnalysisStatus() << endl; } } diff --git a/libIRDB/test/read_variantir.cpp b/libIRDB/test/read_variantir.cpp index 6248cbbe6..912ebee03 100644 --- a/libIRDB/test/read_variantir.cpp +++ b/libIRDB/test/read_variantir.cpp @@ -106,7 +106,7 @@ main(int argc, char* argv[]) ++it) { ICFS_t *icfs = *it; - cout << "icfs set id: " << icfs->GetBaseID() << " complete: " << icfs->IsComplete() << " #ibtargets: " << dec << icfs->size() << " | "; + cout << "icfs set id: " << dec << icfs->GetBaseID() << " analysis status: " << icfs->GetAnalysisStatus() << " #ibtargets: " << icfs->size() << " | "; int count = 0; for(ICFS_t::const_iterator it2=icfs->begin(); it2!=icfs->end(); ++it2, ++count) diff --git a/libIRDB/test/tests/ibtarget/Makefile b/libIRDB/test/tests/ibtarget/Makefile index 4ed7b3e9b..7cc34a278 100644 --- a/libIRDB/test/tests/ibtarget/Makefile +++ b/libIRDB/test/tests/ibtarget/Makefile @@ -3,18 +3,20 @@ INCLUDES= -I../../../../include -I../../../include/ -I../../../../beaengine/incl #OPT=-fPIE -fPIC -O #OPT=-g -fPIE -fPIC OPT=-O -.SUFFIXES: .exe .cpp .peasoup +.SUFFIXES: .exe .cpp .protected PROGS=jmp1.exe jmp2.exe jmp.shared.exe icall.exe -PEASOUP_PROGS=jmp1.peasoup jmp2.peasoup jmp.shared.peasoup icall.peasoup +PEASOUP_PROGS=jmp1.protected jmp2.protected jmp.shared.protected icall.protected all: $(PROGS) -peasoup: $(PEASOUP_PROGS) +protected: $(PEASOUP_PROGS) $(PROGS): ../../../../lib/* -.exe.peasoup: $< - LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." $(PEASOUP_HOME)/tools/ps_analyze.sh $< $@ --backend zipr --step meds_static=off +.exe.protected: $< +# FIX_CALLS_FIX_ALL_CALLS=1 LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." $(PEASOUP_HOME)/tools/ps_analyze.sh $< $@ --backend zipr --step selective_cfi=on + FIX_CALLS_FIX_ALL_CALLS=1 LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." $(PEASOUP_HOME)/tools/ps_analyze.sh $< $@ --backend zipr --step selective_cfi=on --step-option selective_cfi:--color +# LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." $(PEASOUP_HOME)/tools/ps_analyze.sh $< $@ --backend zipr --stop_after pdb_register .o.exe: $< ../lib/libIRDB-core.a ../lib/libIRDB-cfg.a g++ $< $(INCLUDES) $(LIBS) $(OPT) -o $@ @@ -32,4 +34,4 @@ jmp.shared.exe: jmp.shared.so jmp.main.cpp g++ jmp.main.o -L. -ljmp -o $@ clean: - rm -fr $(PROGS) *.o *.so peasoup_executable* *.peasoup + rm -fr $(PROGS) *.o *.so peasoup_executable* *.protected *.tmp.orig diff --git a/libIRDB/test/tests/ibtarget/validate_ibts.sh b/libIRDB/test/tests/ibtarget/validate_ibts.sh new file mode 100755 index 000000000..f95b2cd9d --- /dev/null +++ b/libIRDB/test/tests/ibtarget/validate_ibts.sh @@ -0,0 +1,38 @@ +# build original and protected test programs +make clean all protected +if [ ! $? -eq 0 ]; then + echo "failed to compile test programs" + exit 1 +fi + +export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:." + +ALL_PASS_STATUS=0 +# compare output +for i in `ls *.exe` +do + orig=$i + base=$(basename $orig .exe) + protected=${base}.protected + + echo $orig $base $protected + ./$orig 1 2 > $$.tmp.orig + ./$protected 1 2 > $$.tmp.protected + diff $$.tmp.orig $$.tmp.protected + if [ ! $? -eq 0 ]; then + echo "FAIL: Protected program ${base}.protected does not match original program" + ALL_PASS_STATUS=1 + else + echo "PASS: $orig vs. $protected" + fi + + rm $$.tmp.orig $$.tmp.protected +done + +echo +if [ $ALL_PASS_STATUS -eq 0 ]; then + echo "PASS: all tests passed" +else + echo "FAIL: one or more test failed" +fi + diff --git a/tools/meds2pdb/meds2pdb.cpp b/tools/meds2pdb/meds2pdb.cpp index 1c56ea279..a74510bf5 100644 --- a/tools/meds2pdb/meds2pdb.cpp +++ b/tools/meds2pdb/meds2pdb.cpp @@ -1,4 +1,3 @@ -char temp; /* * Copyright (c) 2014 - Zephyr Software LLC * @@ -96,20 +95,25 @@ void insert_icfs(int fileID, const vector<wahoo::Instruction*>& instructions) for (int i = 0; i < instructions.size(); i ++) { - wahoo::Instruction *instruction = instructions[i]; + wahoo::Instruction *instruction = instructions[i]; assert(instruction); const std::set<Instruction*> &ibts=instruction->getIBTs(); if(ibts.size()==0) continue; - cerr<<"Found fromIB=="<<instruction->getAddress()<<endl; + cerr<<"Found fromIB=="<<hex<<instruction->getAddress()<<endl; string query = "INSERT INTO " + icfsTable; - query += " (icfs_id,is_complete) VALUES "; + query += " (icfs_id,icfs_status) VALUES "; query += "("; query += txn.quote(next_icfs_id) + ","; - query += txn.quote(instruction->isIbComplete()); + + if (instruction->isIbComplete()) + query += txn.quote(ICFS_ANALYSIS_COMPLETE_STR); + else + query += txn.quote(ICFS_ANALYSIS_INCOMPLETE_STR); + query += ")"; string query2 = "INSERT INTO " + icfsMapTable; @@ -117,7 +121,7 @@ void insert_icfs(int fileID, const vector<wahoo::Instruction*>& instructions) for(set<Instruction*>::iterator it=ibts.begin(); it!=ibts.end(); it++) { - cerr<<" Found toIBT=="<<(*it)->getAddress()<<endl; + cerr<<" Found toIBT=="<<hex<<(*it)->getAddress()<<endl; if(it!=ibts.begin()) query2+=","; int target_address_id=instruction_to_addressid_map[*it]; @@ -127,10 +131,22 @@ void insert_icfs(int fileID, const vector<wahoo::Instruction*>& instructions) query2 += ")"; } - + + // update icfs_id entry in instruction + app_iaddr_t instruction_id = address_to_instructionid_map[instruction->getAddress()]; + string query3 = "UPDATE " + instructionTable; + query3 += " SET icfs_id="; + query3 += txn.quote(next_icfs_id); + query3 += " WHERE instruction_id="; + query3 += txn.quote(instruction_id); + query+=";"; query2+=";"; - txn.exec(query+query2); + query3+=";"; + + txn.exec(query+query2+query3); + + cerr<<query+query2+query3<<endl; next_icfs_id++; } cerr<<"Finished inserting ICFS into IR."<<endl; @@ -571,6 +587,7 @@ int main(int argc, char **argv) insert_functions(fileID, functions); insert_instructions(fileID, instructions, functions); update_functions(fileID, functions); + insert_icfs(fileID, instructions); // add function prototype information to the IRDB diff --git a/tools/transforms/Rewrite_Utility.cpp b/tools/transforms/Rewrite_Utility.cpp index fac8f7cad..e162173be 100644 --- a/tools/transforms/Rewrite_Utility.cpp +++ b/tools/transforms/Rewrite_Utility.cpp @@ -45,6 +45,7 @@ Instruction_t* insertAssemblyBefore(FileIR_t* virp, Instruction_t* first, string //"Null" out the original address (it should be as if the instruction was not in the database). first->SetOriginalAddressID(BaseObj_t::NOT_IN_DATABASE); first->GetRelocations().clear(); + first->SetIBTargets(NULL); virp->ChangeRegistryKey(first,next); setInstructionAssembly(virp,first,assembly,next,target); @@ -134,6 +135,7 @@ void copyInstruction(Instruction_t* src, Instruction_t* dest) dest->SetCallback(src->GetCallback()); dest->SetFallthrough(src->GetFallthrough()); dest->SetTarget(src->GetTarget()); + dest->SetIBTargets(src->GetIBTargets()); dest->GetRelocations()=src->GetRelocations(); } diff --git a/xform/rewriter.cpp b/xform/rewriter.cpp index e659756f1..dac766a71 100644 --- a/xform/rewriter.cpp +++ b/xform/rewriter.cpp @@ -631,6 +631,7 @@ void Rewriter::readXrefsFile(char p_filename[]) addr_to_insn_map[instr->getAddress()]=instr; } + set<app_iaddr_t> completeIBT; FILE* fin=fopen(p_filename, "r"); @@ -698,20 +699,25 @@ void Rewriter::readXrefsFile(char p_filename[]) from_instr->addIBT(instr); } } - else - { - wahoo::Instruction *instr = addr_to_insn_map[addr]; - assert(instr); - instr->markIbComplete(); - } } // check for instr xref fromib else if(string("FROMIB")==string(ibt)) { - cerr<<"FROMIB line ignored for now."<<endl; - } - + // annotations can come in any order so the COMPLETE annotation for IB targets + // can come before/after the targets themselves + // in this loop, just keep track of instructions w/ complete targets + // 4004b6 1 INSTR XREF FROMIB COMPLETE 1 + char complete[200]; + fscanf(fin, "%s", complete); + + if(strcmp(complete,"COMPLETE")==0) + { + completeIBT.insert(addr); + } + if(feof(fin)) // deal with blank lines at the EOF + break; + } char remainder[2000]; fgets(remainder, sizeof(remainder), fin); @@ -719,9 +725,17 @@ void Rewriter::readXrefsFile(char p_filename[]) } while(!feof(fin)); - fclose(fin); + // let's backpatch all IB instructions with complete targets + set<app_iaddr_t>::const_iterator it; + for (it = completeIBT.begin(); it != completeIBT.end(); ++it) + { + wahoo::Instruction *instr = addr_to_insn_map[*it]; + assert(instr); + instr->markIbComplete(); + } + fclose(fin); } /* -- GitLab