diff --git a/doc/zzuf.1.in b/doc/zzuf.1.in index 0cd69ba..2576614 100644 --- a/doc/zzuf.1.in +++ b/doc/zzuf.1.in @@ -10,7 +10,8 @@ zzuf \- multiple purpose fuzzer [\fB\-T\fR \fIseconds\fR] [\fB\-U\fR \fIseconds\fR] [\fB\-M\fR \fImebibytes\fR] [\fB\-b\fR \fIranges\fR] [\fB\-p\fR \fIports\fR] [\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR] [\fB\-l\fR \fIlist\fR] [\fB\-I\fR \fIinclude\fR] -[\fB\-E\fR \fIexclude\fR] [\fIPROGRAM\fR [\fIARGS\fR]...] +[\fB\-E\fR \fIexclude\fR] [\fB\-O\fR \fIopmode\fR] +[\fIPROGRAM\fR [\fIARGS\fR]...] .br \fBzzuf \-h\fR | \fB\-\-help\fR .br @@ -194,6 +195,22 @@ Fuzz the application's network input. By default \fBzzuf\fR only fuzzes files. Only INET (IPv4) and INET6 (IPv6) connections are fuzzed. Other protocol families are not yet supported. .TP +\fB\-O\fR, \fB\-\-opmode\fR=\fImode\fR +Use operating mode \fImode\fR. Valid values for \fImode\fR are: +.RS +.TP +\fBpreload\fR +override functions by preloading libzzuf into the executable using the +system's dynamic linker +.TP +\fBcopy\fR +temporarily copy files that need to be fuzzed +.RE +.IP +The default value for \fImode\fR is \fBpreload\fR. \fBcopy\fR is useful on +platforms that do not support dynamic linker injection, for instance when +fuzzing a Cocoa application on Mac OS X. +.TP \fB\-p\fR, \fB\-\-ports\fR=\fIranges\fR Only fuzz network ports that are in \fIranges\fR. By default \fBzzuf\fR fuzzes all ports. The port considered is the listening port if the socket diff --git a/src/myfork.c b/src/myfork.c index 014c858..20d3ea1 100644 --- a/src/myfork.c +++ b/src/myfork.c @@ -69,7 +69,7 @@ # undef ZZUF_RLIMIT_CPU #endif -static int run_process(struct opts *, int[][2]); +static int run_process(struct child *child, struct opts *, int[][2]); #if defined HAVE_WINDOWS_H static void rep32(uint8_t *buf, void *addr); @@ -100,11 +100,11 @@ int myfork(struct child *child, struct opts *opts) } } - pid = run_process(opts, pipes); + pid = run_process(child, opts, pipes); if(pid < 0) { /* FIXME: close pipes */ - fprintf(stderr, "error launching `%s'\n", opts->newargv[0]); + fprintf(stderr, "error launching `%s'\n", child->newargv[0]); return -1; } @@ -132,7 +132,7 @@ static void setenv(char const *name, char const *value, int overwrite) } #endif -static int run_process(struct opts *opts, int pipes[][2]) +static int run_process(struct child *child, struct opts *opts, int pipes[][2]) { char buf[64]; #if defined HAVE_FORK @@ -241,12 +241,14 @@ static int run_process(struct opts *opts, int pipes[][2]) libpath = bigbuf; } - setenv(PRELOAD, libpath, 1); + /* Only preload the library in preload mode */ + if (opts->opmode == OPMODE_PRELOAD) + setenv(PRELOAD, libpath, 1); free(libpath); - if(execvp(opts->newargv[0], opts->newargv)) + if(execvp(child->newargv[0], child->newargv)) { - perror(opts->newargv[0]); + perror(child->newargv[0]); exit(EXIT_FAILURE); } @@ -265,13 +267,13 @@ static int run_process(struct opts *opts, int pipes[][2]) DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[2][1]), pid, &sinfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); sinfo.dwFlags = STARTF_USESTDHANDLES; - ret = CreateProcess(NULL, opts->newargv[0], NULL, NULL, FALSE, + ret = CreateProcess(NULL, child->newargv[0], NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo); if(!ret) return -1; /* Get the child process's entry point address */ - epaddr = (void *)get_entry_point(opts->newargv[0], + epaddr = (void *)get_entry_point(child->newargv[0], pinfo.dwProcessId); if(!epaddr) return -1; diff --git a/src/opts.c b/src/opts.c index f32a000..e60e5f2 100644 --- a/src/opts.c +++ b/src/opts.c @@ -32,6 +32,7 @@ void _zz_opts_init(struct opts *opts) { + opts->opmode = OPMODE_PRELOAD; opts->fuzzing = opts->bytes = opts->list = opts->ports = NULL; opts->allow = NULL; opts->protect = opts->refuse = NULL; @@ -51,7 +52,6 @@ void _zz_opts_init(struct opts *opts) opts->delay = 0; opts->lastlaunch = 0; - opts->newargv = NULL; opts->maxchild = 1; opts->nchild = 0; opts->maxcrashes = 1; @@ -61,9 +61,14 @@ void _zz_opts_init(struct opts *opts) void _zz_opts_fini(struct opts *opts) { + int i; + if(opts->child) + { + for(i = 0; i < opts->maxchild; i++) + if (opts->child[i].newargv) + free(opts->child[i].newargv); free(opts->child); - if(opts->newargv) - free(opts->newargv); + } } diff --git a/src/opts.h b/src/opts.h index 1596f2c..8a2f086 100644 --- a/src/opts.h +++ b/src/opts.h @@ -16,8 +16,13 @@ struct opts { + enum opmode + { + OPMODE_PRELOAD, + OPMODE_COPY, + } opmode; char **oldargv; - char **newargv; + int oldargc; char *fuzzing, *bytes, *list, *ports, *protect, *refuse, *allow; uint32_t seed; uint32_t endseed; @@ -54,6 +59,7 @@ struct opts double ratio; int64_t date; struct md5 *ctx; + char **newargv; } *child; }; diff --git a/src/zzuf.c b/src/zzuf.c index e5f1b8e..233cb36 100644 --- a/src/zzuf.c +++ b/src/zzuf.c @@ -153,7 +153,7 @@ int main(int argc, char *argv[]) # define OPTSTR_RLIMIT_CPU "" #endif #define OPTSTR "+" OPTSTR_REGEX OPTSTR_RLIMIT_MEM OPTSTR_RLIMIT_CPU \ - "a:Ab:B:C:dD:e:f:F:ij:l:mnp:P:qr:R:s:St:U:vxhV" + "a:Ab:B:C:dD:e:f:F:ij:l:mnO:p:P:qr:R:s:St:U:vxhV" #define MOREINFO "Try `%s --help' for more information.\n" int option_index = 0; static struct myoption long_options[] = @@ -182,6 +182,7 @@ int main(int argc, char *argv[]) { "md5", 0, NULL, 'm' }, { "max-memory", 1, NULL, 'M' }, { "network", 0, NULL, 'n' }, + { "opmode", 1, NULL, 'O' }, { "ports", 1, NULL, 'p' }, { "protect", 1, NULL, 'P' }, { "quiet", 0, NULL, 'q' }, @@ -256,6 +257,7 @@ int main(int argc, char *argv[]) break; case 'F': fprintf(stderr, "%s: `-F' is deprecated, use `-j'\n", argv[0]); + _zz_opts_fini(opts); return EXIT_FAILURE; case 'i': /* --stdin */ setenv("ZZUF_STDIN", "1", 1); @@ -294,6 +296,21 @@ int main(int argc, char *argv[]) setenv("ZZUF_NETWORK", "1", 1); network = 1; break; + case 'O': /* --opmode */ + if(myoptarg[0] == '=') + myoptarg++; + if (!strcmp(myoptarg, "preload")) + opts->opmode = OPMODE_PRELOAD; + else if (!strcmp(myoptarg, "copy")) + opts->opmode = OPMODE_COPY; + else + { + fprintf(stderr, "%s: invalid operating mode -- `%s'\n", + argv[0], myoptarg); + _zz_opts_fini(opts); + return EXIT_FAILURE; + } + break; case 'p': /* --ports */ opts->ports = myoptarg; break; @@ -385,7 +402,23 @@ int main(int argc, char *argv[]) _zz_setratio(opts->minratio, opts->maxratio); _zz_setseed(opts->seed); - /* If asked to read from the standard input */ + if(opts->fuzzing) + _zz_fuzzing(opts->fuzzing); + if(opts->bytes) + _zz_bytes(opts->bytes); + if(opts->list) + _zz_list(opts->list); + if(opts->protect) + _zz_protect(opts->protect); + if(opts->refuse) + _zz_refuse(opts->refuse); + + /* Needed for stdin mode and for copy opmode. */ + _zz_fd_init(); + + /* + * Mode 1: asked to read from the standard input + */ if(myoptind >= argc) { if(opts->verbose) @@ -404,106 +437,114 @@ int main(int argc, char *argv[]) } loop_stdin(opts); - - _zz_opts_fini(opts); - return EXIT_SUCCESS; } - - /* If asked to launch programs */ + /* + * Mode 2: asked to launch programs + */ + else + { #if defined HAVE_REGEX_H - if(cmdline) - { - int dashdash = 0; - - for(i = myoptind + 1; i < argc; i++) + if(cmdline) { - if(dashdash) - include = merge_file(include, argv[i]); - else if(!strcmp("--", argv[i])) - dashdash = 1; - else if(argv[i][0] != '-') - include = merge_file(include, argv[i]); - } - } + int dashdash = 0; - if(include) - setenv("ZZUF_INCLUDE", include, 1); - if(exclude) - setenv("ZZUF_EXCLUDE", exclude, 1); + for(i = myoptind + 1; i < argc; i++) + { + if(dashdash) + include = merge_file(include, argv[i]); + else if(!strcmp("--", argv[i])) + dashdash = 1; + else if(argv[i][0] != '-') + include = merge_file(include, argv[i]); + } + } + + if(include) + setenv("ZZUF_INCLUDE", include, 1); + if(exclude) + setenv("ZZUF_EXCLUDE", exclude, 1); #endif - setenv("ZZUF_DEBUG", debug ? debug > 1 ? "2" : "1" : "0", 1); - setenv("ZZUF_DEBUGFD", DEBUG_FILENO_STR, 1); + setenv("ZZUF_DEBUG", debug ? debug > 1 ? "2" : "1" : "0", 1); + setenv("ZZUF_DEBUGFD", DEBUG_FILENO_STR, 1); - if(opts->fuzzing) - setenv("ZZUF_FUZZING", opts->fuzzing, 1); - if(opts->bytes) - setenv("ZZUF_BYTES", opts->bytes, 1); - if(opts->list) - setenv("ZZUF_LIST", opts->list, 1); - if(opts->ports) - setenv("ZZUF_PORTS", opts->ports, 1); - if(opts->allow && opts->allow[0] == '!') - setenv("ZZUF_DENY", opts->allow, 1); - else if(opts->allow) - setenv("ZZUF_ALLOW", opts->allow, 1); - if(opts->protect) - setenv("ZZUF_PROTECT", opts->protect, 1); - if(opts->refuse) - setenv("ZZUF_REFUSE", opts->refuse, 1); + if(opts->fuzzing) + setenv("ZZUF_FUZZING", opts->fuzzing, 1); + if(opts->bytes) + setenv("ZZUF_BYTES", opts->bytes, 1); + if(opts->list) + setenv("ZZUF_LIST", opts->list, 1); + if(opts->ports) + setenv("ZZUF_PORTS", opts->ports, 1); + if(opts->allow && opts->allow[0] == '!') + setenv("ZZUF_DENY", opts->allow, 1); + else if(opts->allow) + setenv("ZZUF_ALLOW", opts->allow, 1); + if(opts->protect) + setenv("ZZUF_PROTECT", opts->protect, 1); + if(opts->refuse) + setenv("ZZUF_REFUSE", opts->refuse, 1); #if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM - if(opts->maxmem >= 0) - { - char buf[32]; - snprintf(buf, 32, "%i", opts->maxmem); - setenv("ZZUF_MEMORY", buf, 1); - } + if(opts->maxmem >= 0) + { + char buf[32]; + snprintf(buf, 32, "%i", opts->maxmem); + setenv("ZZUF_MEMORY", buf, 1); + } #endif - /* Allocate memory for children handling */ - opts->child = malloc(opts->maxchild * sizeof(struct child)); - for(i = 0; i < opts->maxchild; i++) - opts->child[i].status = STATUS_FREE; - opts->nchild = 0; + /* Allocate memory for children handling */ + opts->child = malloc(opts->maxchild * sizeof(struct child)); + for(i = 0; i < opts->maxchild; i++) + opts->child[i].status = STATUS_FREE; + opts->nchild = 0; - /* Create new argv */ - opts->oldargv = argv; - opts->newargv = malloc((argc - myoptind + 1) * sizeof(char *)); - memcpy(opts->newargv, argv + myoptind, (argc - myoptind) * sizeof(char *)); - opts->newargv[argc - myoptind] = (char *)NULL; - - /* Main loop */ - while(opts->nchild || opts->seed < opts->endseed) - { - /* Spawn new children, if necessary */ - spawn_children(opts); - - /* Cleanup dead or dying children */ - clean_children(opts); - - /* Read data from children */ - read_children(opts); - - if(opts->maxcrashes && opts->crashes >= opts->maxcrashes - && opts->nchild == 0) + /* Create new argv */ + opts->oldargc = argc; + opts->oldargv = argv; + for(i = 0; i < opts->maxchild; i++) { - if(opts->verbose) - fprintf(stderr, - "zzuf: maximum crash count reached, exiting\n"); - break; + int len = argc - myoptind; + opts->child[i].newargv = malloc((len + 1) * sizeof(char *)); + memcpy(opts->child[i].newargv, argv + myoptind, + len * sizeof(char *)); + opts->child[i].newargv[len] = (char *)NULL; } - if(opts->maxtime && _zz_time() - opts->starttime >= opts->maxtime - && opts->nchild == 0) + /* Main loop */ + while(opts->nchild || opts->seed < opts->endseed) { - if(opts->verbose) - fprintf(stderr, - "zzuf: maximum running time reached, exiting\n"); - break; + /* Spawn new children, if necessary */ + spawn_children(opts); + + /* Cleanup dead or dying children */ + clean_children(opts); + + /* Read data from children */ + read_children(opts); + + if(opts->maxcrashes && opts->crashes >= opts->maxcrashes + && opts->nchild == 0) + { + if(opts->verbose) + fprintf(stderr, + "zzuf: maximum crash count reached, exiting\n"); + break; + } + + if(opts->maxtime && _zz_time() - opts->starttime >= opts->maxtime + && opts->nchild == 0) + { + if(opts->verbose) + fprintf(stderr, + "zzuf: maximum running time reached, exiting\n"); + break; + } } } /* Clean up */ + _zz_fd_fini(); _zz_opts_fini(opts); return opts->crashes ? EXIT_FAILURE : EXIT_SUCCESS; @@ -518,18 +559,6 @@ static void loop_stdin(struct opts *opts) if(opts->md5) ctx = _zz_md5_init(); - if(opts->fuzzing) - _zz_fuzzing(opts->fuzzing); - if(opts->bytes) - _zz_bytes(opts->bytes); - if(opts->list) - _zz_list(opts->list); - if(opts->protect) - _zz_protect(opts->protect); - if(opts->refuse) - _zz_refuse(opts->refuse); - - _zz_fd_init(); _zz_register(0); for(;;) @@ -578,7 +607,6 @@ static void loop_stdin(struct opts *opts) } _zz_unregister(0); - _zz_fd_fini(); } static void finfo(FILE *fp, struct opts *opts, uint32_t seed) @@ -665,10 +693,60 @@ static void spawn_children(struct opts *opts) if(opts->child[i].status == STATUS_FREE) break; + /* Prepare required files, if necessary */ + if (opts->opmode == OPMODE_COPY) + { + char tmpname[4096]; + char *tmpdir; + FILE *fpin; + int j, k = 0, fdout; + + tmpdir = getenv("TEMP"); + if (!tmpdir || !*tmpdir) + tmpdir = "/tmp"; + + for (j = optind + 1; j < opts->oldargc; j++) + { + fpin = fopen(opts->oldargv[j], "r"); + if (!fpin) + continue; + + sprintf(tmpname, "%s/zzuf.%i.XXXXXX", tmpdir, (int)getpid()); + fdout = mkstemp(tmpname); + if (fdout < 0) + { + fclose(fpin); + continue; + } + + opts->child[i].newargv[j - optind] = strdup(tmpname); + + _zz_register(k); + while(!feof(fpin)) + { + uint8_t buf[BUFSIZ]; + size_t n = fread(buf, 1, BUFSIZ, fpin); + if (n <= 0) + break; + _zz_fuzz(k, buf, n); + _zz_addpos(k, n); + write(fdout, buf, n); + } + _zz_unregister(k); + + fclose(fpin); + close(fdout); + + k++; + } + } + + /* Launch process */ if (myfork(&opts->child[i], opts) < 0) { - fprintf(stderr, "error launching `%s'\n", opts->newargv[0]); + fprintf(stderr, "error launching `%s'\n", opts->child[i].newargv[0]); opts->seed++; + /* FIXME: clean up OPMODE_COPY files here */ return; } @@ -684,7 +762,7 @@ static void spawn_children(struct opts *opts) if(opts->verbose) { finfo(stderr, opts, opts->child[i].seed); - fprintf(stderr, "launched `%s'\n", opts->newargv[0]); + fprintf(stderr, "launched `%s'\n", opts->child[i].newargv[0]); } opts->lastlaunch = now; @@ -814,6 +892,19 @@ static void clean_children(struct opts *opts) if(opts->child[i].fd[j] >= 0) close(opts->child[i].fd[j]); + if (opts->opmode == OPMODE_COPY) + { + for (j = optind + 1; j < opts->oldargc; j++) + { + if (opts->child[i].newargv[j - optind] != opts->oldargv[j]) + { + unlink(opts->child[i].newargv[j - optind]); + free(opts->child[i].newargv[j - optind]); + opts->child[i].newargv[j - optind] = opts->oldargv[j]; + } + } + } + if(opts->md5) { _zz_md5_fini(md5sum, opts->child[i].ctx); @@ -975,6 +1066,7 @@ static void usage(void) #if defined HAVE_REGEX_H printf( " [-I include] [-E exclude]"); #endif + printf(" [-O mode]\n"); printf("\n"); printf(" [PROGRAM [--] [ARGS]...]\n"); printf(" zzuf -h | --help\n"); @@ -1008,6 +1100,7 @@ static void usage(void) printf(" -M, --max-memory maximum child virtual memory in MiB (default %u)\n", DEFAULT_MEM); #endif printf(" -n, --network fuzz network input\n"); + printf(" -O, --opmode use operating mode ([preload] copy)\n"); printf(" -p, --ports only fuzz network destination ports in \n"); printf(" -P, --protect protect bytes and characters in \n"); printf(" -q, --quiet do not print children's messages\n");