static void ShowFunctions (BPatch_image *appImage) { BPatch_Vector<BPatch_function *> *vfunctions = appImage->getProcedures (false); cout << PACKAGE_NAME << ": " << vfunctions->size() << " functions found in binary " << endl; unsigned i = 0; while (i < vfunctions->size()) { char name[1024]; BPatch_function *f = (*vfunctions)[i]; f->getName (name, 1024); if (VerboseLevel) { char mname[1024], tname[1024], modname[1024]; f->getMangledName (mname, 1024); f->getTypedName (tname, 1024); f->getModuleName (modname, 1024); cout << " * " << i+1 << " of " << vfunctions->size() << ", Name: " << name << endl << " Mangled Name: " << mname << endl << " Typed Name : " << tname << endl << " Module name : " << modname << endl << " Base address: " << f->getBaseAddr() << endl << " Instrumentable? " << (f->isInstrumentable()?"yes":"no") << endl << " In shared library? " << (f->isSharedLib()?"yes":"no") << endl << " Number of BB: " << getBasicBlocksSize(f) << endl; if (f->isSharedLib()) { char sharedlibname[1024]; BPatch_module *mod = f->getModule(); mod->getFullName (sharedlibname, 1024); cout << " Full library name: " << sharedlibname << endl; } cout << endl; } else { cout << name << endl; } i++; } }
/* Invoked for every signal handler function, adjusts the value of the saved * fault address to its unrelocated counterpart in the CONTEXT structure, * which contains the PC that is used when execution resumes */ void HybridAnalysis::signalHandlerEntryCB2(BPatch_point *point, Address excCtxtAddr) { mal_printf("\nAt signalHandlerEntry2(%lx , %lx)\n", point->getAddress(), (Address)excCtxtAddr); // calculate the offset of the fault address in the EXCEPTION_RECORD CONTEXT *cont= (CONTEXT*)excCtxtAddr; //bogus pointer, but I won't write to it Address pcAddr = excCtxtAddr + (Address)(&(cont->Eip)) - (Address)cont; // set fault address to the unrelocated address of that instruction // and save the PC address in the CONTEXT structure so the exit handler // can read it BPatch_function *func = point->getFunction(); func->setHandlerFaultAddrAddr((Address)pcAddr,true); handlerFunctions[(Address)func->getBaseAddr()].faultPCaddr = pcAddr; }
void handleModule(BPatch_module *mod, const char *name) { char funcname[BUFFER_STRING_LEN]; // get list of all functions std::vector<BPatch_function *>* functions; functions = mod->getProcedures(); // for each function ... for (unsigned i = 0; i < functions->size(); i++) { BPatch_function *function = functions->at(i); function->getName(funcname, BUFFER_STRING_LEN); printf(" FUNC: %s (%lx)\n", funcname, (unsigned long)function->getBaseAddr()); } }
/* Invoked for every signal handler function, adjusts the value of the saved * fault address to its unrelocated counterpart in the EXCEPTION_RECORD */ void HybridAnalysis::signalHandlerEntryCB(BPatch_point *point, Address excRecAddr) { mal_printf("\nAt signalHandlerEntry(%lx , %lx)\n", point->getAddress(), (Address)excRecAddr); stats_.exceptions++; // calculate the offset of the fault address in the EXCEPTION_RECORD EXCEPTION_RECORD record; proc()->lowlevel_process()->readDataSpace( (void*)excRecAddr, sizeof(EXCEPTION_RECORD), &record, true); Address pcAddr = excRecAddr + (Address) &(record.ExceptionAddress) - (Address) &record; // set fault address to the unrelocated address of that instruction BPatch_function *func = point->getFunction(); func->setHandlerFaultAddrAddr((Address)pcAddr,false); handlerFunctions[(Address)func->getBaseAddr()].isInterrupt = (record.ExceptionCode == EXCEPTION_BREAKPOINT); }
static void ShowFunctions (BPatch_image *appImage) { BPatch_Vector<BPatch_function *> *vfunctions = appImage->getProcedures (false); cout << PACKAGE_NAME << ": " << vfunctions->size() << " functions found in binary " << endl; unsigned i = 0; while (i < vfunctions->size()) { char name[1024]; BPatch_function *f = (*vfunctions)[i]; f->getName (name, 1024); char mname[1024], tname[1024], modname[1024]; f->getMangledName (mname, 1024); f->getTypedName (tname, 1024); f->getModuleName (modname, 1024); cout << " * " << i+1 << " of " << vfunctions->size() << ", Name: " << name << endl << " Mangled Name: " << mname << endl << " Typed Name : " << tname << endl << " Module name : " << modname << endl << " Base address: " << f->getBaseAddr() << endl << " Instrumentable? " << (f->isInstrumentable()?"yes":"no") << endl << " In shared library? " << (f->isSharedLib()?"yes":"no") << endl; if (f->isSharedLib()) { //Old Dyninst API < 9.x //char sharedlibname[1024]; //mod->getFullName (sharedlibname, 1024); BPatch_module *mod = f->getModule(); string sharedlibname; sharedlibname = mod->getObject()->name(); cout << " Full library name: " << sharedlibname << endl; } cout << endl; i++; } }
/* If the context of the exception has been changed so that execution * will resume at a new address, parse and instrument the code at that * address; then add a springboard at that address if it is not the * entry point of a function */ void HybridAnalysis::signalHandlerExitCB(BPatch_point *point, void *) { BPatch_function *func = point->getFunction(); std::map<Dyninst::Address, ExceptionDetails>::iterator diter = handlerFunctions.find((Address)func->getBaseAddr()); assert(handlerFunctions.end() != diter && 0 != diter->second.faultPCaddr); Address pcLoc = diter->second.faultPCaddr; mal_printf("\nAt signalHandlerExit(%lx)\n", point->getAddress()); // figure out the address the program will resume at by reading // in the stored CONTEXT structure Address resumePC; assert(sizeof(Address) == proc()->getAddressWidth()); proc()->lowlevel_process()->readDataSpace( (void*)pcLoc, sizeof(resumePC), &resumePC, true); if (diter->second.isInterrupt) { resumePC += 1; } // parse at the resumePC address, if necessary vector<BPatch_function *> funcs; proc()->findFunctionsByAddr((Address)resumePC,funcs); if (funcs.empty()) { mal_printf("Program will resume in new function at %lx\n", resumePC); } else { mal_printf("Program will resume at %lx in %d existing functions, " "will add shared function starting at %lx\n", resumePC, funcs.size(), resumePC); } analyzeNewFunction(point, (Address)resumePC, true, true); mal_printf("Exception handler exiting at %lx will resume execution at " "%lx %s[%d]\n", point->getAddress(), resumePC, FILE__,__LINE__); }
void HybridAnalysis::badTransferCB(BPatch_point *point, void *returnValue) { Address pointAddr = (Address) point->getAddress(); Address target = (Address) returnValue; time_t tstruct; struct tm * tmstruct; char timeStr[64]; time( &tstruct ); tmstruct = localtime( &tstruct ); strftime(timeStr, 64, "%X", tmstruct); mal_printf("badTransferCB %lx=>%lx %s\n\n", pointAddr, target, timeStr); BPatch_module * targMod = proc()->findModuleByAddr(target); if (!targMod) { mal_printf( "ERROR, NO MODULE for target addr %lx %s[%d]\n", target,FILE__,__LINE__); assert(0); } if (targMod == point->getFunction()->getModule() && targMod->isSystemLib()) { return; } // 1. the target address is in a shared library if ( targMod != point->getFunction()->getModule()) { // process the edge, decide if we should instrument target function bool doMoreProcessing = processInterModuleEdge(point, target, targMod); if (!doMoreProcessing) { return; } } // 2. the point is a call: if (point->getPointType() == BPatch_subroutine) { proc()->beginInsertionSet(); // if the target is in the body of an existing function we'll split // the function and wind up with two or more functions that share // the target address, so make sure we're not in the middle of an // overwrite loop; if we are, check for overwrites immediately BPatch_function *targFunc = proc()->findFunctionByEntry(target); vector<BPatch_function*> targFuncs; proc()->findFunctionsByAddr(target, targFuncs); if (!targFunc && targFuncs.size()) { mal_printf("discovery instr. got new entry point for func\n"); std::set<HybridAnalysisOW::owLoop*> loops; for (unsigned tidx=0; tidx < targFuncs.size(); tidx++) { BPatch_function *curFunc = targFuncs[tidx]; if ( hybridOW()->hasLoopInstrumentation(false, *curFunc, &loops) ) { /* Code sharing will change the loops, the appropriate response is to trigger early exit analysis and remove the loops if the underlying code hasn't changed */ mal_printf("[%d] Removing loop instrumentation for func %lx\n", __LINE__,curFunc->getBaseAddr()); std::set<HybridAnalysisOW::owLoop*>::iterator lIter = loops.begin(); while (lIter != loops.end()) { hybridOW()->deleteLoop(*lIter,false); lIter++; } } } } // 2.1 if the target is new, parse at the target if ( ! targFunc ) { mal_printf("stopThread instrumentation found call %lx=>%lx, " "parsing at call target %s[%d]\n", (long)point->getAddress(), target,FILE__,__LINE__); if (!analyzeNewFunction( point,target,false,false )) { //this happens for some single-instruction functions mal_printf("ERROR: parse of call target %lx=>%lx failed %s[%d]\n", (long)point->getAddress(), target, FILE__,__LINE__); assert(0); instrumentModules(false); proc()->finalizeInsertionSet(false); return; } targFunc = proc()->findFunctionByEntry(target); } // 2.2 if the target is a returning function, parse at the fallthrough bool instrument = true; if ( ParseAPI::RETURN == targFunc->lowlevel_func()->ifunc()->retstatus() ) { //mal_printf("stopThread instrumentation found returning call %lx=>%lx, " // "parsing after call site\n", // (long)point->getAddress(), target); if (parseAfterCallAndInstrument(point, targFunc, false)) { instrument = false; } } if (instrument) { instrumentModules(false); } proc()->finalizeInsertionSet(false); // 2. return return; } // 3. the point is a return instruction: if ( point->getPointType() == BPatch_locExit ) { // 3.2 find the call point so we can parse after it // ( In this case "point" is the return statement and // "target" is the fallthrough address of the call insn ) // in order to find the callPoint in the caller function that // corresponds to the non-returning call, we traverse list of // the caller's points to find the callpoint that is nearest // to the return address Address returnAddr = target; using namespace ParseAPI; // Find the call blocks preceding the address that we're returning // past, but only set set returningCallB if we can be sure that // that we've found a call block that actually called the function // we're returning from pair<Block*, Address> returningCallB((Block*)NULL,0); set<Block*> callBlocks; getCallBlocks(returnAddr, point->llpoint()->func(), point->llpoint()->block(), returningCallB, callBlocks); // 3.2.1 parse at returnAddr as the fallthrough of the preceding // call block, if there is one if (!callBlocks.empty()) { // we don't know if the function we called returns, so // invoke parseAfterCallAndInstrument with NULL as the // called func, so we won't try to parse after other // callers to the called func, as it may not actually // return in a normal fashion if (NULL == returningCallB.first) { vector<BPatch_point*> callPts; for (set<Block*>::iterator bit = callBlocks.begin(); bit != callBlocks.end(); bit++) { getPreCallPoints(*bit, proc(), callPts); } for (vector<BPatch_point*>::iterator pit = callPts.begin(); pit != callPts.end(); pit++) { parseAfterCallAndInstrument( *pit, NULL, true ); } } // if the return address has been parsed as the entry point // of a block, patch post-call areas and return else if ( returningCallB.first->obj()->findBlockByEntry( returningCallB.first->region(), target)) { vector<BPatch_point*> callPts; getPreCallPoints(returningCallB.first, proc(), callPts); for (unsigned j=0; j < callPts.size(); j++) { callPts[j]->patchPostCallArea(); } } else { // parse at the call fallthrough // find one callPoint, any other ones will // be found by parseAfterCallAndInstrument vector<BPatch_point*> callPoints; getPreCallPoints(returningCallB.first, proc(), callPoints); assert(!callPoints.empty()); mal_printf("stopThread instrumentation found return at %lx, " "parsing return addr %lx as fallthrough of call " "instruction at %lx %s[%d]\n", (long)point->getAddress(), target,callPoints[0]->getAddress(),FILE__,__LINE__); if (point->llpoint()->block()->llb()->isShared()) { // because of pc emulation, if the return point is shared, // we may have flipped between functions that share the // return point, so use the call target function BPatch_function *calledFunc = proc()-> findFunctionByEntry(returningCallB.second); parseAfterCallAndInstrument( callPoints[0], calledFunc, true ); } else { parseAfterCallAndInstrument( callPoints[0], point->getFunction(), true); } } } // 3.2.2 no call blocks, parse the return addr as a new function else { if ( point->getFunction()->getModule()->isExploratoryModeOn() ) { // otherwise we've instrumented a function in trusted library // because we want to catch its callbacks into our code, but in // the process are catching calls into other modules mal_printf("hybridCallbacks.C[%d] Observed abuse of normal return " "instruction semantics for insn at %lx target %lx\n", __LINE__, point->getAddress(), returnAddr); } analyzeNewFunction( point, returnAddr, true , true ); // there are no call blocks, so we don't have any post-call pads to patch } // 3. return return; } // 4. else case: the point is a jump/branch proc()->beginInsertionSet(); // 4.1 if the point is a direct branch, remove any instrumentation if (!point->isDynamic()) { BPatch_function *func = point->getFunction(); if (instrumentedFuncs->end() != instrumentedFuncs->find(func) && (*instrumentedFuncs)[func]->end() != (*instrumentedFuncs)[func]->find(point)) { proc()->deleteSnippet( (*(*instrumentedFuncs)[func])[point] ); (*instrumentedFuncs)[func]->erase(point); } //point is set to resolved in handleStopThread } bool newParsing; vector<BPatch_function*> targFuncs; proc()->findFunctionsByAddr(target, targFuncs); if ( 0 == targFuncs.size() ) { newParsing = true; mal_printf("stopThread instrumentation found jump " "at 0x%lx leading to an unparsed target at 0x%lx\n", (long)point->getAddress(), target); } else { newParsing = false; mal_printf("stopThread instrumentation added an edge for jump " " at 0x%lx leading to a previously parsed target at 0x%lx\n", (long)point->getAddress(), target); } // add the new edge to the program, parseNewEdgeInFunction will figure // out whether to extend the current function or parse as a new one. if (targMod != point->getFunction()->getModule()) { // Don't put in inter-module branches if (newParsing) analyzeNewFunction(point, target, true, false); } else { parseNewEdgeInFunction(point, target, false); } if (0 == targFuncs.size()) { proc()->findFunctionsByAddr( target, targFuncs ); } // manipulate init_retstatus so that we will instrument the function's // return addresses, since this jump might be a tail call for (unsigned tidx=0; tidx < targFuncs.size(); tidx++) { parse_func *imgfunc = targFuncs[tidx]->lowlevel_func()->ifunc(); FuncReturnStatus initStatus = imgfunc->init_retstatus(); if (ParseAPI::RETURN == initStatus) { imgfunc->setinit_retstatus(ParseAPI::UNKNOWN); removeInstrumentation(targFuncs[tidx],false,false); instrumentFunction(targFuncs[tidx],false,true); } } // re-instrument the function or the whole module, as needed if (newParsing) { instrumentModules(false); } proc()->finalizeInsertionSet(false); } // end badTransferCB
void readTracePipe() { int read_len; char buf[ STRING_MAX ] = { '\0' }; if (config.pipefd < 0) return; do { errno = 0; sendMsg(config.outfd, ID_TRACE_READ, DEBUG); read_len = read(config.pipefd, buf, trace_msglen); buf[trace_msglen] = '\0'; if (read_len < trace_msglen) { if (read_len == -1 && errno == EAGAIN) { // No data on pipe. Break out of read loop // and re-poll for status change. sendMsg(config.outfd, ID_TRACE_READ, DEBUG, ID_PASS); break; } else if (read_len == 0 && errno == 0) { // Read EOF from pipefd. Close pipe and break. sendMsg(config.outfd, ID_TRACE_READ, DEBUG, ID_PASS); close(config.pipefd); config.pipefd = -1; break; } else if (read_len > 0) { // Partial data written to trace pipe. Report to monitor. sendMsg(config.outfd, ID_TRACE_READ, DEBUG, ID_FAIL, sprintf_static("Read partial message from trace pipe. Discarding message '%s'.", buf)); break; } else if (errno) { // Send error message to monitor. sendMsg(config.outfd, ID_TRACE_READ, DEBUG, ID_FAIL, sprintf_static("Mutator encountered error on trace pipe read(): %s", strerror(errno))); close(config.pipefd); config.pipefd = -1; break; } } void *traceMsg = (void *)strtol(buf, NULL, 16); map< void *, BPatch_function * >::iterator iter = trace_points.find(traceMsg); if (iter == trace_points.end()) { sendMsg(config.outfd, ID_TRACE_READ, DEBUG, ID_FAIL, sprintf_static("Read invalid message from trace pipe. 0x%s does not refer to a valid BPatch_point.", buf)); break; } sendMsg(config.outfd, ID_TRACE_READ, DEBUG, ID_PASS); BPatch_point *point = (BPatch_point *)traceMsg; const char *pType = "Unknown "; if (point->getPointType() == BPatch_entry) pType = "Entering "; if (point->getPointType() == BPatch_exit) pType = "Exiting "; const char *pName = "anonymous function"; BPatch_function *pFunc = (*iter).second; if (pFunc) { if (pFunc->getName(buf, sizeof(buf))) pName = sprintf_static("function %s", buf); else pName = sprintf_static("anonymous function at 0x%0*lx", sizeof(void *), pFunc->getBaseAddr()); } if (config.pipefd > 0) { // Could have been interrupted by mutatee exit. sendMsg(config.outfd, ID_TRACE_POINT, INFO, ID_INFO, strcat_static(pType, pName)); } } while (errno == 0); }
static void GenerateSymFile (set<string> &ParFunc, set<string> &UserFunc, BPatch_image *appImage, BPatch_addressSpace *appProces) { ofstream symfile; string symname = string(::XML_GetFinalDirectory())+string("/")+string(::XML_GetTracePrefix())+".sym"; symfile.open (symname.c_str()); if (!symfile.good()) { cerr << "Cannot create the symbolic file" << symname << endl; return; } for (set<string>::iterator iter = ParFunc.begin(); iter != ParFunc.end(); iter++) { BPatch_function *f = getRoutine ((*iter).c_str(), appImage); if (f != NULL) { BPatch_Vector< BPatch_statement > lines; appProces->getSourceLines ((unsigned long) f->getBaseAddr(), lines); if (lines.size() > 0) { symfile << "P " << hex << f->getBaseAddr() << dec << " \"" << *iter << "\" \"" << lines[0].fileName() << "\" " << lines[0].lineNumber() << endl; } else { /* this happens if the application was not compiled with -g */ char modname[1024]; f->getModuleName (modname, 1024); symfile << "P " << hex << f->getBaseAddr() << dec << " \"" << *iter << "\" \"" << modname << "\" 0" << endl; } } } for (set<string>::iterator iter = UserFunc.begin(); iter != UserFunc.end(); iter++) { BPatch_function *f = getRoutine ((*iter).c_str(), appImage); if (f != NULL) { BPatch_Vector< BPatch_statement > lines; appProces->getSourceLines ((unsigned long) f->getBaseAddr(), lines); if (lines.size() > 0) { symfile << "U " << hex << f->getBaseAddr() << dec << " \"" << *iter << "\" \"" << lines[0].fileName() << "\" " << lines[0].lineNumber() << endl; } else { /* this happens if the application was not compiled with -g */ char modname[1024]; f->getModuleName (modname, 1024); symfile << "U " << hex << f->getBaseAddr() << dec << " \"" << *iter << "\" \"" << modname << "\" 0" << endl; } } } map<string, unsigned>::iterator BB_symbols_iter = BB_symbols->begin(); map<string, unsigned>::iterator BB_symbols_end = BB_symbols->end(); while(BB_symbols_iter != BB_symbols_end){ symfile << "b " << BB_symbols_iter->second << " \"" << BB_symbols_iter->first << "\"\n"; BB_symbols_iter++; } symfile.close(); }