about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--utils/argv_fuzzing/Makefile15
-rw-r--r--utils/argv_fuzzing/README.md41
-rw-r--r--utils/argv_fuzzing/argv-fuzz-inl.h47
-rw-r--r--utils/argv_fuzzing/argv_fuzz_demo.c28
-rw-r--r--utils/argv_fuzzing/argv_fuzz_persistent_demo.c58
5 files changed, 180 insertions, 9 deletions
diff --git a/utils/argv_fuzzing/Makefile b/utils/argv_fuzzing/Makefile
index 183f6bf8..f016c5a7 100644
--- a/utils/argv_fuzzing/Makefile
+++ b/utils/argv_fuzzing/Makefile
@@ -11,7 +11,7 @@
 #   http://www.apache.org/licenses/LICENSE-2.0
 #
 
-.PHONY: all install clean
+.PHONY: all install clean argv_fuzz_persistent_demo argv_fuzz_demo demo
 
 PREFIX     ?= /usr/local
 BIN_PATH    = $(PREFIX)/bin
@@ -41,7 +41,7 @@ __M32FLAG=$(_M32FLAG:00=-mbe32)
 ___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32)
 M32FLAG=$(___M32FLAG)
 
-all: argvfuzz32.so argvfuzz64.so
+all: argvfuzz32.so argvfuzz64.so demo
 
 argvfuzz32.so: argvfuzz.c
 	-@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)"
@@ -54,5 +54,14 @@ install: argvfuzz32.so argvfuzz64.so
 	if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi
 	if [ -f argvfuzz64.so ]; then set -e; install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi
 
+argv_fuzz_persistent_demo: argv_fuzz_persistent_demo.c
+	afl-clang-fast -fsanitize=address -g -o $@ $^
+
+argv_fuzz_demo: argv_fuzz_demo.c
+	afl-clang-fast -fsanitize=address -g -o $@ $^
+
+demo: argv_fuzz_persistent_demo argv_fuzz_demo
+
 clean:
-	rm -f argvfuzz32.so argvfuzz64.so
+	rm -f argvfuzz32.so argvfuzz64.so argv_fuzz_demo argv_fuzz_persistent_demo
+
diff --git a/utils/argv_fuzzing/README.md b/utils/argv_fuzzing/README.md
index e9224995..a085c098 100644
--- a/utils/argv_fuzzing/README.md
+++ b/utils/argv_fuzzing/README.md
@@ -1,16 +1,45 @@
-# argvfuzz
+# argv_fuzzing feature
+AFL++ supports fuzzing file inputs or standard input. The argv_fuzzing feature
+allows for the fuzzing of arguments passed to a program from the command line
+interface rather than from STDIN.  
 
-AFL++ supports fuzzing file inputs or stdin. When source is available,
-`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin.
+## With source code
+When the source code is available, a specific macro from the `argv-fuzz-inl.h`
+header file can be used to change the program's behavior to build argv from STDIN.
 
+### Without persistent mode
+Conditions needed to use the argv_fuzzing feature:
+1. Include `argv-fuzz-inl.h` header file (`#include "argv-fuzz-inl.h"`)
+2. Identify your main function that parses arguments
+(for example, `int main(int argc, char **argv)`)
+3. Use one of the following macros (near the beginning of the main function)
+to initialize argv with the fuzzer's input:
+   - `AFL_INIT_ARGV();` or
+   - `AFL_INIT_SET0("prog_name");` to preserve `argv[0]`
+   (the name of the program being executed)
+   
+see: [argv_fuzz_demo.c](argv_fuzz_demo.c)
+
+### With persistent mode
+Conditions needed to use the argv_fuzzing feature with persistent mode:
+1. Ensure your target can handle persistent mode fuzzing
+2. Follow instructions in the [llvm_mode persistent mode](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md)
+3. Use one of the following macros near the beginning of the main function and after 
+the buffer initialization (`unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF`):
+   - `AFL_INIT_ARGV_PERSISTENT(buf)`, if you want to 
+   - `AFL_INIT_SET0_PERSISTENT("name_of_binary", buf)`
+
+see: [argv_fuzz_persistent_demo.c](argv_fuzz_persistent_demo.c)
+
+## Binary only
 `argvfuzz` tries to provide the same functionality for binaries. When loaded
 using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace
 argv using the same logic of `argv-fuzz-inl.h`.
 
 A few conditions need to be fulfilled for this mechanism to work correctly:
 
-1. As it relies on hooking the loader, it cannot work on static binaries.
+1. As it relies on hooking the loader, it cannot work on static binaries
 2. If the target binary does not use the default libc's `_start` implementation
    (crt1.o), the hook may not run.
-3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the
-   target binary expects argv to be living on the stack, things may go wrong.
\ No newline at end of file
+3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`.
+Things may go wrong if the target binary expects argv to live on the stack.
diff --git a/utils/argv_fuzzing/argv-fuzz-inl.h b/utils/argv_fuzzing/argv-fuzz-inl.h
index ec22c53b..cb0af2bc 100644
--- a/utils/argv_fuzzing/argv-fuzz-inl.h
+++ b/utils/argv_fuzzing/argv-fuzz-inl.h
@@ -29,6 +29,11 @@
    If you would like to always preserve argv[0], use this instead:
    AFL_INIT_SET0("prog_name");
 
+   To enable persistent fuzzing, use the AFL_INIT_ARGV_PERSISTENT macro with
+   buf as argument, or use AFL_INIT_SET0_PERSISTENT("prog_name", buf)
+   to preserver argv[0]. buf is a pointer to a buffer containing
+   the input data for the current test case being processed defined as:
+   unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
 */
 
 #ifndef _HAVE_ARGV_FUZZ_INL
@@ -53,6 +58,22 @@
                                  \
   } while (0)
 
+#define AFL_INIT_ARGV_PERSISTENT(persistent_buff)            \
+  do {                                                       \
+                                                             \
+    argv = afl_init_argv_persistent(&argc, persistent_buff); \
+                                                             \
+  } while (0)
+
+#define AFL_INIT_SET0_PERSISTENT(_p, persistent_buff)        \
+  do {                                                       \
+                                                             \
+    argv = afl_init_argv_persistent(&argc, persistent_buff); \
+    argv[0] = (_p);                                          \
+    if (!argc) argc = 1;                                     \
+                                                             \
+  } while (0)
+
 #define MAX_CMDLINE_LEN 100000
 #define MAX_CMDLINE_PAR 50000
 
@@ -87,6 +108,32 @@ static char **afl_init_argv(int *argc) {
 
 }
 
+static char **afl_init_argv_persistent(int           *argc,
+                                       unsigned char *persistent_buff) {
+
+  static char *ret[MAX_CMDLINE_PAR];
+
+  unsigned char *ptr = persistent_buff;
+  int            rc = 0;
+
+  while (*ptr && rc < MAX_CMDLINE_PAR) {
+
+    ret[rc] = (char *)ptr;
+    if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++;
+    rc++;
+
+    while (*ptr)
+      ptr++;
+    ptr++;
+
+  }
+
+  *argc = rc;
+
+  return ret;
+
+}
+
 #undef MAX_CMDLINE_LEN
 #undef MAX_CMDLINE_PAR
 
diff --git a/utils/argv_fuzzing/argv_fuzz_demo.c b/utils/argv_fuzzing/argv_fuzz_demo.c
new file mode 100644
index 00000000..6ab1e2e5
--- /dev/null
+++ b/utils/argv_fuzzing/argv_fuzz_demo.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <string.h>
+#include "argv-fuzz-inl.h"
+
+int main(int argc, char **argv) {
+
+  // Initialize the argv array for use with the AFL (American Fuzzy Lop) tool
+  AFL_INIT_ARGV();
+
+  /* Check the number of command line arguments and
+    compare the values of the first two arguments to specific strings.
+    If the number of arguments is not correct or the values do not match,
+    an error message is printed. If the values do match, the program
+    calls the abort() function. */
+  if (argc > 1 && strcmp(argv[1], "XYZ") == 0) {
+
+    if (strcmp(argv[2], "TEST2") == 0) { abort(); }
+
+  } else {
+
+    printf("Bad number of arguments!\n");
+
+  }
+
+  return 0;
+
+}
+
diff --git a/utils/argv_fuzzing/argv_fuzz_persistent_demo.c b/utils/argv_fuzzing/argv_fuzz_persistent_demo.c
new file mode 100644
index 00000000..08a62c62
--- /dev/null
+++ b/utils/argv_fuzzing/argv_fuzz_persistent_demo.c
@@ -0,0 +1,58 @@
+/*
+This file contains a simple fuzzer for testing command line argument parsing
+using persistent mode.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "argv-fuzz-inl.h"
+
+__AFL_FUZZ_INIT();
+
+/* The main function is an entry point for a program.
+   The argc parameter is an integer that indicates the number of arguments
+   passed to the program. The argv parameter is an array of character pointers,
+   with each element pointing to a null-terminated string that represents
+   one of the arguments.
+ */
+int main(int argc, char **argv) {
+
+#ifdef __AFL_HAVE_MANUAL_CONTROL
+  __AFL_INIT();
+#endif
+  unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
+
+  /* __AFL_LOOP() limits the maximum number of iterations before exiting
+     the loop and allowing the program to terminate. It protects against
+     accidental memory leaks and similar issues. */
+  while (__AFL_LOOP(100000)) {
+
+    int len = __AFL_FUZZ_TESTCASE_LEN;
+
+    // Check that the length of the test case is at least 8 bytes
+    if (len < 8) continue;
+
+    // Initialize the command line arguments using the testcase buffer
+    AFL_INIT_ARGV_PERSISTENT(buf);
+
+    /* Check if the first argument is "XYZ" and the second argument is "TEST2"
+       If so, call the "abort" function to terminate the program.
+       Otherwise, print an error message. */
+    if (argc > 1 && strcmp(argv[1], "XYZ") == 0) {
+
+      if (strcmp(argv[2], "TEST2") == 0) { abort(); }
+
+    } else {
+
+      printf("Bad number of arguments!\n");
+
+    }
+
+  }
+
+  /* Exiting the loop allows the program to terminate normally. AFL will restart
+     the process with a clean slate for allocated memory, file descriptors, etc.
+  */
+  return 0;
+
+}