Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/dyninstAPI/src/hybridOverwrites.C
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1552 lines
61 KiB
C++

/*
* See the dyninst/COPYRIGHT file for copyright information.
*
* We provide the Paradyn Tools (below described as "Paradyn")
* on an AS IS basis, and do not warrant its validity or performance.
* We reserve the right to update, modify, or discontinue this
* software at any time. We shall have no obligation to supply such
* updates or modifications or any other form of support to you.
*
* By your use of Paradyn, you understand and agree that we (or any
* other person or entity with proprietary rights in Paradyn) are
* under no obligation to provide either maintenance services,
* update services, notices of latent defects, or correction of
* defects for Paradyn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <algorithm>
#include "hybridAnalysis.h"
#include "BPatch_process.h"
#include "BPatch_function.h"
#include "BPatch_basicBlock.h"
#include "BPatch_flowGraph.h"
#include "BPatch_edge.h"
#include "BPatch_module.h"
#include "dynProcess.h"
#include "BPatch_point.h"
#include "function.h"
#include "debug.h"
#include "common/h/DynAST.h"
#include "dataflowAPI/h/Absloc.h"
#include "dataflowAPI/h/AbslocInterface.h"
#include "instructionAPI/h/InstructionDecoder.h"
#include "instPoint.h"
#include "mapped_object.h"
#include "mapped_module.h"
using namespace Dyninst;
static void overwriteAnalysis_wrapper(BPatch_point *point, void *calc)
{
dynamic_cast<BPatch_process*>(point->getFunction()->getProc())->
getHybridAnalysis()->hybridOW()->overwriteAnalysis(point,calc);
}
static void overwriteSignalCB_wrapper(BPatch_point *viol_instruc, Address viol_target)
{
dynamic_cast<BPatch_process*>(viol_instruc->getFunction()->getProc())->
getHybridAnalysis()->hybridOW()->overwriteSignalCB
((Address)viol_instruc->getAddress(),viol_target);
}
InternalCodeOverwriteCallback HybridAnalysisOW::getCodeOverwriteCB()
{
return overwriteSignalCB_wrapper;
}
int HybridAnalysisOW::owLoop::IDcounter_ = 0;
HybridAnalysisOW::owLoop::owLoop(HybridAnalysisOW *hybridow,
Address writeTarg)
{
hybridow_ = hybridow;
writeTarget_ = writeTarg;
activeStatus_ = true;
writesOwnPage_ = false;
realLoop_ = true;
loopID_ = owLoop::getNextLoopId();
hybridow_->idToLoop[loopID_] = this;
hybridow_->loops.insert(this);
}
HybridAnalysisOW::owLoop::~owLoop()
{
hybridow_->idToLoop.erase(loopID_);
hybridow_->loops.erase(this);
std::map<Dyninst::Address,unsigned char *>::iterator sIter = shadowMap.begin();
for(; sIter != shadowMap.end(); sIter++) {
if ((*sIter).second) {
::free((*sIter).second);
}
}
}
// returns true if any of the modfuncs contains loop code
bool HybridAnalysisOW::codeChangeCB
(std::vector<BPatch_function*> &modfuncs)
{
for (std::map<int, owLoop*>::iterator lIter= idToLoop.begin();
lIter != idToLoop.end();
lIter++)
{
for (std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator
biter = ((*lIter).second)->blocks.begin();
biter != ((*lIter).second)->blocks.end();
biter++)
{
vector<ParseAPI::Function *> funcs;
(*biter)->lowlevel_block()->llb()->getFuncs(funcs);
vector<ParseAPI::Function*>::iterator fiter = funcs.begin();
for ( ; fiter != funcs.end(); fiter++) {
Address fAddr = proc()->lowlevel_process()->
findFunction(dynamic_cast<parse_func*>(*fiter))->
addr();
std::vector<BPatch_function*>::iterator bfiter=modfuncs.begin();
for (; bfiter != modfuncs.end(); bfiter++) {
if (fAddr == (Address)(*bfiter)->getBaseAddr()) {
return true;
}
}
}
}
}
return false;
}
HybridAnalysisOW::HybridAnalysisOW(HybridAnalysis *hybrid)
: bpatchBeginCB(NULL), bpatchEndCB(NULL)
{
hybrid_ = hybrid;
}
HybridAnalysisOW::~HybridAnalysisOW()
{
std::set<owLoop*>::iterator lIter = loops.begin();
for (; lIter != loops.end(); lIter++) {
delete *lIter;
}
}
void HybridAnalysisOW::owLoop::setWriteTarget(Address targ)
{
writeTarget_ = targ;
}
void HybridAnalysisOW::owLoop::setWritesOwnPage(bool wop)
{
writesOwnPage_ = wop;
}
void HybridAnalysisOW::owLoop::setActive(bool act)
{
activeStatus_ = act;
}
// return value of 0 means not in loop
HybridAnalysisOW::owLoop *HybridAnalysisOW::findLoop(Address blockStart)
{
if (blockToLoop.find(blockStart) != blockToLoop.end()) {
std::map<int, owLoop*>::iterator iter = idToLoop.find(blockToLoop[blockStart]);
if (iter == idToLoop.end()) {
// because of blocks being overwritten, sometimes we can't tear blocks
// out because the internal blocks have been purged and we can't figure
// out the block address. Eventually, if the block is reconstituted
// we may be able to find it here with a reference to a defunct loop,
// make sure that this is not the case, if it is, tear the block out
blockToLoop.erase(blockStart);
return NULL;
}
return iter->second;
}
return NULL;
}
bool HybridAnalysisOW::isInLoop(Address blockAddr, bool activeOnly)
{
owLoop *loop = findLoop(blockAddr);
if ( !loop ) {
return false;
}
if (activeOnly && loop->isActive()) {
return false;
}
return true;
}
/* 1. Check for changes to the underlying code to see if this is safe to do
* 2. If the loop is active, check for changes to the underlying data, and
* if no changes have occurred, we can just remove the loop instrumentation
* and everything will be hunky dory once we re-instate the write
* protections for the loop's pages
* return true if the loop was active
*/
bool HybridAnalysisOW::deleteLoop(owLoop *loop,
bool useInsertionSet,
BPatch_point *writePoint,
bool uninstrument)
{
mal_printf("deleteLoop: loopID =%d active=%d %s[%d]\n",
loop->getID(),loop->isActive(),FILE__,__LINE__);
bool ret = removeLoop(loop, useInsertionSet, writePoint, uninstrument);
// anything added after this call must also be added to overwriteAnalysis's
// handling of the "overwroteLoop==true" case.
delete loop;
return ret;
}
/* 1. Check for changes to the underlying code to see if this is safe to do
* 2. If the loop is active, check for changes to the underlying data, and
* if no changes have occurred, we can just remove the loop instrumentation
* and everything will be hunky dory once we re-instate the write
* protections for the loop's pages
* return true if the loop was active
*/
bool HybridAnalysisOW::removeLoop(owLoop *loop,
bool useInsertionSet,
BPatch_point *writePoint,
bool uninstrument)
{
bool isLoopActive = loop->isActive();
if (isLoopActive) {
// make sure the underlying code hasn't changed
bool changedPages = false;
bool changedCode = false;
std::vector<pair<Address,int> > deadBlocks;
std::vector<BPatch_function*> modFuncs;
if (writePoint) {
cerr << "Calling overwriteAnalysis with point @ " << hex << writePoint->getAddress() << dec << endl;
overwriteAnalysis(writePoint,(void*)(intptr_t)loop->getID());
} else {
std::set<BPatch_function *> funcsToInstrument;
proc()->overwriteAnalysisUpdate(loop->shadowMap,
deadBlocks,
modFuncs,
funcsToInstrument,
changedPages,
changedCode);
assert(!changedCode && "bug, overwrite loops should not contain "
"instructions that could trigger analysis update callbacks");
}
proc()->protectAnalyzedCode();
}
// remove loop instrumentation
if (uninstrument) {
if (useInsertionSet) {
proc()->beginInsertionSet();
}
std::set<BPatchSnippetHandle*>::iterator sIter = loop->snippets.begin();
mal_printf("deleting snippets from loop %d\n",loop->getID());
for (; sIter != loop->snippets.end(); sIter++) {
proc()->deleteSnippet(*sIter);
}
loop->snippets.clear();
if (useInsertionSet) {
proc()->finalizeInsertionSet(false);
}
}
// clear loop from blockToLoop, idToLoop, and loops datastructures
std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator bIter
= loop->blocks.begin();
for (; bIter != loop->blocks.end(); bIter++) {
if ( !(*bIter)->lowlevel_block() ) {
mal_printf("WARNING: Can't remove overwritten loop block, the "
"internal block was deleted so we can't ascertain its "
"address: loopID %d block %p %s[%d]\n",
loop->getID(), (void*)*bIter,FILE__,__LINE__);
} else {
blockToLoop.erase((*bIter)->getStartAddress());
}
}
assert(idToLoop.end() != idToLoop.find(loop->getID()));
idToLoop.erase(idToLoop.find(loop->getID()));
loops.erase(loop);
return isLoopActive;
}
bool HybridAnalysisOW::hasLoopInstrumentation
(bool activeOnly, BPatch_function &func, std::set<owLoop*> *loops_)
{
//_ASSERTE(_CrtCheckMemory());
// NEED TO BE CAREFUL BECAUSE WHEN WE OVERWRITE A BLOCK WE DON'T INVALIDATE
// THE FLOWGRAPH, BUT FROM THE INT-LAYER ON DOWN THINGS ARE INVALIDATED.
// ANOTHER PROBLEM IS THAT WE MAY BE KEEPING THE BLOCK AROUND AS A PART
// OF A BLOCK LOOP
bool foundLoop = false;
// get function's blocks
std::set<BPatch_basicBlock*> blocks;
BPatch_flowGraph *cfg = func.getCFG();
if (!cfg) {
assert(0);
return false;
}
cfg->getAllBasicBlocks(blocks);
// find loops matching the function's blocks
std::set<BPatch_basicBlock*>::iterator bIter = blocks.begin();
for (; bIter != blocks.end(); bIter++)
{
owLoop *loop = findLoop((*bIter)->getStartAddress());
if (loop && (loop->isActive() || !activeOnly) ) {
foundLoop = true;
if (loops_) {
loops_->insert(loop);
}
}
}
return foundLoop;
}
/* 1. Gather up all instrumentation sites that need to be monitored:
1a. The edges of all instrumented blocks that leave the block set
1b. Unresolved points in instrumented blocks
2. Instrument exit edges and unresolved points with callbacks to
the analysis update routine
2a.Instrument at loop exit edges
2b.Instrument at unresolved edges in the loop
*/
void HybridAnalysisOW::owLoop::instrumentOverwriteLoop(Address writeInsn)
{
assert(blocks.size());
writeInsns.insert(writeInsn);
// call user callback before instrumenting,
// convert loop's block set into a vector
std::vector<BPatch_basicBlock*> loopBlocks;
std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator bIter;
for (bIter = blocks.begin(); bIter != blocks.end(); bIter++) {
loopBlocks.push_back(*bIter);
}
hybridow_->bpatchBeginCB(loopBlocks);
// 1. Gather up all instrumentation sites that need to be monitored:
std::set<BPatch_edge*> instEdges;
vector<BPatch_edge*> outEdges;
bIter = blocks.begin();
while (bIter != blocks.end()) {
mal_printf("loop block [%lx %lx]\n", (*bIter)->getStartAddress(),
(*bIter)->getEndAddress());
// 1a. The edges of all instrumented blocks that leave the block set
(*bIter)->getOutgoingEdges(outEdges);
for (unsigned eIdx=0; eIdx < outEdges.size(); eIdx++) {
if ( blocks.end() ==
blocks.find(outEdges[eIdx]->getTarget()) )
{
mal_printf("instrumenting loop out edge %lx -> %lx\n",
(*bIter)->getLastInsnAddress(),
outEdges[eIdx]->getTarget()->getStartAddress());
instEdges.insert(outEdges[eIdx]);
BPatch_basicBlock *src = outEdges[eIdx]->getSource();
if (src->lowlevel_block()->isShared()) {
vector<func_instance*> funcs;
src->lowlevel_block()->getFuncs(std::back_inserter(funcs));
for (unsigned int fidx=0; fidx < outEdges.size(); fidx++) {
if (funcs[fidx] != src->getFlowGraph()->getFunction()->lowlevel_func()) {
BPatch_function *bpf = hybridow_->proc()->
findOrCreateBPFunc(funcs[fidx],src->getFlowGraph()->getModule());
BPatch_basicBlock *shared = bpf->getCFG()->findBlockByAddr(src->getStartAddress());
vector<BPatch_edge*> sharedOutEdges;
shared->getOutgoingEdges(sharedOutEdges);
for (unsigned sIdx=0; sIdx < sharedOutEdges.size(); sIdx++) {
if (sharedOutEdges[sIdx]->getTarget()->getStartAddress()
== outEdges[eIdx]->getTarget()->getStartAddress())
{
instEdges.insert(sharedOutEdges[sIdx]);
}
}
}
}
}
}
}
outEdges.clear();
bIter++;
}
// 2. Instrument exit edges and unresolved points with callbacks to
// the analysis update routine
BPatch_stopThreadExpr stopForAnalysis(overwriteAnalysis_wrapper,
BPatch_constExpr(getID()),
false,
BPatch_noInterp);
// 2a.Instrument at loop exit edges
std::set<BPatch_edge*>::iterator eIter = instEdges.begin();
BPatchSnippetHandle *snippetHandle = NULL;
while (eIter != instEdges.end()) {
BPatch_point *edgePoint = (*eIter)->getPoint();
mal_printf(" instr edge: 0x%lx -> 0x%lx\n",
(*eIter)->getSource()->getLastInsnAddress(),
(*eIter)->getTarget()->getStartAddress());
snippetHandle = hybridow_->proc()->insertSnippet
(stopForAnalysis, *edgePoint, BPatch_firstSnippet);
assert(snippetHandle);
snippets.insert(snippetHandle);
eIter++;
}
// 2b.Instrument at unresolved edges in the loop
BPatch_dynamicTargetExpr dynTarg;
std::set<BPatch_point*>::iterator uIter = unresExits_.begin();
while (uIter != unresExits_.end()) {
Address uAddr = (Address)(*uIter)->getAddress();
mal_printf(" instr unresolved: 0x%p in func at 0x%p\n",
(void*)uAddr, (*uIter)->getFunction()->getBaseAddr());
if ((*uIter)->isDynamic()) {
long st = 0;
vector<Address> targs;
if (hybridow_->hybrid_->getCallAndBranchTargets
((*uIter)->llpoint()->block(), targs))
{
st = * targs.begin();
}
BPatch_constExpr stSnip(st);
BPatch_ifExpr stopIfNewTarg
( BPatch_boolExpr(BPatch_ne, dynTarg, stSnip),
stopForAnalysis );
snippetHandle = hybridow_->proc()->insertSnippet
(stopIfNewTarg, *(*uIter), BPatch_firstSnippet);
} else {
snippetHandle = hybridow_->proc()->insertSnippet
(stopForAnalysis, *(*uIter), BPatch_firstSnippet);
}
assert(snippetHandle);
snippets.insert(snippetHandle);
uIter++;
}
}
void HybridAnalysisOW::owLoop::instrumentOneWrite(Address writeInsnAddr,
vector<BPatch_function*> writeFuncs)
{
realLoop_ = false;
writeInsns.insert(writeInsnAddr);
// create the stopthread expression
BPatch_stopThreadExpr stopForAnalysis
(overwriteAnalysis_wrapper,
BPatch_constExpr(loopID_),
false,BPatch_noInterp);
for (unsigned fidx =0; fidx < writeFuncs.size(); fidx++)
{
// We can afford to be really slow and precise in the lookup, as this is the
// very, very, very uncommon case.
block_instance *block = writeFuncs[fidx]->lowlevel_func()->obj()->findOneBlockByAddr(writeInsnAddr);
if (!block) continue;
instPoint *ip = instPoint::postInsn(writeFuncs[fidx]->lowlevel_func(),
block,
writeInsnAddr);
BPatch_point *writePoint = hybridow_->proc()->findOrCreateBPPoint(writeFuncs[fidx],
ip,
BPatch_locInstruction);
// instrument and store the snippet handle
BPatchSnippetHandle *snippetHandle = hybridow_->proc()->insertSnippet
(stopForAnalysis, *writePoint, BPatch_callAfter);
assert(snippetHandle);
snippets.insert(snippetHandle);
}
}
/*1. initialize necessary variables
* get loop blocks
* build the set that describes the type of accesses we're looking for
2. create bounds array for all blocks in the loop
* for each block
* create instrumentation points
* store block bounds
* create and initialize the snippet for the array of bounds
3. create the bounds check function call snippet
checkBounds(boundsArray, arrayLen, target)
* arg0: bounds array
* arg1: array size
* arg2: effective address of write instruction
* arg3: call
* find DYNINST_checkBounds function
* create the conditional expression based on bounds check's return value
4. instrument each write point
* create the stopthread expression
* create the if expression
* insert the snippet
*/
void HybridAnalysisOW::owLoop::instrumentLoopWritesWithBoundsCheck()
{
assert(!blocks.empty());
// build the set that describes the type of accesses we're looking for
std::set<BPatch_opCode> insnTypes;
insnTypes.insert(BPatch_opStore);
// 2. create bounds array for all blocks in the loop
int array_length = 2 * blocks.size(); // entries (not bytes) in array
Address *boundsArray = new Address[array_length];
// for each block
std::vector<BPatch_point*> loopWrites;
std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator bIter = blocks.begin();
unsigned boundsIdx=0;
while (bIter != blocks.end()) {
// create instrumentation points
Address blockAddr = (*bIter)->getStartAddress();
ParseAPI::Block *blk = (*bIter)->lowlevel_block()->llb();
Address base = blockAddr - blk->start();
assert(blockAddr == base+blk->start() &&
(*bIter)->getEndAddress() == base+blk->end());
std::vector<BPatch_point*>* blockWrites = (*bIter)->findPoint(insnTypes);
for (unsigned widx = 0; widx < (*blockWrites).size(); widx++) {
if (BPatch_locSubroutine == (*blockWrites)[widx]->getPointType() ||
!hybridow_->isRealStore(
(Address)(*blockWrites)[widx]->getAddress(),
(*bIter)->lowlevel_block(),
(*blockWrites)[widx]->getFunction()))
{
(*blockWrites)[widx] = (*blockWrites)[blockWrites->size()-1];
blockWrites->pop_back();
widx--; // there's an unexamined point at this vector location
}
}
loopWrites.insert(loopWrites.end(),
blockWrites->begin(),
blockWrites->end());
// store block bounds or alter previous entry if blocks are contiguous
if (boundsIdx > 0 && boundsArray[boundsIdx-1] == (*bIter)->getStartAddress()) {
boundsArray[boundsIdx-1] = (*bIter)->getEndAddress();
} else {
if (boundsIdx > 0) {
mal_printf("BA[%u] = [%lx %lx]\n", boundsIdx-2,
boundsArray[boundsIdx-2],boundsArray[boundsIdx-1]);
}
boundsArray[boundsIdx] = (*bIter)->getStartAddress();
boundsArray[boundsIdx+1] = (*bIter)->getEndAddress();
boundsIdx+=2;
}
bIter++;
}
if (boundsIdx > 0) {
mal_printf("BA[%u] = [%lx %lx]\n",
boundsIdx-2, boundsArray[boundsIdx-2], boundsArray[boundsIdx-1]);
}
mal_printf("instrumenting %lu store instructions in loop %d %s[%d]\n",
loopWrites.size(),loopID_,FILE__,__LINE__);
// array length is now probably smaller than earlier worst case value
array_length = boundsIdx;
// create and initialize the snippet for the array of bounds
BPatch_variableExpr *varBA = hybridow_->proc()->malloc
(array_length *sizeof(Address));
varBA->writeValue(boundsArray, array_length * sizeof(Address), false);
//3. create the bounds check function call snippet
// checkBounds(boundsArray, arrayLen, target)
BPatch_Vector<BPatch_snippet*> args;
// arg0: bounds array
BPatch_constExpr varArrayBase(varBA->getBaseAddr());
args.push_back(&varArrayBase);
// arg1: array length
BPatch_constExpr varAS(array_length);
args.push_back(&varAS);
// arg2: effective address of write instruction
BPatch_effectiveAddressExpr eae;
args.push_back(&eae);
// create function call snippet to DYNINST_checkBounds
vector<BPatch_function *> funcs;
hybridow_->hybrid()->getRuntimeLib()->findFunction
("DYNINST_boundsCheck", funcs);
BPatch_function *boundsCheckFunc = funcs[0];
BPatch_funcCallExpr bcCall(*boundsCheckFunc, args);
// create the conditional expression based on bounds check's return value
BPatch_boolExpr condition(BPatch_ne, bcCall, BPatch_constExpr(0));
// create the conditional expression based on bounds check's return value
BPatch_constExpr ce_lowAddr(boundsArray[0]);
BPatch_constExpr ce_highAddr(boundsArray[array_length-1]);
BPatch_boolExpr cond_lb(BPatch_le, ce_lowAddr, eae);
BPatch_boolExpr cond_hb(BPatch_lt, eae, ce_highAddr);
BPatch_boolExpr cond_bounds(BPatch_and, cond_lb, cond_hb);
//4. instrument each write point
for (unsigned wIdx=0; wIdx < loopWrites.size(); wIdx++) {
// create the stopthread expression
BPatch_stopThreadExpr stopForAnalysis
(overwriteAnalysis_wrapper,
BPatch_constExpr(-1*loopID_),
false,BPatch_noInterp);
mal_printf("BoundsCheck Call at %p\n",loopWrites[wIdx]->getAddress());
// create the if expression
BPatch_ifExpr ifBoundsThenStop(condition, stopForAnalysis);
// insert the snippet
BPatchSnippetHandle *handle = hybridow_->proc()->insertSnippet
(ifBoundsThenStop, *loopWrites[wIdx], BPatch_callAfter);
snippets.insert(handle);
}
}
// Returns a loop if all callers to the writeAddr function are in a single
// function and there is a loop that contains them all.
// (otherwise we would have to do a stackwalk and that's too expensive)
// If more than one loop satisfies this criteria, choose the largest one
BPatch_basicBlockLoop* HybridAnalysisOW::getParentLoop(BPatch_function &func, Address writeAddr)
{
set<BPatch_function*> callFs;
vector<BPatch_point*> callPs;
func.getCallerPoints(callPs);
for (vector<BPatch_point*>::iterator pit = callPs.begin(); pit != callPs.end(); pit++) {
callFs.insert((*pit)->getFunction());
}
if (callFs.empty()) {
return NULL;
}
BPatch_function *callF = NULL;
if (callFs.size() > 1) {
if (writeHits.end() == writeHits.find(writeAddr)) {
writeHits[writeAddr] = 0;
}
writeHits[writeAddr] += 1;
if (writeHits[writeAddr] >= HIT_THRESHOLD) {
vector<vector<Frame> > stacks;
proc()->lowlevel_process()->walkStacks(stacks);
unsigned writeStackIdx = stacks.size();
for (unsigned sidx=0; sidx < stacks.size(); sidx++) {
if (stacks[sidx].front().getFunc() == func.lowlevel_func()) {
writeStackIdx = sidx;
break;
}
}
if (writeStackIdx < stacks.size()) {
// pick a loop to return, we choose the deepest function with
// a loop that calls the writeFunction directly, or if there
// is none, the first loop we find in any function
vector<BPatch_basicBlockLoop*> callWriteLoops;
BPatch_basicBlockLoop* firstNoCallLoop = NULL;
for (unsigned fidx=1; fidx < stacks[writeStackIdx].size(); fidx++) {
func_instance *curIntF = stacks[writeStackIdx][fidx].getFunc();
if (!curIntF) {
break; // we haven't parsed at the function's call
// fallthrough address, if there is a loop here
// we haven't parsed it yet, later on we may
// complete the parse, discovering the loop
}
BPatch_function *curF = proc()->
findOrCreateBPFunc(curIntF, NULL);
// translate PC to unrelocated version
Address origPC = stacks[writeStackIdx][fidx].getPC();
vector<func_instance*> tmp1;
baseTramp *tmp2;
proc()->lowlevel_process()->getAddrInfo
(stacks[writeStackIdx][fidx].getPC(),origPC,tmp1,tmp2);
// get loop
BPatch_basicBlockLoop *curL = getWriteLoop
(*curF, origPC, false);
if (curL) {
// curF calls the write loop directly
if (callFs.end() != callFs.find(curF)) {
callWriteLoops.push_back(curL);
}
else if (NULL == firstNoCallLoop) {
firstNoCallLoop = curL;
}
}
}
if (!callWriteLoops.empty()) {
return callWriteLoops.back();
} else if (firstNoCallLoop) {
return firstNoCallLoop;
}
}
}
return NULL;
}
else {
callF = * callFs.begin();
map<int,BPatch_basicBlockLoop*> goodLoops;
for (vector<BPatch_point*>::iterator pit = callPs.begin(); pit != callPs.end(); pit++) {
BPatch_basicBlockLoop *curL = getWriteLoop(*callF, (Address)(*pit)->getAddress(), false);
if (curL) {
bool containsAllCalls = true;
for (vector<BPatch_point*>::iterator pit2 = callPs.begin();
pit2 != callPs.end(); pit2++)
{
if ( ! curL->containsAddressInclusive((Address)(*pit2)->getAddress()) ) {
containsAllCalls = false;
}
}
if (containsAllCalls) {
vector<BPatch_basicBlock*> loopBs;
curL->getLoopBasicBlocks(loopBs);
goodLoops[loopBs.size()] = curL;
}
}
}
if (goodLoops.empty()) {
return NULL;
}
return goodLoops.rbegin()->second;
}
}
// gets biggest loop without unresolved/multiply resolved indirect ctrl flow that it can find
BPatch_basicBlockLoop* HybridAnalysisOW::getWriteLoop
(BPatch_function &func, Address writeAddr, bool allowCallerLoop)
{
BPatch_flowGraph *graph = func.getCFG();
vector<BPatch_basicBlockLoop*> loops_;
vector<BPatch_point*> blockPoints;
vector<BPatch_basicBlock *>loopBlocks;
graph->getLoops(loops_);
if (allowCallerLoop && loops_.empty()) {
BPatch_basicBlockLoop *pLoop = getParentLoop(func,writeAddr);
if (pLoop) {
return pLoop;
}
}
vector<BPatch_basicBlockLoop*>::iterator lIter = loops_.begin();
BPatch_basicBlockLoop *writeLoop = NULL;
while (lIter != loops_.end()) {
// mal_printf("found nat'l loop w/ block[0] at 0x%x, back edge at 0%x\n",
// (*lIter)->getLoopHead()->getStartAddress(),
// (*lIter)->getBackEdge()->getSource()->getLastInsnAddress());
if ((*lIter)->containsAddressInclusive(writeAddr) &&
(!writeLoop || writeLoop->hasAncestor(*lIter)))
{
// set writeLoop if the curloop has no indirect or non-returning control transfers
bool hasUnresolved = false;
(*lIter)->getLoopBasicBlocks(loopBlocks);
for (vector<BPatch_basicBlock*>::iterator bIter= loopBlocks.begin();
bIter != loopBlocks.end() && !hasUnresolved;
bIter++)
{
Address blockStart = (*bIter)->getStartAddress();
if (blockToLoop.find(blockStart) != blockToLoop.end()) {
// this block corresponds to another instrumented loop!
mal_printf("WARNING: block [%lx %lx] in loop overlaps with "
"existing loop %d %s[%d]\n", blockStart,
(*bIter)->getEndAddress(),
blockToLoop[blockStart], FILE__,__LINE__);
}
(*bIter)->getAllPoints(blockPoints);
for (vector<BPatch_point*>::iterator pIter= blockPoints.begin();
pIter != blockPoints.end();
pIter++)
{
if ((*pIter)->isDynamic()) {
vector<Address> targs;
if (!hybrid_->getCallAndBranchTargets((*pIter)->llpoint()->block(),targs)) {
mal_printf("loop has an unresolved indirect transfer at %p\n",
(*pIter)->getAddress());
hasUnresolved = true;
} else if (targs.size() > 1) {
mal_printf("loop has an indirect transfer resolving to multiple targets at %p\n",
(*pIter)->getAddress());
hasUnresolved = true;
}
}
}
blockPoints.clear();
}
loopBlocks.clear();
if (!hasUnresolved) {
writeLoop = *lIter;
}
}
lIter++;
}
if (writeLoop) {
// mal_printf("CHOSE nat'l loop with: block[0] at 0x%x, back edge at 0%x\n",
// writeLoop->getLoopHead()->getStartAddress(),
// writeLoop->getBackEdge()->getSource()->getLastInsnAddress());
}
return writeLoop;
}
// adds to visitMe if the library is in a non-system library in
// exploratory or defensive mode
static void addLoopFunc(BPatch_function *func,
set<BPatch_function*> &visited,
set<BPatch_function*> &visitMe)
{
if (!func) {
return;
}
// if we've visited the func, return
if (visited.end() != visited.find(func)) {
return;
}
// add to visited
visited.insert(func);
// add to visitMe, if func is in a defensive binary
if ( BPatch_defensiveMode == func->getModule()->getHybridMode() ) {
mal_printf("new loop func at=%lx %d\n",
(Address)func->getBaseAddr(),__LINE__);
visitMe.insert(func);
}
else if ( ! func->getModule()->isSystemLib() ) {
const int nameLen = 32;
char modName[nameLen];
func->getModule()->getName(modName,nameLen);
fprintf(stderr,"ERROR: overwrite loop calls into func at "
"0x%lx in module %s that is not marked with "
"malware mode %s[%d]\n",
(Address)func->getBaseAddr(),
modName,FILE__,__LINE__);
}
}
// recursively add all functions that contain calls,
// return true if the function contains no unresolved control flow
// and the function returns normally
bool HybridAnalysisOW::addFuncBlocks(owLoop *loop,
std::set<BPatch_function*> &addFuncs,
std::set<BPatch_function*> &seenFuncs,
std::set<int> &overlappingLoops)
{
bool hasUnresolved = false;
std::set<BPatch_function*> nextAddFuncs;
std::vector<BPatch_point*> unresolvedCF;
// for each function in addFuncs
for (set<BPatch_function*>::iterator fIter= addFuncs.begin();
fIter != addFuncs.end();
fIter++)
{
// for each of its blocks
std::set<BPatch_basicBlock*> fBlocks;
(*fIter)->getCFG()->getAllBasicBlocks(fBlocks);
for (std::set<BPatch_basicBlock*>::iterator bIter= fBlocks.begin();
bIter != fBlocks.end();
bIter++)
{
// check to see if the block has a non-returning call
if ((*bIter)->lowlevel_block()->llb()->isCallBlock() &&
NULL == (*bIter)->lowlevel_block()->getFallthrough())
{
mal_printf("loop %d calls func %p which has a non-returning "
"call at %lx %s[%d]\n", loop->getID(),
(*fIter)->getBaseAddr(),
(*bIter)->getLastInsnAddress(), FILE__,__LINE__);
hasUnresolved = true;
}
// add the block to the new loop's datastructures
Address blockStart = (*bIter)->getStartAddress();
if (blockToLoop.find(blockStart) != blockToLoop.end() &&
loop->getID() != blockToLoop[blockStart])
{
mal_printf("block [%lx %lx] in loop %d overlaps with existing loop %d %s[%d]\n",
blockStart, (*bIter)->getEndAddress(),
loop->getID(), blockToLoop[blockStart],FILE__,__LINE__);
overlappingLoops.insert(blockToLoop[blockStart]);
}
blockToLoop[blockStart] = loop->getID();
loop->blocks.insert(*bIter);
// if the block has a call to an unseen function, add it for next iteration
if ((*bIter)->lowlevel_block()->containsCall()) {
addLoopFunc((*bIter)->getCallTarget(), seenFuncs, nextAddFuncs);
}
}
// if func contains ambiguously resolved control flow, or truly
// unresolved ctrl flow, then set flag and clear vector
(*fIter)->getUnresolvedControlTransfers(unresolvedCF);
for (vector<BPatch_point*>::iterator pIter = unresolvedCF.begin();
!hasUnresolved && pIter != unresolvedCF.end();
pIter++)
{
vector<Address> targs;
hybrid_->getCallAndBranchTargets((*pIter)->llpoint()->block(), targs);
if (1 != targs.size()) {
loop->unresExits_.insert(*pIter);
hasUnresolved = true;
mal_printf("loop %d calls func %p which has an indirect "
"transfer at %p that resolves to %lu targets "
"%s[%d]\n", loop->getID(), (*fIter)->getBaseAddr(),
(*pIter)->getAddress(), targs.size(), FILE__,__LINE__);
} else {
// add target function if it's not in seenFuncs
addLoopFunc(proc()->findFunctionByEntry(targs[0]), seenFuncs, nextAddFuncs);
}
}
if (unresolvedCF.size()) {
unresolvedCF.clear();
}
// also want to make sure this function is returning
if ( ParseAPI::RETURN !=
(*fIter)->lowlevel_func()->ifunc()->retstatus() )
{
mal_printf("loop %d calls func %p which could be "
"non-returning %s[%d]\n", loop->getID(),
(*fIter)->getBaseAddr(), FILE__,__LINE__);
hasUnresolved = true;
}
}
// if these functions called additional functions
if (!hasUnresolved && nextAddFuncs.size()) {
if (false == addFuncBlocks(loop,
nextAddFuncs,
seenFuncs,
overlappingLoops))
{
hasUnresolved = true;
}
}
return ! hasUnresolved;
}
// if writeLoop is null, return the whole function in the loop.
// returns true if we were able to identify all code in the loop
//
// the checks on the basicBlockLoop's immediate blocks are not strictly
// necessary as they should have been checked by getWriteLoop
//
// considering the loop save to instrument if its indirect control transfers
// have so far always been resolved to a single control flow target
//
// Does not set the block->loop map for overlapping blocks
bool HybridAnalysisOW::setLoopBlocks(owLoop *loop,
BPatch_basicBlockLoop *writeLoop,
std::set<int> &overlappingLoops)
{
bool hasUnresolvedCF = false;
vector<BPatch_point*> blockPoints;
std::set<BPatch_function*> loopFuncs;// functions called by loop insns
vector<BPatch_basicBlock *>loopBlocks;
writeLoop->getLoopBasicBlocks(loopBlocks);
// for each block in the loop
for (vector<BPatch_basicBlock*>::iterator bIter= loopBlocks.begin();
bIter != loopBlocks.end();
bIter++)
{
// add the block to the loop's block-list datastructure
Address blockStart = (*bIter)->getStartAddress();
if (blockToLoop.find(blockStart) != blockToLoop.end()) {
// deal w/ overlapping blocks in the caller
// this block corresponds to another instrumented loop!
fprintf(stderr,"WARNING: block [%lx %lx] in loop %d overlaps with "
"existing loop %d %s[%d]\n", blockStart,
(*bIter)->getEndAddress(), loop->getID(),
blockToLoop[blockStart], FILE__,__LINE__);
overlappingLoops.insert(blockToLoop[blockStart]);
}
blockToLoop[(*bIter)->getStartAddress()] = loop->getID();
loop->blocks.insert(*bIter);
// if the block has an indirect control transfer
(*bIter)->getAllPoints(blockPoints);
for (vector<BPatch_point*>::iterator pIter= blockPoints.begin();
pIter != blockPoints.end();
pIter++)
{
if ((*pIter)->isDynamic()) {
// We've already checked that the transfer is uniquely
// resolved, add the target.
vector<Address> targs;
hybrid_->getCallAndBranchTargets((*pIter)->llpoint()->block(), targs);
assert(targs.size() == 1);
mal_printf("loop %d has a resolved indirect transfer at %p with "
"target %lx\n", loop->getID(), (*pIter)->getAddress(),
*targs.begin());
vector<BPatch_function *> targFuncs;
proc()->findFunctionsByAddr(*targs.begin(),targFuncs);
if (!targFuncs.empty() &&
targFuncs[0]->getModule()->isExploratoryModeOn())
{
loopFuncs.insert(targFuncs.begin(),targFuncs.end());
} else if (!targFuncs.empty()) {
mal_printf("loop contains call to non-mal-func:%p %d\n",
targFuncs[0]->getBaseAddr(), __LINE__);
}
}
}
blockPoints.clear();
// if the block has a call, add it to the list of called funcs
BPatch_function *targFunc = NULL;
if ((*bIter)->lowlevel_block()->containsCall()) {
targFunc = (*bIter)->getCallTarget();
}
if (targFunc &&
(*bIter)->getFlowGraph()->getModule() == targFunc->getModule())
{
mal_printf("loop has a function call %lx->%p\n",
(*bIter)->getLastInsnAddress(), targFunc->getBaseAddr());
loopFuncs.insert(targFunc);
} else if (targFunc) {
mal_printf("loop calls func at %p in different module [%d]\n",
targFunc->getBaseAddr(), __LINE__);
}
}
//recursively add blocks in called functions to the loop
if (!loopFuncs.empty()) {
std::set<BPatch_function*> loopFuncCopy(loopFuncs);
if (false == addFuncBlocks(loop,
loopFuncs,
loopFuncCopy,
overlappingLoops))
{
hasUnresolvedCF = true;
}
}
// if the loop has bad ctrl flow, clear up the loop-block datastructures
if (hasUnresolvedCF == true) {
std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator bIter
= loop->blocks.begin();
while (bIter != loop->blocks.end()) {
blockToLoop.erase((*bIter)->getStartAddress());
bIter++;
}
loop->blocks.clear();
}
return ! hasUnresolvedCF;
}
#if !defined(os_windows)
void HybridAnalysisOW::makeShadow_setRights(Address , owLoop *) {}
void HybridAnalysisOW::overwriteAnalysis(BPatch_point *, void *) {}
bool HybridAnalysisOW::removeOverlappingLoops
(owLoop *, std::set<int> &) { return false; }
#else
// return true if the loop's blocks are a superset of the loop(s) it
// overlaps with
bool HybridAnalysisOW::removeOverlappingLoops(owLoop *loop,
std::set<int> &overlappingLoops)
{
bool isSuperset = true;
// iterate to ensure that overlappingLoops are contained in this loop
std::set<int>::iterator lIter = overlappingLoops.begin();
std::set<int> removeLoops;
for (; lIter != overlappingLoops.end(); lIter++)
{
// get blocks for other loops
owLoop *otherLoop = idToLoop[*lIter];
if (otherLoop->blocks.empty()) { // if cleanup is only partially done
deleteLoop(otherLoop,false);
removeLoops.insert(*lIter);
} else {// iterate through otherLoop's blocks to ensure they're a subset
std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator
obIter = otherLoop->blocks.begin();
for (; obIter != otherLoop->blocks.end(); obIter++) {
if (loop->blocks.end() == loop->blocks.find(*obIter))
{
fprintf(stderr,"WARNING: write loop %d is not a subset of "
"current loop, block [%lx %lx] not in curloop "
"%s[%d]\n", *lIter, (*obIter)->getStartAddress(),
(*obIter)->getEndAddress(), FILE__,__LINE__);
isSuperset = false;
}
}
}
}
// remove from overlappingLoops the loops that are overlapping
for (lIter = removeLoops.begin(); lIter != removeLoops.end(); lIter++) {
overlappingLoops.erase( overlappingLoops.find(*lIter) );
}
// if the overlapping loops are contained in this loop
if (isSuperset) {
// remove instrumentation for other loops
for (lIter = overlappingLoops.begin();
lIter != overlappingLoops.end();
lIter++)
{
deleteLoop(idToLoop[*lIter],false); //CAREFUL! removes write perm's from loop pages
}
// restore write permissions to pages that are currently shadowed
if (!loop->shadowMap.empty()) {
Dyninst::ProcControlAPI::Process::mem_perm rights(true, true, true);
std::map<Address,unsigned char*>::iterator siter = loop->shadowMap.begin();
for (; siter != loop->shadowMap.end(); siter++) {
proc()->setMemoryAccessRights(siter->first,
proc()->lowlevel_process()->getMemoryPageSize(),
rights /*PAGE_EXECUTE_READWRITE*/);
mal_printf("revbug restoring write to %lx %s[%d]\n",(*siter).first,
FILE__,__LINE__);
}
}
// add all blocks to block -> loop map (overlap blocks won't be)
std::set<BPatch_basicBlock*,HybridAnalysis::blockcmp>::iterator bIter;
for(bIter = loop->blocks.begin(); bIter != loop->blocks.end(); bIter++) {
Address blockStart = (*bIter)->getStartAddress();
if (blockToLoop.end() == blockToLoop.find(blockStart)) {
blockToLoop[blockStart] = loop->getID();
} else {
assert(loop->getID() == blockToLoop[blockStart]);
}
}
}
return isSuperset;
}
// remove any coverage instrumentation
// make a shadow page,
// restore write privileges to the page,
void HybridAnalysisOW::makeShadow_setRights
(Address pageAddr, // addr on the page, not necessarily the start of the page
owLoop *loop)
{
const unsigned int pageSize = proc()->lowlevel_process()->getMemoryPageSize();
pageAddr = (pageAddr / pageSize ) * pageSize;
// . Make a shadow copy of the block that is about to be overwritten
loop->shadowMap[pageAddr] = proc()->makeShadowPage(pageAddr);
Dyninst::ProcControlAPI::Process::mem_perm rights(true, true, true);
// Restore write permissions to the written page
proc()->setMemoryAccessRights(pageAddr, pageSize,
rights /* PAGE_EXECUTE_READWRITE */);
}
/*
* 1. Identify overwritten blocks and update the analysis
* 2. Update instrumentation
* 3. Remove loop instrumentation
* 4. Free shadow pages
*/
void HybridAnalysisOW::overwriteAnalysis(BPatch_point *point, void *loopID_)
{
Address pointAddr = (Address) point->getAddress();
mal_printf("\noverwriteAnalysis(trigger=%lx, loopID=%d)\n",
pointAddr,(long)loopID_);
// setup
bool changedPages = false;
bool changedCode = false;
std::vector<BPatch_function*> owFuncs;
std::vector<pair<Address,int> > deadBlocks;
std::vector<BPatch_function*> newFuncs;
std::vector<BPatch_function*> modFuncs;
std::set<BPatch_module*> modModules;
std::set<BPatch_function*> funcsToInstrument;
const unsigned int pageSize = proc()->lowlevel_process()->getMemoryPageSize();
Address pageAddress = (((Address)pointAddr) / pageSize) * pageSize;
bool overwroteLoop = false;
long loopID = (long)loopID_;
//if we quit early because we failed a bounds check on a write instruction:
if (loopID < 0) {
loopID *= -1;
overwroteLoop = true;
}
// find the loop corresponding to the loopID, and if there is none, it
// means we tried to delete the instrumentation earlier, but failed
if (idToLoop.end() == idToLoop.find(loopID)) {
fprintf(stderr,"executed instrumentation for old loop %d at point %lx "
"%s[%d]\n", loopID, pointAddr, FILE__,__LINE__);
assert(0);
return;
}
owLoop *loop = idToLoop[loopID];
loop->setActive(false);
loop->unresExits_.clear();
if (overwroteLoop) {
mal_printf("Overwrite loop modified its own code\n");
for (std::map<Address, unsigned char *>::iterator pIter
= loop->shadowMap.begin();
pIter != loop->shadowMap.end();
pIter++)
{
modModules.insert( proc()->findModuleByAddr( (*pIter).first ) );
}
removeLoop(loop, true, NULL, true);
}
/* 1. Identify overwritten blocks and update the analysis */
// if writeTarget is non-zero, only one byte was overwritten
proc()->overwriteAnalysisUpdate(loop->shadowMap,
deadBlocks,
owFuncs,
funcsToInstrument,
changedPages,
changedCode);
if (overwroteLoop) {
delete(loop);
loop = NULL;
}
proc()->beginInsertionSet();
/* 2. if the code changed, remove loop & overwritten instrumentation,
then re-instrument */
for (std::set<BPatch_function *>::iterator iter = funcsToInstrument.begin();
iter != funcsToInstrument.end(); ++iter)
{
hybrid_->instrumentFunction(*iter,
false, // Already in an insertion set, so don't start another
false, // Only need unresolved instrumentation, I think.
false); // And don't add shadow synchronization.
}
if (changedCode) {
// overwrite stats
hybrid_->stats_.owCount++;
unsigned long owBytes = 0;
for (vector<pair<Address,int> >::iterator bit = deadBlocks.begin();
bit != deadBlocks.end();
bit++)
{
owBytes += bit->second;
}
hybrid_->stats_.owBytes = owBytes;
// build up list of modified modules
if (!overwroteLoop) {
for (std::map<Address, unsigned char *>::iterator pIter
= loop->shadowMap.begin();
pIter != loop->shadowMap.end();
pIter++)
{
modModules.insert( proc()->findModuleByAddr( (*pIter).first ) );
}
}
// debugging output
for(vector<BPatch_function*>::iterator fIter = owFuncs.begin();
fIter != owFuncs.end();
fIter++)
{
mal_printf("modified function at %lx %s[%d]\n",
(*fIter)->getBaseAddr(),FILE__,__LINE__);
}
// remove function instrumentation datastructures
vector<BPatch_function*>::iterator fIter = owFuncs.begin();
for(;fIter != owFuncs.end(); fIter++) {
// this will already have happened internally, here we clear out
// our instrumentation datastructures
hybrid_->removeInstrumentation( *fIter,false,false );
(*fIter)->getCFG()->invalidate();
}
// Clean up datastructures for pages that had code but don't anymore
// Re-instate code discovery instrumentation
for(set<BPatch_module*>::iterator mIter = modModules.begin();
mIter != modModules.end();
mIter++)
{
(*mIter)->lowlevel_mod()->obj()->removeEmptyPages();
hybrid_->instrumentModule(*mIter,false);
}
// call user-level callback, but first set up its arguments
hybrid_->proc()->getImage()->getNewCodeRegions(newFuncs,modFuncs);
for(fIter = owFuncs.begin(); fIter != owFuncs.end(); fIter++) {
std::vector<BPatch_function*>::iterator fIter2 =newFuncs.begin();
for (;fIter2 != newFuncs.end(); fIter2++) {
if ((*fIter2) == (*fIter)) {
newFuncs.erase(fIter2);
break;
}
}
fIter2 =modFuncs.begin();
for (;fIter2 != modFuncs.end(); fIter2++) {
if ((*fIter2) == (*fIter)) {
modFuncs.erase(fIter2);
break;
}
}
}
bpatchEndCB(deadBlocks, owFuncs, modFuncs, newFuncs);
hybrid_->proc()->getImage()->clearNewCodeRegions();
} // if the code changed
else {
hybrid_->stats_.owFalseAlarm++;
}
// delete the loop, but if the code changed, we've probably already done so
if (!overwroteLoop && idToLoop.end() != idToLoop.find(loopID)) {
deleteLoop(loop,false);
}
for (map<Address,int>::iterator lit = blockToLoop.begin();
lit != blockToLoop.end();)
{
if (lit->second == loopID) {
lit = blockToLoop.erase(lit);
} else {
lit++;
}
}
proc()->finalizeInsertionSet(false);
proc()->protectAnalyzedCode();
malware_cerr << "overWriteAnalysis returns" << endl;
}
#endif
bool HybridAnalysisOW::isRealStore(Address insnAddr, block_instance *block,
BPatch_function *func)
{
using namespace InstructionAPI;
const unsigned char* buf = reinterpret_cast<const unsigned char*>
(proc()->lowlevel_process()->getPtrToInstruction(insnAddr));
InstructionDecoder decoder(buf,
InstructionDecoder::maxInstructionLength,
proc()->lowlevel_process()->getArch());
Instruction insn = decoder.decode();
assert(insn.isValid());
parse_func *imgfunc = func->lowlevel_func()->ifunc();
Address image_addr = func->lowlevel_func()->addrToOffset(insnAddr);
std::vector<Assignment::Ptr> assignments;
AssignmentConverter aConverter(false, false);
aConverter.convert(insn, image_addr, imgfunc, block->llb(), assignments);
for (std::vector<Assignment::Ptr>::const_iterator a_iter = assignments.begin();
a_iter != assignments.end(); ++a_iter)
{
if ((*a_iter)->out().contains(Absloc::Heap)) {
return true;
}
}
return false;
}
/* Informs the mutator that an instruction will write to a page
` * that contains analyzed code.
* This function decides where to put the instrumentation that will mark
* the end of the overwriting phase
*
* 1. If this is an already instrumented instruction that has now moved onto
* an adjacent page or is in a subsequent iteration of the instrumented loop:
* 1a.Make a shadow copy of the overwritten page and restore write permissions
* . Instrument the loop
* . Make a shadow copy of the block that is about to be overwritten
* . Restore write permissions to the written page
*/
void HybridAnalysisOW::overwriteSignalCB
(Address faultInsnAddr, Address writeTarget)
{
using namespace std;
// debugging output
mal_printf("\noverwriteSignalCB(%lx , %lx)\n", faultInsnAddr, writeTarget);
// setup
vector<BPatch_function*> faultFuncs;
proc()->findFunctionsByAddr(faultInsnAddr,faultFuncs);
assert(!faultFuncs.empty());
vector<BPatch_basicBlock*> faultBlocks;
for (unsigned fidx=0; fidx < faultFuncs.size(); fidx++) {
faultBlocks.push_back(faultFuncs[fidx]->getCFG()->findBlockByAddr(faultInsnAddr));
}
assert(faultBlocks.size() == faultFuncs.size());
const unsigned int pageSize = proc()->lowlevel_process()->getMemoryPageSize();
Address pageAddress = (writeTarget / pageSize) * pageSize;
proc()->beginInsertionSet();
/* 1. If this is an already instrumented instruction that has now moved onto
an adjacent page or is in a subsequent iteration of the instrumented loop: */
owLoop *loop = findLoop(faultBlocks[0]->getStartAddress());
if ( loop ) {
mal_printf("matches existing loop %d of %lu blocks\n", loop->getID(), loop->blocks.size());
//make sure we haven't added this page to the loop's shadows already
assert(loop->shadowMap.end() == loop->shadowMap.find(pageAddress));
// if this loop has written to more than one location since it
// last exited (or will), indicate it by zeroing out loopWriteTarget
if (loop->getWriteTarget() != writeTarget || loop->blocks.size() > 1) {
loop->setWriteTarget(0);
}
// if the write is to same page as the write instruction and we didn't
// already know that it does this, instrument the loop with bounds
// checks
if( loop->isRealLoop() &&
pageAddress <= faultInsnAddr &&
faultInsnAddr < (pageAddress + pageSize) &&
! loop->writesOwnPage() )
{
loop->setWritesOwnPage(true);
mal_printf("discovered that loop %d writes to one of its code pages, "
"will add write-bounds instrumentation\n", loop->getID());
loop->instrumentLoopWritesWithBoundsCheck();
}
makeShadow_setRights(writeTarget, loop);
loop->setActive(true);
proc()->finalizeInsertionSet(false);
return;
}
// grab the next available loopID
loop = new owLoop(this, writeTarget);
mal_printf("new overwrite loop %d %s[%d]\n", loop->getID(), FILE__,__LINE__);
// find loops surrounding the write insn
BPatch_basicBlockLoop *bblLoop = getWriteLoop(*faultFuncs[0],faultInsnAddr,true);
if ( bblLoop)
{
// set loop blocks, returns false if there's indirect control flow in the loop,
// in which case we default to immediate instrumentation
set<int> overlappingLoops;
if (1 == faultFuncs.size() &&
setLoopBlocks(loop, bblLoop, overlappingLoops) &&
( 0 == overlappingLoops.size() ||
removeOverlappingLoops(loop,overlappingLoops)))
{
// add bounds checks to the loop if it writes to its own page
if (pageAddress <= faultInsnAddr &&
faultInsnAddr < (pageAddress + pageSize))
{
loop->instrumentLoopWritesWithBoundsCheck();
loop->setWritesOwnPage(true);
for (unsigned bidx=0; bidx < faultBlocks.size(); bidx++) {
// If the fault block contains the write target,
// we will have to single-step the write operation
if (writeTarget >= faultBlocks[bidx]->getStartAddress() &&
writeTarget < faultBlocks[bidx]->getEndAddress())
{
assert(0 && "KEVINTEST: test what happens when "
"program writes to its own basic block");
// change PC and context to next instruction to be
// executed, then single step the write instruction
// how does Olly do this?
}
}
}
}
else {
// clear out loop block datastructures
// KEVINOPTIMIZE: shift to instrumenting a smaller loop if there are
// any and they don't contain indirect ctrl flow, instead of
// reverting to single block instrumentation, as we currently are
mal_printf("Found loop, but it contains unresolved control flow "
"functions that may not return, shared code, or overlaps "
"with another loop in a strange way "
"will instrument after write %s[%d]\n",FILE__,__LINE__);
bblLoop = NULL;
}
}
// found no loop! instrument after write operation
if ( !bblLoop ) {
mal_printf("Instrumenting after overwrite instruction at %lx in block "
"[%lx %lx), loopID=%d %s[%d]\n",faultInsnAddr,
faultBlocks[0]->getStartAddress(), faultBlocks[0]->getEndAddress(),
loop->getID(), FILE__, __LINE__);
assert(blockToLoop.find(faultBlocks[0]->getStartAddress()) == blockToLoop.end());
for (unsigned bidx= 0; bidx < faultBlocks.size(); bidx++) {
loop->blocks.insert(faultBlocks[bidx]);
blockToLoop[faultBlocks[bidx]->getStartAddress()] = loop->getID();
}
loop->instrumentOneWrite(faultInsnAddr,faultFuncs);
// we're only going to write to one spot, set write target and writeInsns
}
else {
// . Instrument the loop
loop->instrumentOverwriteLoop(faultInsnAddr);
mal_printf("Instrumenting loop for write at %lx, loop has %lx blocks, "
"loopID=%d %s[%d]\n", faultInsnAddr,
loop->blocks.size(), loop->getID(), FILE__, __LINE__);
}
// make a shadow page and restore write privileges to the page
makeShadow_setRights(writeTarget, loop);
loop->setActive(true);
proc()->finalizeInsertionSet(false);
}
bool HybridAnalysisOW::registerCodeOverwriteCallbacks
(BPatchCodeOverwriteBeginCallback cbBegin,
BPatchCodeOverwriteEndCallback cbEnd)
{
bpatchBeginCB = cbBegin;
bpatchEndCB = cbEnd;
return true;
}
bool HybridAnalysisOW::removeCodeOverwriteCallbacks()
{
bool ret = false;
if (bpatchBeginCB && bpatchEndCB) {
ret = true;
}
bpatchBeginCB = NULL;
bpatchEndCB = NULL;
return ret;
}
bool HybridAnalysisOW::getActiveLoops
(std::vector<HybridAnalysisOW::owLoop*> &active)
{
std::set<HybridAnalysisOW::owLoop*>::iterator lit;
for (lit = loops.begin(); lit != loops.end(); lit++) {
if ((*lit)->isActive()) {
active.push_back(*lit);
}
}
return !active.empty();
}
bool HybridAnalysisOW::activeOverwritePages(std::set<Address> &pages)
{
std::vector<HybridAnalysisOW::owLoop*> active;
getActiveLoops(active);
for (vector<HybridAnalysisOW::owLoop*>::iterator lit = active.begin();
lit != active.end();
lit++)
{
for (map<Address,unsigned char*>::iterator pit = (*lit)->shadowMap.begin();
pit != (*lit)->shadowMap.end();
pit++)
{
pages.insert(pit->first);
}
}
return !pages.empty();
}