/*
 * BPatch_addressSpace::replaceFunctionCall
 *
 * Replace a function call with a call to a different function.  Returns true
 * upon success, false upon failure.
 *
 * point        The call site that is to be changed.
 * newFunc      The function that the call site will now call.
 */
bool BPatch_addressSpace::replaceFunctionCall(BPatch_point &point,
      BPatch_function &newFunc)
{
   char name[1024];
   newFunc.getName(name, 1024);

   // Can't make changes to code when mutations are not active.
   if (!getMutationsActive())
      return false;

   assert(point.point && newFunc.lowlevel_func());

  /* PatchAPI stuffs */
   AddressSpace* addr_space = point.getAS();
   DynModifyCallCommand* rep_call = DynModifyCallCommand::create(addr_space,
      point.point->block(), newFunc.lowlevel_func(), point.point->func());
  addr_space->patcher()->add(rep_call);
  /* End of PatchAPI */

   if (pendingInsertions == NULL) {
     // Trigger it now
     bool tmp;
     finalizeInsertionSet(false, &tmp);
   }
   return true;
}
/*
 * BPatch_addressSpace::revertReplaceFunction
 *
 * Undoes a replaceFunction operation
 */
bool BPatch_addressSpace::revertReplaceFunction(BPatch_function &oldFunc)
{
  assert(oldFunc.lowlevel_func());
  if (!getMutationsActive())
    return false;

  func_instance *func = oldFunc.lowlevel_func();

  func->proc()->revertReplacedFunction(func);

  if (pendingInsertions == NULL) {
    // Trigger it now
    bool tmp;
    finalizeInsertionSet(false, &tmp);
  }
  return true;
}
/*
 * BPatch_addressSpace::replaceFunction
 *
 * Replace all calls to function OLDFUNC with calls to NEWFUNC.
 * Returns true upon success, false upon failure.
 *
 * oldFunc      The function to replace
 * newFunc      The replacement function
 */
bool BPatch_addressSpace::replaceFunction(BPatch_function &oldFunc,
      BPatch_function &newFunc)
{
  assert(oldFunc.lowlevel_func() && newFunc.lowlevel_func());
  if (!getMutationsActive())
    return false;

  // Self replacement is a nop
  // We should just test direct equivalence here...
  if (oldFunc.lowlevel_func() == newFunc.lowlevel_func()) {
    return true;
  }

  /* PatchAPI stuffs */
  AddressSpace* addr_space = oldFunc.lowlevel_func()->proc();
  DynReplaceFuncCommand* rep_func = DynReplaceFuncCommand::create(addr_space,
     oldFunc.lowlevel_func(), newFunc.lowlevel_func());
  addr_space->patcher()->add(rep_func);
  /* End of PatchAPI */

  if (pendingInsertions == NULL) {
    // Trigger it now
    bool tmp;
    finalizeInsertionSet(false, &tmp);
  }
  return true;
}
Exemple #4
0
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
Exemple #5
0
void HybridAnalysis::virtualFreeCB(BPatch_point *, void *t) {
	assert(virtualFreeAddr_ != 0);
	unsigned type = (unsigned) t;
	cerr << "virtualFree [" << hex << virtualFreeAddr_ << "," << virtualFreeAddr_ + (unsigned) virtualFreeSize_ << "], " << (unsigned) type << dec << endl;

	Address pageSize = proc()->lowlevel_process()->getMemoryPageSize();

	// Windows page-aligns everything.

	unsigned addrShift = virtualFreeAddr_ % pageSize;
	unsigned sizeShift = pageSize - (virtualFreeSize_ % pageSize);

	virtualFreeAddr_ -= addrShift;

	if (type != MEM_RELEASE)
	{
		virtualFreeSize_ += addrShift + sizeShift;
	}

	// We need to:
	// 1) Remove any function with a block in the deleted range
	// 2) Remove memory translation for that range
	// 3) Skip trying to set permissions for any page in the range.

	// DEBUG!
	if (1 || type == MEM_RELEASE)
	{
		mapped_object *obj = proc()->lowlevel_process()->findObject(virtualFreeAddr_);

		if (!obj) return;
		virtualFreeAddr_ = obj->codeBase();
		virtualFreeSize_ = obj->imageSize();
		// DEBUG!
		cerr << "Removing VirtualAlloc'ed shared object " << obj->fileName() << endl;
      image *img = obj->parse_img();
		proc()->lowlevel_process()->removeASharedObject(obj);
		virtualFreeAddr_ = 0;
      // Since removeASharedObject doesn't actually delete the object, 
      // or its image (even if its refCount==0), make sure the image
      // goes away from global datastructure allImages
      for (unsigned int i=0; i < allImages.size(); i++) {
         if (img == allImages[i]) {
            allImages[i] = allImages.back();
            allImages.pop_back();
         }
      }
		return;
	}

	std::set<func_instance *> deletedFuncs;
	for (Address i = virtualFreeAddr_; i < (virtualFreeAddr_ + virtualFreeSize_); ++i) {
		proc()->lowlevel_process()->findFuncsByAddr(i, deletedFuncs);
	}
	for (std::set<func_instance *>::iterator iter = deletedFuncs.begin();
		iter != deletedFuncs.end(); ++iter)
	{
		BPatch_function * bpfunc = proc()->findOrCreateBPFunc(*iter, NULL);
		if (!bpfunc) continue;
        PatchAPI::PatchModifier::remove(bpfunc->lowlevel_func());
	}

	proc()->lowlevel_process()->getMemEm()->removeRegion(virtualFreeAddr_, virtualFreeSize_);
	// And nuke the RT cache

	proc()->lowlevel_process()->proc()->flushAddressCache_RT(virtualFreeAddr_, virtualFreeSize_);

	virtualFreeAddr_ = 0;
	return;
}