diff --git a/doc/zzuf.1 b/doc/zzuf.1 index 5229043..64fe34e 100644 --- a/doc/zzuf.1 +++ b/doc/zzuf.1 @@ -2,13 +2,13 @@ .SH NAME zzuf \- multiple purpose fuzzer .SH SYNOPSIS -\fBzzuf\fR [\fB\-cdinqS\fR] [\fB\-r\fR \fIratio\fR] [\fB\-s\fR \fIseed\fR | \fB\-s\fR \fIstart:stop\fR] +\fBzzuf\fR [\fB\-cdiMnqS\fR] [\fB\-r\fR \fIratio\fR] [\fB\-s\fR \fIseed\fR | \fB\-s\fR \fIstart:stop\fR] .br - [\fB\-F\fR \fIforks\fR] [\fB\-C\fR \fIcrashes\fR] [\fB\-B\fR \fIbytes\fR] [\fB\-T\fR \fIseconds\fR] + [\fB\-F\fR \fIforks\fR] [\fB\-C\fR \fIcrashes\fR] [\fB\-B\fR \fIbytes\fR] [\fB\-T\fR \fIseconds\fR] .br - [\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR] + [\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR] .br - [\fB\-I\fR \fIinclude\fR] [\fB\-E\fR \fIexclude\fR] [\fIPROGRAM\fR [\fIARGS\fR]...] + [\fB\-I\fR \fIinclude\fR] [\fB\-E\fR \fIexclude\fR] [\fIPROGRAM\fR [\fIARGS\fR]...] .br \fBzzuf \-h\fR | \fB\-\-help\fR .br @@ -94,6 +94,10 @@ and you only want specific files to be fuzzed. Multiple \fB\-I\fR flags can be specified, in which case files matching any one of the regular expressions will be fuzzed. See also the \fB\-c\fR flag. .TP +\fB\-M\fR, \fB\-\-md5\fR +Instead of displaying the program's output, just print the MD5 digest of that +output. +.TP \fB\-n\fR, \fB\-\-network\fR Fuzz the application's network input. By default \fBzzuf\fR only fuzzes files. .TP diff --git a/src/Makefile.am b/src/Makefile.am index 538dc60..9fdd958 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ COMMON = random.c random.h fd.c fd.h fuzz.c fuzz.h bin_PROGRAMS = zzuf -zzuf_SOURCES = zzuf.c $(COMMON) +zzuf_SOURCES = zzuf.c $(COMMON) md5.c md5.h zzuf_CFLAGS = -DLIBDIR=\"$(libdir)/zzuf\" pkglib_LTLIBRARIES = libzzuf.la diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..15d9cce --- /dev/null +++ b/src/md5.c @@ -0,0 +1,237 @@ +/* + * zzuf - general purpose fuzzer + * Copyright (c) 2002, 2007 Sam Hocevar + * All Rights Reserved + * + * $Id$ + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + */ + +/* + * md5.c: MD5 computation. Written and put into the public domain + * by Colin Plumb in 1993. + */ + +#include "config.h" + +#if defined HAVE_STDINT_H +# include +#elif defined HAVE_INTTYPES_H +# include +#endif +#include +#include + +#include "md5.h" + +struct md5 { + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; +}; + +static void transform(uint32_t buf[4], uint32_t in[16]); + +#define HIGHFIRST +#ifdef __i386__ +#undef HIGHFIRST +#endif + +#ifndef HIGHFIRST +#define swapbytes(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +void swapbytes(uint8_t *buf, unsigned bytes) +{ + uint32_t t; + do + { + t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + bytes -= 4; + } while(bytes > 0); +} +#endif + +struct md5 *_zz_md5_init(void) +{ + struct md5 *ctx = malloc(sizeof(struct md5)); + + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; + + return ctx; +} + +void _zz_md5_add(struct md5 *ctx, uint8_t *buf, unsigned len) +{ + uint32_t t; + + t = ctx->bits[0]; + if((ctx->bits[0] = t + ((uint32_t)len << 3)) < t) + ctx->bits[1]++; + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; + + if(t) + { + uint8_t *p = (uint8_t *)ctx->in + t; + + t = 64 - t; + if(len < t) + { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + swapbytes(ctx->in, 64); + transform(ctx->buf, (uint32_t *)ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) + { + memcpy(ctx->in, buf, 64); + swapbytes(ctx->in, 64); + transform(ctx->buf, (uint32_t *)ctx->in); + buf += 64; + len -= 64; + } + + memcpy(ctx->in, buf, len); +} + +void _zz_md5_fini(uint8_t *digest, struct md5 *ctx) +{ + unsigned count; + uint8_t *p; + + count = (ctx->bits[0] >> 3) & 0x3F; + p = ctx->in + count; + *p++ = 0x80; + + count = 64 - 1 - count; + if(count < 8) + { + memset(p, 0, count); + swapbytes(ctx->in, 64); + transform(ctx->buf, (uint32_t *) ctx->in); + memset(ctx->in, 0, 56); + } + else + memset(p, 0, count - 8); + + swapbytes(ctx->in, 56); + memcpy(ctx->in + 56, ctx->bits, 8); + transform(ctx->buf, (uint32_t *)ctx->in); + swapbytes((uint8_t *)ctx->buf, 16); + memcpy(digest, ctx->buf, 16); + free(ctx); +} + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +static void transform(uint32_t buf[4], uint32_t in[16]) +{ + uint32_t a = buf[0]; + uint32_t b = buf[1]; + uint32_t c = buf[2]; + uint32_t d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..0d06a65 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,24 @@ +/* + * zzuf - general purpose fuzzer + * Copyright (c) 2002, 2007 Sam Hocevar + * All Rights Reserved + * + * $Id$ + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + */ + +/* + * md5.h: MD5 computation + */ + +struct md5; + +extern struct md5 *_zz_md5_init(void); +extern void _zz_md5_add(struct md5 *ctx, uint8_t *buf, unsigned len); +extern void _zz_md5_fini(uint8_t *digest, struct md5 *ctx); + diff --git a/src/zzuf.c b/src/zzuf.c index e3d2759..107fb12 100644 --- a/src/zzuf.c +++ b/src/zzuf.c @@ -41,6 +41,7 @@ #include "random.h" #include "fd.h" #include "fuzz.h" +#include "md5.h" static void spawn_child(char **); static void clean_children(void); @@ -69,6 +70,7 @@ static struct child_list int fd[3]; /* 0 is debug, 1 is stderr, 2 is stdout */ int bytes, seed; time_t date; + struct md5 *ctx; } *child_list; static int maxforks = 1, child_count = 0, maxcrashes = 1, crashes = 0; @@ -76,6 +78,7 @@ static int seed = 0; static int endseed = 1; static int quiet = 0; static int maxbytes = -1; +static int md5 = 0; static double maxtime = -1.0; #define ZZUF_FD_SET(fd, p_fdset, maxfd) \ @@ -114,6 +117,7 @@ int main(int argc, char *argv[]) { "max-forks", 1, NULL, 'F' }, { "stdin", 0, NULL, 'i' }, { "include", 1, NULL, 'I' }, + { "md5", 0, NULL, 'M' }, { "network", 0, NULL, 'n' }, { "protect", 1, NULL, 'P' }, { "quiet", 0, NULL, 'q' }, @@ -125,11 +129,11 @@ int main(int argc, char *argv[]) { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'v' }, }; - int c = getopt_long(argc, argv, "B:cC:dE:F:iI:nP:qr:R:s:ST:hv", + int c = getopt_long(argc, argv, "B:cC:dE:F:iI:MnP:qr:R:s:ST:hv", long_options, &option_index); # else # define MOREINFO "Try `%s -h' for more information.\n" - int c = getopt(argc, argv, "B:cC:dE:F:iI:nP:qr:R:s:ST:hv"); + int c = getopt(argc, argv, "B:cC:dE:F:iI:MnP:qr:R:s:ST:hv"); # endif if(c == -1) break; @@ -172,6 +176,9 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } break; + case 'M': /* --md5 */ + md5 = 1; + break; case 'n': /* --network */ setenv("ZZUF_NETWORK", "1", 1); break; @@ -219,6 +226,12 @@ int main(int argc, char *argv[]) /* If asked to read from the standard input */ if(optind >= argc) { + uint8_t md5sum[16]; + struct md5 *ctx = NULL; + + if(md5) + ctx = _zz_md5_init(); + if(endseed != seed + 1) { printf("%s: seed ranges are incompatible with stdin fuzzing\n", @@ -245,7 +258,22 @@ int main(int argc, char *argv[]) _zz_fuzz(0, buf, ret); _zz_addpos(0, ret); - fwrite(buf, 1, ret, stdout); + if(md5) + _zz_md5_add(ctx, buf, ret); + else + fwrite(buf, 1, ret, stdout); + } + + if(md5) + { + _zz_md5_fini(md5sum, ctx); + fprintf(stdout, "zzuf[seed=%i]: %.02x%.02x%.02x%.02x%.02x%.02x" + "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x\n", + seed, md5sum[0], md5sum[1], md5sum[2], md5sum[3], + md5sum[4], md5sum[5], md5sum[6], md5sum[7], + md5sum[8], md5sum[9], md5sum[10], md5sum[11], + md5sum[12], md5sum[13], md5sum[14], md5sum[15]); + fflush(stdout); } _zz_unregister(0); @@ -369,7 +397,7 @@ static void spawn_child(char **argv) pid_t pid; int i, j; - /* Find an empty slot */ + /* Find the empty slot */ for(i = 0; i < maxforks; i++) if(child_list[i].status == STATUS_FREE) break; @@ -408,23 +436,24 @@ static void spawn_child(char **argv) perror(argv[0]); exit(EXIT_FAILURE); } - break; - default: - /* We’re the parent, acknowledge spawn */ - child_list[i].date = time(NULL); - child_list[i].pid = pid; - for(j = 0; j < 3; j++) - { - close(fd[j][1]); - child_list[i].fd[j] = fd[j][0]; - } - child_list[i].bytes = 0; - child_list[i].seed = seed; - child_list[i].status = STATUS_RUNNING; - child_count++; - seed++; - break; + return; } + + /* We’re the parent, acknowledge spawn */ + child_list[i].date = time(NULL); + child_list[i].pid = pid; + for(j = 0; j < 3; j++) + { + close(fd[j][1]); + child_list[i].fd[j] = fd[j][0]; + } + child_list[i].bytes = 0; + child_list[i].seed = seed; + child_list[i].status = STATUS_RUNNING; + if(md5) + child_list[i].ctx = _zz_md5_init(); + child_count++; + seed++; } static void clean_children(void) @@ -473,6 +502,7 @@ static void clean_children(void) /* Collect dead children */ for(i = 0; i < maxforks; i++) { + uint8_t md5sum[16]; int status; pid_t pid; @@ -502,6 +532,16 @@ static void clean_children(void) if(child_list[i].fd[j] >= 0) close(child_list[i].fd[j]); + if(md5) + { + _zz_md5_fini(md5sum, child_list[i].ctx); + fprintf(stdout, "zzuf[seed=%i]: %.02x%.02x%.02x%.02x%.02x%.02x" + "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x\n", + child_list[i].seed, md5sum[0], md5sum[1], md5sum[2], + md5sum[3], md5sum[4], md5sum[5], md5sum[6], md5sum[7], + md5sum[8], md5sum[9], md5sum[10], md5sum[11], md5sum[12], + md5sum[13], md5sum[14], md5sum[15]); + } child_list[i].status = STATUS_FREE; child_count--; } @@ -537,7 +577,7 @@ static void read_children(void) /* XXX: cute (i, j) iterating hack */ for(i = 0, j = 0; i < maxforks; i += (j == 2), j = (j + 1) % 3) { - char buf[BUFSIZ]; + uint8_t buf[BUFSIZ]; if(child_list[i].status != STATUS_RUNNING) continue; @@ -551,7 +591,10 @@ static void read_children(void) /* We got data */ if(j != 0) child_list[i].bytes += ret; - if(!quiet || j == 0) + + if(md5 && j > 0) + _zz_md5_add(child_list[i].ctx, buf, ret); + else if(!quiet || j == 0) write((j < 2) ? STDERR_FILENO : STDOUT_FILENO, buf, ret); } else if(ret == 0) @@ -606,10 +649,10 @@ static void version(void) #if defined(HAVE_GETOPT_H) static void usage(void) { - printf("Usage: zzuf [-cdinqS] [-r ratio] [-s seed | -s start:stop]\n"); - printf(" [-F forks] [-C crashes] [-B bytes] [-T seconds]\n"); - printf(" [-P protect] [-R refuse]\n"); - printf(" [-I include] [-E exclude] [PROGRAM [ARGS]...]\n"); + printf("Usage: zzuf [-cdiMnqS] [-r ratio] [-s seed | -s start:stop]\n"); + printf(" [-F forks] [-C crashes] [-B bytes] [-T seconds]\n"); + printf(" [-P protect] [-R refuse]\n"); + printf(" [-I include] [-E exclude] [PROGRAM [ARGS]...]\n"); # ifdef HAVE_GETOPT_LONG printf(" zzuf -h | --help\n"); printf(" zzuf -v | --version\n"); @@ -629,6 +672,7 @@ static void usage(void) printf(" -F, --max-forks number of concurrent children (default 1)\n"); printf(" -i, --stdin fuzz standard input\n"); printf(" -I, --include only fuzz files matching \n"); + printf(" -M, --md5 compute the output's MD5 hash\n"); printf(" -n, --network fuzz network input\n"); printf(" -P, --protect protect bytes and characters in \n"); printf(" -q, --quiet do not print children's messages\n"); @@ -649,6 +693,7 @@ static void usage(void) printf(" -F number of concurrent forks (default 1)\n"); printf(" -i fuzz standard input\n"); printf(" -I only fuzz files matching \n"); + printf(" -M compute the output's MD5 hash\n"); printf(" -n fuzz network input\n"); printf(" -P protect bytes and characters in \n"); printf(" -q do not print the fuzzed application's messages\n");