summary refs log tree commit diff
diff options
context:
space:
mode:
authorNguyễn Gia Phong <cnx@loang.net>2024-11-05 12:47:14 +0900
committerNguyễn Gia Phong <cnx@loang.net>2024-11-05 16:27:39 +0900
commit1fe6579eb183c8762c6ce41fc2ace742c8f5a5c9 (patch)
tree169abf126dbd3ff291e93194252a17ca3d91cd00
parent3aa0dbdc85f9a4cadac0152ed1bfb4cad7c3174b (diff)
downloadafl-dyninst-1fe6579eb183c8762c6ce41fc2ace742c8f5a5c9.tar.gz
Rework afl-dyninst's CLI
It is now compatible with help2man,
hence the new barebone manual page.
-rw-r--r--CHANGES10
-rw-r--r--Makefile20
-rw-r--r--afl-dyninst.cc207
3 files changed, 124 insertions, 113 deletions
diff --git a/CHANGES b/CHANGES
index 57f4bf2..058282a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,13 +3,17 @@ Tag: 1.0.0
 	Build recipe rework
 
 	Additions:
-	- Compliance with REUSE 3.0 has been added
+	- Manual page for afl-dyninst(1)
+	- Positional arguments for input and output binaries
+	  of afl-dyninst(1)
+	- Long command-line options for afl-dyninst(1)
 	- Make target uninstall
+	- Compliance with REUSE 3.0
 
 	Removals:
 	- Support for Dyninst < 10
-	- Command-line options -dfl
-
+	- afl-dyninst(1)'s command-line options -dfilo
+	- afl-dyninst's unnecessary info logs
 
 Remote: https://github.com/vanhauser-thc/afl-dyninst
 Date: 2021-05-21
diff --git a/Makefile b/Makefile
index df078b0..9c7135c 100644
--- a/Makefile
+++ b/Makefile
@@ -14,13 +14,14 @@ VERSION ::= 1.0.0
 PREFIX ?= /usr/local
 BINDIR ::= $(DESTDIR)$(PREFIX)/bin
 LIBDIR ::= $(DESTDIR)$(PREFIX)/lib
+MANDIR ::= $(DESTDIR)$(PREFIX)/share/man
 
 # Override on non-FHS environments
 DYNINST_LIB ?= $(LIBDIR)
 # Override to . for local development
 AFL_DYNINST_LIB ?= $(LIBDIR)
 
-CPPFLAGS = -DPROG=\"afl-dyninst\" -DVERSION=\"$(VERSION)\"\
+CPPFLAGS = -DVERSION=\"$(VERSION)\"\
 	-DAFL_DYNINST_LIB=\"$(AFL_DYNINST_LIB)/libafldyninst.so\"\
 	-DDYNINSTAPI_RT_LIB=\"$(DYNINST_LIB)/libdyninstAPI_RT.so\"
 CXXFLAGS += -std=c++11 -Wextra -Werror
@@ -29,8 +30,9 @@ LDLIBS += -ldyninstAPI
 
 BIN ::= afl-dyninst afl-dyninst-env
 LIB ::= libafldyninst.so
+MAN ::= afl-dyninst.1
 
-all: $(BIN) $(LIB)
+all: $(BIN) $(LIB) $(MAN)
 
 afl-dyninst: afl-dyninst.cc
 
@@ -41,10 +43,13 @@ afl-dyninst-env: afl-dyninst-env.m4
 %.so: %.cc
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $< -o $@
 
+%.1: %
+	help2man --no-info --output=$@ `realpath $<`
+
 clean:
-	rm -f $(BIN) $(LIB)
+	rm -f $(BIN) $(LIB) $(MAN)
 
-install: $(BIN:%=$(BINDIR)/%) $(LIB:%=$(LIBDIR)/%)
+install: $(BIN:%=$(BINDIR)/%) $(LIB:%=$(LIBDIR)/%) $(MAN:%.1:$(MANDIR)/man1/%.1)
 
 $(BINDIR)/%: %
 	install -Dm 755 $< $@
@@ -52,5 +57,10 @@ $(BINDIR)/%: %
 $(LIBDIR)/%: %
 	install -Dm 644 $< $@
 
+$(MANDIR)/man1/%.1 : %.1
+	install -Dm 644 $< $@
+
 uninstall:
-	rm -f $(BIN:%=$(BINDIR)/%) $(LIB:%=$(LIBDIR)/%)
+	rm -f $(BIN:%=$(BINDIR)/%)
+	rm -f $(LIB:%=$(LIBDIR)/%)
+	rm -f $(MAN:%.1:$(MANDIR)/man1/%.1)
diff --git a/afl-dyninst.cc b/afl-dyninst.cc
index 5fb3d38..745b072 100644
--- a/afl-dyninst.cc
+++ b/afl-dyninst.cc
@@ -32,14 +32,11 @@ 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;
@@ -55,95 +52,6 @@ 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;
 
@@ -189,14 +97,12 @@ bool insertCallToInit(BPatch_addressSpace *appBin, BPatch_function *instIncFunc,
 
     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);
@@ -315,17 +221,110 @@ bool insertBBCallback(BPatch_addressSpace *appBin, BPatch_function *curFunc, cha
 }
 
 int main(int argc, char **argv) {
-  char *func2patch = NULL;
-  int loop;
+  static const char *const USAGE = "Usage: afl-dyninst"
+    " [OPTIONS]... INFILE OUTFILE\n\n"
+    "Instrument binary to be fuzzed by AFL.\n\n"
+    "Options:\n"
+    "  -h, --help               show this help message and exit\n"
+    "      --version            show program's version number and exit\n"
+    "  -e ADDR, --entry=ADDR    "
+    "entry point address to patch (required for stripped binaries)\n"
+    "  -E ADDR, --exit=ADDR     force exit(0) at this address (multiple use)\n"
+    "  -D                       "
+    "instrument only a simple fork server and also forced exit functions\n"
+    "  -r PATH, --library=PATH  runtime library to instrument (multiple use)\n"
+    "  -I NAME, --include=NAME  "
+    "instrument only this function and nothing else (multiple use)\n"
+    "  -S NAME, --exclude=NAME  "
+    "do not instrument this function (multiple use)\n"
+    "  -m N, --min-size=N       "
+    "minimum size of a basic bock to instrument (default to 10)\n"
+    "  -s N, --skip=N           "
+    "number of initial basic blocks to skip in binary\n"
+    "  -v, --verbose            enable verbose output (up to 3 levels)\n"
+    "  -x                       "
+    "experimental performance mode (multiple use, ~25-50% speed improvement)\n";
 
-  if (argc < 3 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--h", 3) == 0) {
-    cout << "Usage: " << argv[0] << USAGE;
-    return false;
+  int c;
+  int option_index = 0;
+  const static struct option long_options[] = {
+    {"help", no_argument, NULL, 'h'},
+    {"entry", required_argument, NULL, 'e'},
+    {"exit", required_argument, NULL, 'E'},
+    {"library", required_argument, NULL, 'r'},
+    {"include", required_argument, NULL, 'I'},
+    {"exclude", required_argument, NULL, 'S'},
+    {"min-size", required_argument, NULL, 'm'},
+    {"skip", required_argument, NULL, 's'},
+    {"verbose", no_argument, NULL, 'v'},
+    {"version", no_argument, NULL, 0},
+    {NULL, 0, NULL, 0},
+  };
+  while ((c = getopt_long(argc, argv, "he:E:Dr:I:S:m:s:vx",
+                          long_options, &option_index)) != -1) {
+    switch (c) {
+    case 0:
+      if (strcmp(long_options[option_index].name, "version") == 0) {
+        std::cout << "afl-dyninst " VERSION "\n";
+        return EXIT_SUCCESS;
+      }
+      __builtin_unreachable();
+    case 'h':
+      std::cout << USAGE;
+      return EXIT_SUCCESS;
+    case 'e':
+      if ((entryPoint = strtoul(optarg, NULL, 16)) < 0x1000)
+        entryPointName = optarg;
+      break;
+    case 'E':
+      exitAddresses.insert(strtoul(optarg, NULL, 16));
+      break;
+    case 'D':
+      do_bb = false;
+      break;
+    case 'r':
+      runtimeLibraries.insert(optarg);
+      break;
+    case 'I':
+      onlyAddresses.insert(optarg);
+      break;
+    case 'S':
+      skipAddresses.insert(optarg);
+      break;
+    case 'm':
+      bbMinSize = atoi(optarg);
+      break;
+    case 's':
+      bbSkip = atoi(optarg);
+      break;
+    case 'v':
+      verbose++;
+      break;
+    case 'x':
+      performance++;
+      if (performance > 2) {
+//#if ( __amd64__ || __x86_64__ )
+//        std::cerr << "Warning: performance level 3 is currently totally experimental\n";
+//#else
+//        std::cerr << "Warning: maximum performance level for non-intelx64 x86 is 2\n";
+        performance = 2;
+//#endif
+      }
+      break;
+    default:
+      return EXIT_FAILURE;
+    }
   }
-
-  if (!parseOptions(argc, argv)) {
+  if (optind >= argc) {
+    std::cerr << USAGE;
+    return EXIT_FAILURE;
+  }
+  const char *const originalBinary = argv[optind];
+  if (++optind >= argc) {
+    std::cerr << USAGE;
     return EXIT_FAILURE;
   }
+  const char *const instrumentedBinary = argv[optind];
 
   BPatch bpatch;
 
@@ -359,8 +358,9 @@ int main(int argc, char **argv) {
   string defaultModuleName;
 
   // look for _init
+  char *func2patch = NULL;
   if (defaultModuleName.empty()) {
-    for (loop = 0; functions[loop] != NULL && func2patch == NULL; loop++) {
+    for (unsigned loop = 0; functions[loop] != NULL && func2patch == NULL; loop++) {
       for (moduleIter = modules->begin(); moduleIter != modules->end(); ++moduleIter) {
         vector<BPatch_function *>::iterator funcsIterator;
         char moduleName[1024];
@@ -569,7 +569,6 @@ int main(int argc, char **argv) {
     }
   }
 
-  cout << "Saving the instrumented binary to " << instrumentedBinary << " ..." << endl;
   // Output the instrumented binary
   BPatch_binaryEdit *appBinr = dynamic_cast<BPatch_binaryEdit *>(appBin);
 
@@ -681,7 +680,5 @@ int main(int argc, char **argv) {
       exit(-1);
     }
   }
-
-  cout << "All done! Happy fuzzing!" << endl;
   return EXIT_SUCCESS;
 }