diff --git a/libIRDB/test/fill_in_indtargs.cpp b/libIRDB/test/fill_in_indtargs.cpp index 5baa796659e2f12ec369b5ddc6d49fdf136912d2..73878ceaeefc873e86e74f0bd1441d5445b96f74 100644 --- a/libIRDB/test/fill_in_indtargs.cpp +++ b/libIRDB/test/fill_in_indtargs.cpp @@ -138,7 +138,7 @@ ELFIO::section* find_section(int addr, ELFIO::elfio *elfiop) assert(pSec); if(pSec->get_address() > addr) continue; - if(addr > pSec->get_address()+pSec->get_size()) + if(addr >= pSec->get_address()+pSec->get_size()) continue; return pSec; @@ -483,7 +483,8 @@ cout<<hex<<"Found switch dispatch at "<<I3->GetAddress()->GetVirtualOffset()<< " /* did we finish the loop or break out? */ if(i==3) { - cout<<"Found switch table (thunk-relative) at "<<hex<<table_base+table_offset<<endl; + if(getenv("VERBOSE")!=0) + cout<<"Found switch table (thunk-relative) at "<<hex<<table_base+table_offset<<endl; // finished the loop. for(i=0;true;i++) { @@ -493,13 +494,17 @@ cout<<hex<<"Found switch dispatch at "<<I3->GetAddress()->GetVirtualOffset()<< " const int *table_entry_ptr=(const int*)&(secdata[offset+i*4]); int table_entry=*table_entry_ptr; - cout<<"Found switch table (thunk-relative) entry["<<dec<<i<<"], "<<hex<<thunk_base+table_entry<<endl; + if(getenv("VERBOSE")!=0) + cout<<"Found switch table (thunk-relative) entry["<<dec<<i<<"], "<<hex<<thunk_base+table_entry<<endl; if(!possible_target(thunk_base+table_entry,table_base+i*4)) break; } } else - cout<<"Found that "<<hex<<table_base+table_offset<<endl; + { + if(getenv("VERBOSE")!=0) + cout<<"Found that "<<hex<<table_base+table_offset<<endl; + } // now, try next thunk base } diff --git a/tools/transforms/PNMain.cpp b/tools/transforms/PNMain.cpp index 3513fb85a7edc100768332c4d8cd8364f35f6c6b..6188ff529d1b1c54df01f330dc73f86c67153d16 100644 --- a/tools/transforms/PNMain.cpp +++ b/tools/transforms/PNMain.cpp @@ -306,7 +306,7 @@ int main(int argc, char **argv) try { - PNTransformDriver transform_driver(pidp,BED_script); + PNTransformDriver transform_driver(pidp,BED_script, &pqxx_interface); OffsetInference *offset_inference = new OffsetInference(); diff --git a/tools/transforms/PNTransformDriver.cpp b/tools/transforms/PNTransformDriver.cpp index dc0eb33b16be6cdd137f67c255dd75b2e299b6a4..80b297fe6f84b7831842bc091cfbe8853450f249 100644 --- a/tools/transforms/PNTransformDriver.cpp +++ b/tools/transforms/PNTransformDriver.cpp @@ -48,8 +48,8 @@ static bool CompareValidationRecordAscending(validation_record a, validation_rec } -PNTransformDriver::PNTransformDriver(VariantID_t *pidp,string BED_script) : - pn_regex(NULL) +PNTransformDriver::PNTransformDriver(VariantID_t *pidp,string BED_script, pqxxDB_t *pqxx_if) : + pn_regex(NULL), pqxx_interface(pqxx_if) { //TODO: throw exception? assert(pidp != NULL); @@ -485,6 +485,24 @@ void PNTransformDriver::GenerateTransformsInit() } +void PNTransformDriver::InitNewFileIR(File_t* this_file) +{ + //Always modify a.ncexe first. This is assumed to be what the constructor of FileIR_t will return if + //the variant ID is used alone as the parameter to the constructor. + orig_virp = new FileIR_t(*pidp, this_file); + assert(orig_virp && pidp); + + int elfoid=orig_virp->GetFile()->GetELFOID(); + pqxx::largeobject lo(elfoid); + lo.to_file(pqxx_interface->GetTransaction(),"readeh_tmp_file.exe"); + + elfiop=new ELFIO::elfio; + elfiop->load("readeh_tmp_file.exe"); + + ELFIO::dump::header(cout,*elfiop); + ELFIO::dump::section_headers(cout,*elfiop); +} + void PNTransformDriver::GenerateTransforms() @@ -509,11 +527,8 @@ void PNTransformDriver::GenerateTransforms() //name, something to consider //--------------------a.ncexe protection----------------------- + InitNewFileIR(NULL); - //Always modify a.ncexe first. This is assumed to be what the constructor of FileIR_t will return if - //the variant ID is used alone as the parameter to the constructor. - orig_virp = new FileIR_t(*pidp); - assert(orig_virp && pidp); // now that we've loaded the FileIR, we can init the reg expressions needed for this object. pn_regex=new PNRegularExpressions; @@ -554,8 +569,21 @@ void PNTransformDriver::GenerateTransforms() continue; // read the db - orig_virp=new FileIR_t(*pidp,this_file); - assert(orig_virp && pidp); + InitNewFileIR(this_file); +#if 0 +orig_virp=new FileIR_t(*pidp,this_file); +assert(orig_virp && pidp); +int elfoid=firp->GetFile()->GetELFOID(); +pqxx::largeobject lo(elfoid); +lo.to_file(pqxx_interface.GetTransaction(),"readeh_tmp_file.exe"); + +ELFIO::elfio* elfiop=new ELFIO::elfio; +elfiop->load("readeh_tmp_file.exe"); + +ELFIO::dump::header(cout,*elfiop); +ELFIO::dump::section_headers(cout,*elfiop); +#endif + map<string,double> file_coverage_map; @@ -683,7 +711,7 @@ bool check_for_push_pop_coherence(Function_t *func) // count pushes in the prologue int prologue_pushes=count_prologue_pushes(func); -cerr<<"Found "<<prologue_pushes<<" pushes in "<<func->GetName()<<endl; +//cerr<<"Found "<<prologue_pushes<<" pushes in "<<func->GetName()<<endl; // keep a map that keeps the count of pops for each function exit. map<Instruction_t*, int> pop_count_per_exit; @@ -706,7 +734,7 @@ cerr<<"Found "<<prologue_pushes<<" pushes in "<<func->GetName()<<endl; { DISASM d2; exit_insn->Disassemble(d2); -cerr<<"Found exit insn ("<< d2.CompleteInstr << ") for pop ("<< d.CompleteInstr << ")"<<endl; +//cerr<<"Found exit insn ("<< d2.CompleteInstr << ") for pop ("<< d.CompleteInstr << ")"<<endl; map<Instruction_t*, int>::iterator mit; mit=pop_count_per_exit.find(exit_insn); if(mit == pop_count_per_exit.end()) // not found @@ -716,7 +744,7 @@ cerr<<"Found exit insn ("<< d2.CompleteInstr << ") for pop ("<< d.CompleteInstr } else { -cerr<<"Could not find exit insn for pop ("<< d.CompleteInstr << ")"<<endl; +//cerr<<"Could not find exit insn for pop ("<< d.CompleteInstr << ")"<<endl; } } } @@ -738,7 +766,7 @@ cerr<<"Could not find exit insn for pop ("<< d.CompleteInstr << ")"<<endl; DISASM d; insn->Disassemble(d); -cerr<<"Found "<<map_pair.second<<" pops in exit: \""<< d.CompleteInstr <<"\" func:"<<func->GetName()<<endl; +//cerr<<"Found "<<map_pair.second<<" pops in exit: \""<< d.CompleteInstr <<"\" func:"<<func->GetName()<<endl; // do the check if(prologue_pushes != map_pair.second) @@ -751,6 +779,103 @@ cerr<<"Found "<<map_pair.second<<" pops in exit: \""<< d.CompleteInstr <<"\" fun return true; } + +static ELFIO::section* find_section(unsigned int addr, ELFIO::elfio *elfiop) +{ + for ( int i = 0; i < elfiop->sections.size(); ++i ) + { + ELFIO::section* pSec = elfiop->sections[i]; + assert(pSec); + if(pSec->get_address() > addr) + continue; + if(addr >= pSec->get_address()+pSec->get_size()) + continue; + + return pSec; + } + return NULL; +} + +bool PNTransformDriver::check_jump_tables(Instruction_t* insn) +{ + /* quick check to skip most instructions */ + if(insn->GetTarget() || insn->GetFallthrough()) + return true; + + DISASM d; + insn->Disassemble(d); + + /* only look at jumps */ + int branch_type = d.Instruction.BranchType; + if(branch_type!=JmpType) + return true; + + /* make sure this is an indirect branch */ + if((d.Argument1.ArgType&MEMORY_TYPE)==0) + return true; + + if(d.Argument1.Memory.Scale<4) + return true; + + int displacement=d.Argument1.Memory.Displacement; + + ELFIO::section* pSec=find_section(displacement,elfiop); + + if(!pSec) + return true; + + const char *secdata=pSec->get_data(); + + int offset=displacement-pSec->get_address(); + + set<int> jump_tab_entries; + for(int i=0;i<5;i++) + { + if(offset+i*4+sizeof(int) > pSec->get_size()) + break; + + const int *table_entry_ptr=(const int*)&(secdata[offset+i*4]); + int table_entry=*table_entry_ptr; + + if(table_entry!=0) + { +// cout << "found possible table entry "<<hex<<table_entry<<" from func "<<insn->GetFunction()->GetName()<<endl; + jump_tab_entries.insert(table_entry); + } + + } + + for( + set<Instruction_t*>::const_iterator it=orig_virp->GetInstructions().begin(); + it!=orig_virp->GetInstructions().end(); + ++it + ) + { + Instruction_t* ftinsn=*it;; + AddressID_t* addr=ftinsn->GetAddress(); + int voff=addr->GetVirtualOffset(); + + // check to see if this instruction is in my table + if(jump_tab_entries.find(voff)!=jump_tab_entries.end()) + { + // it is! + + // now, check to see if the instruction is in my function + if(insn->GetFunction()!=ftinsn->GetFunction()) + { + cout<<"Sanitizing function "<< insn->GetFunction()->GetName()<< "due to jump-table to diff-func detected."<<endl; + return false; + } + else + { + // cout<<"Verified that the instruction is in my function, yay!"<<endl; + } + } + } + return true; + +} + void PNTransformDriver::SanitizeFunctions() { //TODO: for now, the sanitized list is only created for an individual IR file @@ -790,6 +915,8 @@ void PNTransformDriver::SanitizeFunctions() continue; */ + // check if there is a fallthrough from this function + // into another function. if so, sanitize both. if(FallthroughFunctionCheck(instr,instr->GetFallthrough())) { //check if instruciton is a call, unconditional jump, or ret @@ -797,10 +924,24 @@ void PNTransformDriver::SanitizeFunctions() //if not, filter the functions int branch_type = disasm.Instruction.BranchType; if(branch_type!=RetType && branch_type!=JmpType && branch_type!=CallType) + // check if instr->GetTarget() (for cond branches) + // exits the function. If so, sanitize both funcs. TargetFunctionCheck(instr,instr->GetTarget()); } + + // if it's not already sanitized + if(sanitized.find(func)==sanitized.end()) + { + // check for push/pop coherence. + if(!check_jump_tables(instr)) + { + jump_table_sanitized++; + sanitized.insert(func); + continue; + } + } } // if it's not already sanitized @@ -1524,6 +1665,7 @@ void PNTransformDriver::Print_Report() cerr<<"Blacklisted Functions \t\t"<<blacklist_funcs<<endl; cerr<<"Sanitized Functions \t\t"<<sanitized_funcs<<endl; cerr<<"Push/Pop Sanitized Functions \t\t"<<push_pop_sanitized_funcs<<endl; + cerr<<"Jump table Sanitized Functions \t\t"<<jump_table_sanitized<<endl; cerr<<"Transformable Functions \t"<<(total_funcs-not_transformable.size())<<endl; cerr<<"Transformed \t\t\t"<<total_transformed<<endl; } diff --git a/tools/transforms/PNTransformDriver.hpp b/tools/transforms/PNTransformDriver.hpp index f3fcb10888c795f7249cde246e7fac5da747158c..fa5fe5b850229b19297da88e168fe1166f56cb44 100644 --- a/tools/transforms/PNTransformDriver.hpp +++ b/tools/transforms/PNTransformDriver.hpp @@ -4,12 +4,15 @@ #include <vector> #include <set> +#include "elfio/elfio.hpp" +#include "elfio/elfio_dump.hpp" #include "PNStackLayoutInference.hpp" #include "PNRegularExpressions.hpp" #include <csignal> #include "Rewrite_Utility.hpp" #include <libIRDB-cfg.hpp> + //TODO: I should use the types defined by beaengine //#define RetType 13 //#define JmpType 11 @@ -41,13 +44,15 @@ struct validation_record class PNTransformDriver { -protected: - libIRDB::VariantID_t *pidp; - libIRDB::FileIR_t *orig_virp; - std::string BED_script; - int orig_progid; - bool do_canaries; - bool do_align; + protected: + + libIRDB::VariantID_t *pidp; + libIRDB::FileIR_t *orig_virp; + ELFIO::elfio* elfiop; + std::string BED_script; + int orig_progid; + bool do_canaries; + bool do_align; //TODO: coverage map should not use function name as the key, since //we may want to support coverage for shared objects. std::map<std::string,std::map<std::string,double> > coverage_map; @@ -55,97 +60,104 @@ protected: double coverage_threshold; bool do_shared_object_protection; - std::vector< std::vector<PNStackLayoutInference*> > transform_hierarchy; - PNRegularExpressions *pn_regex; - std::set<std::string> blacklist; + std::vector< std::vector<PNStackLayoutInference*> > transform_hierarchy; + PNRegularExpressions *pn_regex; + std::set<std::string> blacklist; std::set<libIRDB::Function_t*> sanitized; - std::set<std::string> only_validate_list; - //std::map<libIRDB::Instruction_t*,std::string> undo_list; - //std::map<libIRDB::Instruction_t*,libIRDB::Instruction_t*> undo_list; - std::map<libIRDB::Function_t*, std::map<libIRDB::Instruction_t*,libIRDB::Instruction_t*> > undo_list; - std::map< std::string,std::vector<PNStackLayout*> > transformed_history; - int blacklist_funcs; + std::set<std::string> only_validate_list; + //std::map<libIRDB::Instruction_t*,std::string> undo_list; + //std::map<libIRDB::Instruction_t*,libIRDB::Instruction_t*> undo_list; + std::map<libIRDB::Function_t*, std::map<libIRDB::Instruction_t*,libIRDB::Instruction_t*> > undo_list; + std::map< std::string,std::vector<PNStackLayout*> > transformed_history; + int blacklist_funcs; int sanitized_funcs; int push_pop_sanitized_funcs; - int total_funcs; + int jump_table_sanitized; + int total_funcs; int dynamic_frames; - std::vector<std::string> not_transformable; - std::vector<libIRDB::Function_t*> failed; + std::vector<std::string> not_transformable; + std::vector<libIRDB::Function_t*> failed; std::vector<finalize_record> finalization_registry; std::set<FileIR_t*> registered_firps; int high_coverage_count, low_coverage_count, no_coverage_count, validation_count; - // write stack objects to IRDB - bool write_stack_ir_to_db; - - // virtual bool Rewrite(PNStackLayout *layout, libIRDB::Function_t *func); -// virtual bool LayoutValidation(PNStackLayout *layout); - virtual bool Validate(libIRDB::FileIR_t *virp, std::string name); - //virtual void undo(std::map<libIRDB::Instruction_t*,std::string> undo_list, libIRDB::Function_t *func); - virtual void undo( libIRDB::Function_t *func); - //virtual void reset_undo(std::string func); - virtual std::vector<PNStackLayout*> GenerateInferences(libIRDB::Function_t *func, int level); - virtual bool ShuffleValidation(int reps, PNStackLayout *layout,libIRDB::Function_t *func); + // write stack objects to IRDB + bool write_stack_ir_to_db; + +//virtual bool Rewrite(PNStackLayout *layout, libIRDB::Function_t *func); +//virtual bool LayoutValidation(PNStackLayout *layout); +//virtual void undo(std::map<libIRDB::Instruction_t*,std::string> undo_list, libIRDB::Function_t *func); +//virtual void reset_undo(std::string func); + + virtual bool Validate(libIRDB::FileIR_t *virp, std::string name); + virtual void undo( libIRDB::Function_t *func); + virtual std::vector<PNStackLayout*> GenerateInferences(libIRDB::Function_t *func, int level); + virtual bool ShuffleValidation(int reps, PNStackLayout *layout,libIRDB::Function_t *func); virtual void ShuffleValidation(std::vector<validation_record> &vrs); - //virtual void GenerateTransforms2(libIRDB::FileIR_t *virp,std::vector<libIRDB::Function_t*> funcs,std::string BED_script, int progid); - //virtual bool ValidateLayout(PNStackLayout *layout,std::string BED_script,int progid); -// virtual bool Canary_Rewrite(FileIR_t *virp, PNStackLayout *orig_layout,libIRDB::Function_t *func); - //altered for TNE hack for dyn array padding, assuming all virp is orig_virp - virtual bool Canary_Rewrite( PNStackLayout *orig_layout,libIRDB::Function_t *func); - virtual bool Sans_Canary_Rewrite(PNStackLayout *orig_layout, libIRDB::Function_t *func); - inline bool Instruction_Rewrite(PNStackLayout *layout, libIRDB::Instruction_t *instr, ControlFlowGraph_t* cfg); +//virtual void GenerateTransforms2(libIRDB::FileIR_t *virp,std::vector<libIRDB::Function_t*> funcs,std::string BED_script, int progid); +//virtual bool ValidateLayout(PNStackLayout *layout,std::string BED_script,int progid); +//virtual bool Canary_Rewrite(FileIR_t *virp, PNStackLayout *orig_layout,libIRDB::Function_t *func); + + //altered for TNE hack for dyn array padding, assuming all virp is orig_virp + virtual bool Canary_Rewrite( PNStackLayout *orig_layout,libIRDB::Function_t *func); + virtual bool Sans_Canary_Rewrite(PNStackLayout *orig_layout, libIRDB::Function_t *func); + inline bool Instruction_Rewrite(PNStackLayout *layout, libIRDB::Instruction_t *instr, ControlFlowGraph_t* cfg); inline bool FunctionCheck(libIRDB::Function_t* a, libIRDB::Function_t* b); inline bool TargetFunctionCheck(libIRDB::Instruction_t* a, libIRDB::Instruction_t* b); inline bool FallthroughFunctionCheck(libIRDB::Instruction_t* a, libIRDB::Instruction_t* b); virtual PNStackLayout* Get_Next_Layout(validation_record &vr); - virtual void Print_Report(); - virtual bool CanaryTransformHandler(PNStackLayout *layout, libIRDB::Function_t *func,bool validate); - virtual bool PaddingTransformHandler(PNStackLayout *layout, libIRDB::Function_t *func,bool validate); - virtual bool LayoutRandTransformHandler(PNStackLayout *layout, libIRDB::Function_t *func, bool validate); - virtual void GenerateTransformsInit(); - virtual bool IsBlacklisted(libIRDB::Function_t *func); - virtual unsigned int GetRandomCanary(); + virtual void Print_Report(); + virtual bool CanaryTransformHandler(PNStackLayout *layout, libIRDB::Function_t *func,bool validate); + virtual bool PaddingTransformHandler(PNStackLayout *layout, libIRDB::Function_t *func,bool validate); + virtual bool LayoutRandTransformHandler(PNStackLayout *layout, libIRDB::Function_t *func, bool validate); + virtual void GenerateTransformsInit(); + virtual bool IsBlacklisted(libIRDB::Function_t *func); + virtual unsigned int GetRandomCanary(); virtual void GenerateTransformsHidden(std::map<std::string,double> &file_coverage_map); void SanitizeFunctions(); -// virtual bool WriteToDB(); - virtual bool WriteStackIRToDB(); +//virtual bool WriteToDB(); + virtual bool WriteStackIRToDB(); virtual void Finalize_Transformation(); void Register_Finalized(std::vector<validation_record> &vrs,unsigned int start, int length); bool Validate_Recursive(std::vector<validation_record> &vrs, unsigned int start, int length);//,bool suspect=false); -// bool Validate_Linear(std::vector<validation_record> &vrs, unsigned int start, int length); +//bool Validate_Linear(std::vector<validation_record> &vrs, unsigned int start, int length); // see .cpp int prologue_offset_to_actual_offset(ControlFlowGraph_t* cfg, Instruction_t *instr,int offset); + bool check_jump_tables(Instruction_t* insn); + void InitNewFileIR(File_t* this_file); + + pqxxDB_t *pqxx_interface; public: - static bool timeExpired; - //TODO: use unsigned int? + static bool timeExpired; + //TODO: use unsigned int? - PNTransformDriver(libIRDB::VariantID_t *pidp, std::string BED_script); - virtual ~PNTransformDriver(); - - //Level indicates the priority of the layout when attempting - //the transform. Levels start at 1 (the highest priority). A level less than 1 raises - //an exception (for now an assert will fail). If not level is - //provided, the default is level 1. - virtual void AddInference(PNStackLayoutInference *inference, int level=0); - virtual void AddBlacklist(std::set<std::string> &blacklist); - virtual void AddBlacklistFunction(std::string func_name); - virtual void AddOnlyValidateList(std::set<std::string> &only_validate_list); - virtual void SetDoCanaries(bool do_canaries); - virtual void SetDoAlignStack(bool align_stack); + PNTransformDriver(libIRDB::VariantID_t *pidp, std::string BED_script, pqxxDB_t *pqxx_if); + virtual ~PNTransformDriver(); + + //Level indicates the priority of the layout when attempting + //the transform. Levels start at 1 (the highest priority). A level less than 1 raises + //an exception (for now an assert will fail). If not level is + //provided, the default is level 1. + virtual void AddInference(PNStackLayoutInference *inference, int level=0); + virtual void AddBlacklist(std::set<std::string> &blacklist); + virtual void AddBlacklistFunction(std::string func_name); + virtual void AddOnlyValidateList(std::set<std::string> &only_validate_list); + virtual void SetDoCanaries(bool do_canaries); + virtual void SetDoAlignStack(bool align_stack); virtual void SetCoverageMap(std::map<std::string,std::map<std::string,double> > coverage_map); virtual void SetNoValidationLevel(unsigned int no_validation_level); virtual void SetCoverageThreshold(double threshold); virtual void SetProtectSharedObjects(bool do_protection); - virtual void GenerateTransforms(); - virtual void SetWriteStackIrToDb(bool setting) { write_stack_ir_to_db = setting; } + virtual void GenerateTransforms(); + virtual void SetWriteStackIrToDb(bool setting) { write_stack_ir_to_db = setting; } }; #endif