about summary refs log tree commit diff
path: root/afl-dyninst.cc
diff options
context:
space:
mode:
authorNguyễn Gia Phong <cnx@loang.net>2024-11-04 16:49:47 +0900
committerNguyễn Gia Phong <cnx@loang.net>2024-11-04 17:10:03 +0900
commitf9d644e7f357a21aa64a5de27408b74cbd345dc8 (patch)
tree7829136efbd1f4b12816f884af2c053b54a7704f /afl-dyninst.cc
parentcff2d88f97f983683f4caf0e8f87a4f858b486be (diff)
downloadafl-dyninst-f9d644e7f357a21aa64a5de27408b74cbd345dc8.tar.gz
Rework build recipe
Diffstat (limited to 'afl-dyninst.cc')
-rw-r--r--afl-dyninst.cc693
1 files changed, 693 insertions, 0 deletions
diff --git a/afl-dyninst.cc b/afl-dyninst.cc
new file mode 100644
index 0000000..cb580d0
--- /dev/null
+++ b/afl-dyninst.cc
@@ -0,0 +1,693 @@
+// Binary instrumentation tool
+//
+// SPDX-FileCopyrightText: 2016 Aleksandar Nikolic <anikolich@sourcefire.com>
+// SPDX-FileCopyrightText: 2018-2021 Marc "van Hauser" Heuse <vh@thc.org>
+// SPDX-License-Identifier: Apache-2.0
+//
+// SPDX-FileCopyrightText: 2024 Nguyễn Gia Phong <cnx@loang.net>
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+#include "afl-dyninst.h"
+
+// DyninstAPI includes
+#include "BPatch.h"
+#include "BPatch_flowGraph.h"
+#include "BPatch_point.h"
+
+#include <cstdlib>
+#include <fcntl.h>
+#include <getopt.h>
+#include <iostream>
+#include <sstream>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <vector>
+
+using namespace std;
+using namespace Dyninst;
+
+// cmd line options
+char *originalBinary;
+char *instrumentedBinary;
+char *entryPointName = NULL;
+int verbose = 0;
+
+Dyninst::Address entryPoint;
+set<string> todo;
+set<string> instrumentLibraries;
+set<string> runtimeLibraries;
+set<string> skipAddresses;
+set<string> onlyAddresses;
+set<unsigned long> exitAddresses;
+unsigned int bbMinSize = 10;
+int bbSkip = 0, performance = 1;
+bool skipMainModule = false, do_bb = true, dynfix = false;
+unsigned long int insertions = 0;
+uintptr_t mapaddr = 0;
+
+BPatch_function *save_rdi;
+BPatch_function *restore_rdi;
+
+const char *functions[] = {"main", "_main", "_initproc", "_init", "start", "_start", NULL};
+
+static const char *OPT_STR = "fi:o:l:e:E:vs:dr:m:S:I:Dx";
+static const char *USAGE = " -vxD -i <binary> -o <binary> -e <address> -E <address> -s <number> -S <funcname> -I <funcname> -m <size>\n \
+  -i: input binary \n \
+  -o: output binary\n \
+  -r: runtime library to instrument (path to, repeat for more than one)\n \
+  -e: entry point address to patch (required for stripped binaries)\n \
+  -E: exit point - force exit(0) at this address (repeat for more than one)\n \
+  -s: number of initial basic blocks to skip in binary\n \
+  -m: minimum size of a basic bock to instrument (default: 10)\n \
+  -I: only instrument this function and nothing else (repeat for more than one)\n \
+  -S: do not instrument this function (repeat for more than one)\n \
+  -D: instrument only a simple fork server and also forced exit functions\n \
+  -x: experimental performance mode (~25-50% speed improvement)\n \
+  -v: verbose output\n";
+
+bool parseOptions(int argc, char **argv) {
+  int c;
+
+  while ((c = getopt(argc, argv, OPT_STR)) != -1) {
+    switch ((char)c) {
+    case 'x':
+      performance++;
+      /*
+            if (performance == 3) {
+      #if ( __amd64__ || __x86_64__ )
+              fprintf(stderr, "Warning: performance level 3 is currently totally experimental\n");
+      #else
+              fprintf(stderr, "Warning: maximum performance level for non-intelx64 x86 is 2\n");
+              performance = 2;
+      #endif
+            } else*/
+      if (performance > 2) performance = 2;
+      break;
+    case 'I':
+      onlyAddresses.insert(optarg);
+      break;
+    case 'S':
+      skipAddresses.insert(optarg);
+      break;
+    case 'e':
+      if ((entryPoint = strtoul(optarg, NULL, 16)) < 0x1000)
+        entryPointName = optarg;
+      break;
+    case 'i':
+      originalBinary = optarg;
+      instrumentLibraries.insert(optarg);
+      break;
+    case 'o':
+      instrumentedBinary = optarg;
+      break;
+    case 'E':
+      exitAddresses.insert(strtoul(optarg, NULL, 16));
+      break;
+    case 'r':
+      runtimeLibraries.insert(optarg);
+      break;
+    case 's':
+      bbSkip = atoi(optarg);
+      break;
+    case 'm':
+      bbMinSize = atoi(optarg);
+      break;
+    case 'D':
+      do_bb = false;
+      break;
+    case 'v':
+      verbose++;
+      break;
+    default:
+      cerr << "Usage: " << argv[0] << USAGE;
+      return false;
+    }
+  }
+
+  if (originalBinary == NULL) {
+    cerr << "Input binary is required!" << endl;
+    cerr << "Usage: " << argv[0] << USAGE;
+    return false;
+  }
+
+  if (instrumentedBinary == NULL) {
+    cerr << "Output binary is required!" << endl;
+    cerr << "Usage: " << argv[0] << USAGE;
+    return false;
+  }
+
+  return true;
+}
+
+BPatch_function *findFuncByName(BPatch_image *appImage, char *funcName) {
+  BPatch_Vector<BPatch_function *> funcs;
+
+  if (NULL == appImage->findFunction(funcName, funcs) || !funcs.size() || NULL == funcs[0]) {
+    cerr << "Failed to find " << funcName << " function." << endl;
+    return NULL;
+  }
+
+  return funcs[0];
+}
+
+// insert callback to initialization function in the instrumentation library
+// either at _init or at manualy specified entry point.
+bool insertCallToInit(BPatch_addressSpace *appBin, BPatch_function *instIncFunc, BPatch_module *module, BPatch_function *funcInit, bool install_hack) {
+  /* Find the instrumentation points */
+  vector<BPatch_point *> points;
+  vector<BPatch_point *> *funcEntry = funcInit->findPoint(BPatch_entry);
+  BPatch_image *appImage = appBin->getImage();
+  BPatchSnippetHandle *handle;
+
+  if (NULL == funcEntry) {
+    cerr << "Failed to find entry for function. " << endl;
+    return false;
+  }
+
+  // THIS BLOCK IS DISABLED - dyninst is too volatile for this to work reliably
+  // disabled because performance can not be greater than 2
+  if (performance >= 3 && install_hack == true) {
+    cout << "Inserting global variables" << endl;
+    // we set up a fake map so we do not have crashes if the the forkserver
+    // is not installed in _init but later for speed reasons.
+    // we could also check in the bb() code if map == 0 but that would
+    // cost precious instructions.
+    BPatch_variableExpr *fakemap = appBin->malloc(65536);
+    BPatch_constExpr fakemap_ptr(fakemap->getBaseAddr());
+    BPatch_variableExpr *map = appBin->malloc(*(appImage->findType("size_t")), "map");
+    BPatch_arithExpr initmap(BPatch_assign, *map, fakemap_ptr);
+
+    appBin->insertSnippet(initmap, *funcEntry, BPatch_firstSnippet);
+    BPatch_constExpr map_ptr(map->getBaseAddr());
+    BPatch_variableExpr *prev_id = appBin->malloc(*(appImage->findType("size_t")), "prev_id");
+    BPatch_arithExpr initprevid(BPatch_assign, *prev_id, BPatch_constExpr(0));
+
+    appBin->insertSnippet(initprevid, *funcEntry);
+    BPatch_Vector<BPatch_snippet *> instArgs;
+    cout << "Inserting init callback." << endl;
+    instArgs.push_back(&map_ptr);
+    BPatch_funcCallExpr instIncExpr(*instIncFunc, instArgs);
+
+    handle = appBin->insertSnippet(instIncExpr, *funcEntry, BPatch_callBefore, BPatch_lastSnippet);
+  } else {
+    BPatch_Vector<BPatch_snippet *> instArgs;
+    cout << "Inserting init callback." << endl;
+    BPatch_funcCallExpr instIncExpr(*instIncFunc, instArgs);
+
+    handle = appBin->insertSnippet(instIncExpr, *funcEntry, BPatch_callBefore, BPatch_lastSnippet);
+  }
+
+  if (!handle) {
+    cerr << "Failed to insert init callback." << endl;
+    return false;
+  }
+  return true;
+}
+
+// inserts a callback for each basic block assigning it an instrumentation
+// time 16bit random ID just as afl
+bool insertBBCallback(BPatch_addressSpace *appBin, BPatch_function *curFunc, char *funcName, BPatch_function *instBBIncFunc, int *bbIndex) {
+  BPatch_image *appImage = appBin->getImage();
+  BPatch_flowGraph *appCFG = curFunc->getCFG();
+  unsigned short randID;
+
+  if (!appCFG) {
+    cerr << "Failed to find CFG for function " << funcName << endl;
+    return false;
+  }
+
+  BPatch_Set<BPatch_basicBlock *> allBlocks;
+  if (!appCFG->getAllBasicBlocks(allBlocks)) {
+    cerr << "Failed to find basic blocks for function " << funcName << endl;
+    return false;
+  } else if (allBlocks.size() == 0) {
+    cerr << "No basic blocks for function " << funcName << endl;
+    return false;
+  }
+
+  BPatch_Set<BPatch_basicBlock *>::iterator iter;
+  for (iter = allBlocks.begin(); iter != allBlocks.end(); iter++) {
+    if (*bbIndex < bbSkip || (*iter)->size() < bbMinSize) { // skip over first bbSkip bbs or below minimum size
+      (*bbIndex)++;
+      continue;
+    }
+
+    BPatch_point *bbEntry = (*iter)->findEntryPoint();
+
+    if (performance >= 1) {
+      if ((*iter)->isEntryBlock() == false) {
+        bool good = false;
+
+        BPatch_Vector<BPatch_basicBlock *> sources;
+        (*iter)->getSources(sources);
+        for (unsigned int i = 0; i < sources.size() && good == false; i++) {
+          BPatch_Vector<BPatch_basicBlock *> targets;
+          sources[i]->getTargets(targets);
+          if (targets.size() > 1)
+            good = true;
+        }
+        if (good == false)
+          continue;
+      }
+    }
+
+    unsigned long address = (*iter)->getStartAddress();
+
+    randID = rand() % USHRT_MAX;
+    if (verbose >= 1) {
+      cout << "Instrumenting Basic Block 0x" << hex << address << " of " << funcName << " with size " << dec << (*iter)->size() << " with random id " << randID << "/0x" << hex << randID << endl;
+    }
+
+    if (NULL == bbEntry) {
+      // warn the user, but continue
+      cerr << "Failed to find entry for basic block at 0x" << hex << address << endl;
+      (*bbIndex)++;
+      continue;
+    }
+
+    BPatchSnippetHandle *handle;
+
+    // level 3 is disabled
+    if (performance >= 3) {
+      // these are dummy instructions we overwrite later
+      BPatch_variableExpr *pid = appImage->findVariable("prev_id");
+      BPatch_arithExpr new_prev_id(BPatch_assign, *pid, BPatch_arithExpr(BPatch_divide, BPatch_constExpr(8), BPatch_constExpr(2)));
+
+      handle = appBin->insertSnippet(new_prev_id, *bbEntry, BPatch_lastSnippet);
+      BPatch_variableExpr *map = appImage->findVariable("map");
+      BPatch_variableExpr *pid2 = appImage->findVariable("prev_id");
+      BPatch_arithExpr map_idx(BPatch_arithExpr(BPatch_plus, *map, BPatch_arithExpr(BPatch_divide, *pid2, BPatch_constExpr(2))));
+
+      if (mapaddr == 0) {
+        printf("Map for AFL is installed at: %p\n", (void *)map->getBaseAddr());
+        mapaddr = (uintptr_t)map->getBaseAddr();
+      }
+      handle = appBin->insertSnippet(map_idx, *bbEntry, BPatch_firstSnippet);
+    } else {
+      BPatch_Vector<BPatch_snippet *> instArgs1;
+      BPatch_Vector<BPatch_snippet *> instArgs;
+      BPatch_constExpr bbId(randID);
+
+      instArgs.push_back(&bbId);
+      BPatch_funcCallExpr instIncExpr(*instBBIncFunc, instArgs);
+
+
+      handle = appBin->insertSnippet(instIncExpr, *bbEntry, BPatch_callBefore);
+    }
+
+    if (!handle) {
+      // warn the user, but continue to next bb
+      cerr << "Failed to insert instrumention in basic block at 0x" << hex << address << endl;
+      (*bbIndex)++;
+      continue;
+    } else
+      insertions++;
+
+    (*bbIndex)++;
+  }
+
+  return true;
+}
+
+int main(int argc, char **argv) {
+  char *func2patch = NULL;
+  int loop;
+
+  if (argc < 3 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--h", 3) == 0) {
+    cout << "Usage: " << argv[0] << USAGE;
+    return false;
+  }
+
+  if (!parseOptions(argc, argv)) {
+    return EXIT_FAILURE;
+  }
+
+  BPatch bpatch;
+
+  if (performance >= 2) {
+    bpatch.setSaveFPR(false);
+    bpatch.setTrampRecursive(true);
+  }
+
+  const char *dyninstapi_rt_lib_old = getenv("DYNINSTAPI_RT_LIB");
+  if (setenv("DYNINSTAPI_RT_LIB", dyninstapi_rt_lib, true) != 0) {
+    cerr << "Failed to set DYNINSTAPI_RT_LIB\n"; // TODO: explain
+    return EXIT_FAILURE;
+  }
+  BPatch_addressSpace *appBin = bpatch.openBinary(originalBinary, instrumentLibraries.size() != 1);
+  if (dyninstapi_rt_lib_old == NULL)
+    unsetenv("DYNINSTAPI_RT_LIB");
+  else
+    setenv("DYNINSTAPI_RT_LIB", dyninstapi_rt_lib_old, true);
+  if (appBin == NULL) {
+    cerr << "Failed to open binary" << endl;
+    return EXIT_FAILURE;
+  }
+
+  BPatch_image *appImage = appBin->getImage();
+
+  // get and iterate over all modules, instrumenting only the default and manually specified ones
+  vector<BPatch_module *> *modules = appImage->getModules();
+  vector<BPatch_module *>::iterator moduleIter;
+  vector<BPatch_function *> *funcsInModule;
+  BPatch_module *defaultModule = NULL, *firstModule = NULL;
+  string defaultModuleName;
+
+  // look for _init
+  if (defaultModuleName.empty()) {
+    for (loop = 0; functions[loop] != NULL && func2patch == NULL; loop++) {
+      for (moduleIter = modules->begin(); moduleIter != modules->end(); ++moduleIter) {
+        vector<BPatch_function *>::iterator funcsIterator;
+        char moduleName[1024];
+
+        if (firstModule == NULL)
+          firstModule = (*moduleIter);
+        (*moduleIter)->getName(moduleName, 1024);
+        funcsInModule = (*moduleIter)->getProcedures();
+        if (verbose >= 2)
+          cout << "Looking for init function " << functions[loop] << " in " << moduleName << endl;
+        for (funcsIterator = funcsInModule->begin(); funcsIterator != funcsInModule->end(); ++funcsIterator) {
+          char funcName[1024];
+
+          (*funcsIterator)->getName(funcName, 1024);
+          if (verbose >= 3 && loop == 0)
+            printf("module: %s function: %s\n", moduleName, funcName);
+          if (string(funcName) == string(functions[loop])) {
+            func2patch = (char *)functions[loop];
+            defaultModuleName = string(moduleName);
+            defaultModule = (*moduleIter);
+            if (verbose >= 1) {
+              cout << "Found " << func2patch << " in " << moduleName << endl;
+            }
+            break;
+          }
+        }
+        if (!defaultModuleName.empty())
+          break;
+      }
+      if (func2patch != NULL)
+        break;
+    }
+  }
+  // last resort, by name of the binary
+  if (defaultModuleName.empty())
+    defaultModuleName = string(originalBinary).substr(string(originalBinary).find_last_of("\\/") + 1);
+  if (defaultModule == NULL)
+    defaultModule = firstModule;
+
+  if (!appBin->loadLibrary(afl_dyninst_lib)) {
+    cerr << "Failed to open instrumentation library " << afl_dyninst_lib << endl;
+    cerr << "It needs to be located in the current working directory." << endl;
+    return EXIT_FAILURE;
+  }
+
+  /* Find code coverage functions in the instrumentation library */
+  BPatch_function *initAflForkServer;
+
+  BPatch_function *bbCallback = findFuncByName(appImage, (char *)"bbCallback");
+  BPatch_function *forceCleanExit = findFuncByName(appImage, (char *)"forceCleanExit");
+
+  if (do_bb == true) {
+    if (performance >= 3)
+      initAflForkServer = findFuncByName(appImage, (char *)"initAflForkServerVar");
+    else
+      initAflForkServer = findFuncByName(appImage, (char *)"initAflForkServer");
+  } else
+    initAflForkServer = findFuncByName(appImage, (char *)"initOnlyAflForkServer");
+
+  if (!initAflForkServer || !bbCallback || !forceCleanExit) {
+    cerr << "Instrumentation library lacks callbacks!" << endl;
+    return EXIT_FAILURE;
+  }
+
+  int bbIndex = 0;
+
+  // if an entrypoint was set then find function, else find _init
+  BPatch_function *funcToPatch = NULL;
+
+  if (entryPoint == 0 && entryPointName == NULL) {
+    if (func2patch == NULL) {
+      cerr << "Couldn't locate _init, specify entry point manually with -e 0xaddr" << endl;
+      return EXIT_FAILURE;
+    }
+    BPatch_Vector<BPatch_function *> funcs;
+    defaultModule->findFunction(func2patch, funcs);
+    if (!funcs.size()) {
+      cerr << "Couldn't locate _init, specify entry point manually with -e 0xaddr" << endl;
+      return EXIT_FAILURE;
+    }
+    // there should really be only one
+    funcToPatch = funcs[0];
+  } else {
+    if (entryPointName != NULL) {
+      for (moduleIter = modules->begin(); moduleIter != modules->end() && funcToPatch == 0; ++moduleIter) {
+        BPatch_Vector<BPatch_function *> funcs;
+        (*moduleIter)->findFunction(entryPointName, funcs);
+        if (funcs.size() > 0) {
+          char moduleName[1024];
+
+          funcToPatch = funcs[0];
+          defaultModule = (*moduleIter);
+          defaultModule->getName(moduleName, 1024);
+          defaultModuleName = string(moduleName);
+          printf("Found entypoint %s in module %s\n", entryPointName, moduleName);
+          break;
+        }
+      }
+    }
+    if (!funcToPatch) {
+      if (verbose > 1)
+        printf("Looking for entrypoint %p\n", (char *)entryPoint);
+      funcToPatch = defaultModule->findFunctionByEntry(entryPoint);
+      if (!funcToPatch && defaultModule != firstModule) {
+        funcToPatch = firstModule->findFunctionByEntry(entryPoint);
+        if (funcToPatch)
+          defaultModule = firstModule;
+      }
+      if (!funcToPatch) { // ok lets go hardcore ...
+        if (verbose > 1)
+          printf("OK we did not find the entrypoint so far, lets dig deeper ...\n");
+        for (moduleIter = modules->begin(); moduleIter != modules->end() && funcToPatch != NULL; ++moduleIter) {
+          vector<BPatch_function *>::iterator funcsIterator;
+          funcToPatch = (*moduleIter)->findFunctionByEntry(entryPoint);
+          if (funcToPatch)
+            defaultModule = (*moduleIter);
+        }
+      }
+      if (funcToPatch && verbose >= 1) {
+        char moduleName[1024];
+
+        defaultModule->getName(moduleName, 1024);
+        defaultModuleName = string(moduleName);
+        printf("Found entypoint %p in module %s\n", (void *)entryPoint, moduleName);
+      }
+    }
+  }
+  if (!funcToPatch) {
+    cerr << "Couldn't locate function at given entry point. " << endl;
+    cerr << "Try: readelf -ls " << originalBinary << " | egrep 'Entry|FUNC.*GLOBAL.*DEFAULT' | egrep -v '@|UND'" << endl;
+    return EXIT_FAILURE;
+  }
+  if (!insertCallToInit(appBin, initAflForkServer, defaultModule, funcToPatch, true)) {
+    cerr << "Could not insert init callback at given entry point." << endl;
+    return EXIT_FAILURE;
+  }
+
+  for (moduleIter = modules->begin(); moduleIter != modules->end(); ++moduleIter) {
+    char moduleName[1024];
+
+    (*moduleIter)->getName(moduleName, 1024);
+    if ((*moduleIter)->isSharedLib()) {
+      if (instrumentLibraries.find(moduleName) == instrumentLibraries.end() && string(moduleName).find(".so") != string::npos) {
+        cout << "Skipping library: " << moduleName << endl;
+        continue;
+      }
+    }
+
+    if (string(moduleName).find(defaultModuleName) != string::npos) {
+      if (skipMainModule)
+        continue;
+    }
+
+    if (do_bb == true) {
+      cout << "Instrumenting module: " << moduleName << endl;
+      vector<BPatch_function *> *allFunctions = (*moduleIter)->getProcedures();
+      vector<BPatch_function *>::iterator funcIter;
+      // iterate over all functions in the module
+      for (funcIter = allFunctions->begin(); funcIter != allFunctions->end(); ++funcIter) {
+        BPatch_function *curFunc = *funcIter;
+        char funcName[1024];
+        int do_patch = 1;
+
+        curFunc->getName(funcName, 1024);
+        if (string(funcName) == string("_init") || string(funcName) == string("__libc_csu_init") || string(funcName) == string("_start")) {
+          if (verbose)
+            cout << "Skipping instrumenting function " << funcName << endl;
+          continue; // here's a bug on hlt // XXX: check what happens if removed
+        }
+        if (!skipAddresses.empty()) {
+          set<string>::iterator saiter;
+          for (saiter = skipAddresses.begin(); saiter != skipAddresses.end() && do_patch == 1; saiter++)
+            if (*saiter == string(funcName))
+              do_patch = 0;
+          if (do_patch == 0) {
+            cout << "Skipping instrumenting function " << funcName << endl;
+            continue;
+          }
+        }
+        if (!onlyAddresses.empty()) {
+          do_patch = 0;
+          set<string>::iterator saiter;
+          for (saiter = onlyAddresses.begin(); saiter != onlyAddresses.end() && do_patch == 1; saiter++)
+            if (*saiter == string(funcName))
+              do_patch = 1;
+          if (do_patch == 0) {
+            cout << "Skipping instrumenting function " << funcName << endl;
+            continue;
+          }
+        }
+        insertBBCallback(appBin, curFunc, funcName, bbCallback, &bbIndex);
+      }
+    }
+  }
+
+  if (!exitAddresses.empty()) {
+    cout << "Instrumenting forced exit addresses." << endl;
+    set<unsigned long>::iterator uliter;
+
+    for (uliter = exitAddresses.begin(); uliter != exitAddresses.end(); uliter++) {
+      if (*uliter > 0 && (signed long)*uliter != -1) {
+        funcToPatch = defaultModule->findFunctionByEntry(*uliter);
+        if (!funcToPatch) {
+          cerr << "Could not find enty point 0x" << hex << *uliter << " (continuing)" << endl;
+        } else {
+          if (!insertCallToInit(appBin, forceCleanExit, defaultModule, funcToPatch, false))
+            cerr << "Could not insert force clean exit callback at 0x" << hex << *uliter << " (continuing)" << endl;
+        }
+      }
+    }
+  }
+
+  cout << "Saving the instrumented binary to " << instrumentedBinary << " ..." << endl;
+  // Output the instrumented binary
+  BPatch_binaryEdit *appBinr = dynamic_cast<BPatch_binaryEdit *>(appBin);
+
+  if (!appBinr->writeFile(instrumentedBinary)) {
+    cerr << "Failed to write output file: " << instrumentedBinary << endl;
+    return EXIT_FAILURE;
+  }
+  todo.insert(instrumentedBinary);
+
+  if (!runtimeLibraries.empty()) {
+    cout << "Instrumenting runtime libraries." << endl;
+    set<string>::iterator rtLibIter;
+    for (rtLibIter = runtimeLibraries.begin(); rtLibIter != runtimeLibraries.end(); rtLibIter++) {
+      BPatch_addressSpace *libBin = bpatch.openBinary((*rtLibIter).c_str(), false);
+
+      if (libBin == NULL) {
+        cerr << "Failed to open binary " << *rtLibIter << endl;
+        return EXIT_FAILURE;
+      }
+      BPatch_image *libImg = libBin->getImage();
+
+      vector<BPatch_module *> *modules = libImg->getModules();
+      moduleIter = modules->begin();
+      for (; moduleIter != modules->end(); ++moduleIter) {
+        char moduleName[1024];
+
+        (*moduleIter)->getName(moduleName, 1024);
+        cout << "Instrumenting module: " << moduleName << endl;
+        vector<BPatch_function *> *allFunctions = (*moduleIter)->getProcedures();
+        vector<BPatch_function *>::iterator funcIter;
+        // iterate over all functions in the module
+        for (funcIter = allFunctions->begin(); funcIter != allFunctions->end(); ++funcIter) {
+          BPatch_function *curFunc = *funcIter;
+          char funcName[1024];
+          int do_patch = 1;
+
+          curFunc->getName(funcName, 1024);
+          if (string(funcName) == string("_init") || string(funcName) == string("__libc_csu_init") || string(funcName) == string("_start"))
+            continue;
+          if (!skipAddresses.empty()) {
+            set<string>::iterator saiter;
+            for (saiter = skipAddresses.begin(); saiter != skipAddresses.end() && do_patch == 1; saiter++)
+              if (*saiter == string(funcName))
+                do_patch = 0;
+            if (do_patch == 0) {
+              cout << "Skipping instrumenting function " << funcName << endl;
+              continue;
+            }
+          }
+
+          insertBBCallback(libBin, curFunc, funcName, bbCallback, &bbIndex);
+        }
+      }
+      appBinr = dynamic_cast<BPatch_binaryEdit *>(libBin);
+      if (!appBinr->writeFile((*rtLibIter + ".ins").c_str())) {
+        cerr << "Failed to write output file: " << (*rtLibIter + ".ins").c_str() << endl;
+        return EXIT_FAILURE;
+      } else {
+        cout << "Saved the instrumented library to " << (*rtLibIter + ".ins").c_str() << "." << endl;
+        todo.insert(*rtLibIter + ".ins");
+      }
+    }
+  }
+
+  printf("Did a total of %lu basic block insertions\n", insertions);
+
+  if (performance >= 3) {
+    int fd;
+    struct stat st;
+    uint64_t i, found = 0;
+    unsigned char *ptr;
+
+    unsigned char snip1[] = {0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00};
+    unsigned char snip2[] = {0x08, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00};
+    unsigned char fullsnip[] = {0x53, 0x50, 0x41, 0x52, 0x48, 0xBB, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x03, 0x48, 0x85, 0xc0, 0x74, 0x28, 0x49, 0xBA, 0x08, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x41, 0x8b, 0x1a, 0x66, 0x81, 0xf3, 0x99, 0x99, 0x48, 0x0f, 0xb7, 0xdb, 0x80, 0x04, 0x18, 0x01, 0x66, 0x41, 0x8b, 0x1a, 0x66, 0xd1, 0xfb, 0x66, 0x41, 0x89, 0x1a, 0x41, 0x5a, 0x58, 0x5b, 0x90, 0x90, 0x90, 0x90};
+    memcpy(snip1, (char *)&mapaddr, sizeof(mapaddr));
+    memcpy(fullsnip + 6, (char *)&mapaddr, sizeof(mapaddr));
+    mapaddr += sizeof(mapaddr);
+    memcpy(snip2, (char *)&mapaddr, sizeof(mapaddr));
+    memcpy(fullsnip + 24, (char *)&mapaddr, sizeof(mapaddr));
+    set<string>::iterator fn;
+    for (fn = todo.begin(); fn != todo.end(); fn++) {
+      cout << "Reinstrumenting " << *fn << " ..." << endl;
+      if ((fd = open((const char *)(fn->c_str()), O_RDWR)) == -1 || fstat(fd, &st) != 0) {
+        cerr << "Error: file is gone: " << *fn << endl;
+        exit(-1);
+      }
+      if ((size_t)st.st_size < (size_t)sizeof(fullsnip)) {
+        cerr << "Error: somethings horrible wrong here with " << *fn << " ..." << endl;
+        continue;
+      }
+      ptr = (unsigned char *)mmap(NULL, st.st_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
+      for (i = 2; i < (size_t)st.st_size - (size_t)sizeof(fullsnip); i++) {
+        if (memcmp(ptr + i, snip1, sizeof(snip1)) == 0 && memcmp(ptr + i + sizeof(snip1) + 4, snip2, sizeof(snip2)) == 0) {
+          found++;
+          fullsnip[0x27] = rand() % 256;
+          fullsnip[0x28] = rand() % 256;
+          memcpy(ptr + i - 2, fullsnip, sizeof(fullsnip));
+        }
+      }
+      // printf("found %lu entries, snipsize %u\n", found, (unsigned int)sizeof(fullsnip));
+      munmap((void *)ptr, st.st_size);
+      close(fd);
+    }
+    if (found == insertions) {
+      printf("SUCCESS! Performance level 3 succeeded :)\n");
+    } else {
+      fprintf(stderr, "Error: can not complete performance level 3, could not find all insertions (%lu of %lu).\n", found, insertions);
+      exit(-1);
+    }
+  }
+
+  cout << "All done! Happy fuzzing!" << endl;
+  return EXIT_SUCCESS;
+}