Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev

This commit is contained in:
Andrea Fioraldi
2020-04-03 09:39:12 +02:00
80 changed files with 3568 additions and 2709 deletions

View File

@ -1,2 +1,7 @@
all:
$(CC) $(CFLAGS) -fPIC -shared -g -I ../../include example.c -o libexamplemutator.so
all: libexamplemutator.so
libexamplemutator.so:
$(CC) $(CFLAGS) -D_FORTIFY_SOURCE=2 -O3 -fPIC -shared -g -I ../../include example.c -o libexamplemutator.so
clean:
rm -rf libexamplemutator.so

View File

@ -1,10 +1,10 @@
# Examples for the custom mutator
These are example and helper files for the custom mutator feature.
See [docs/python_mutators.md](../docs/custom_mutators.md) for more information
See [docs/custom_mutators.md](../docs/custom_mutators.md) for more information
Note that if you compile with python3.7 you must use python3 scripts, and if
you use pyton2.7 to compile python2 scripts!
you use python2.7 to compile python2 scripts!
example.c - this is a simple example written in C and should be compiled to a
shared library. Use make to compile it and produce libexamplemutator.so

View File

@ -5,13 +5,25 @@
#include "types.h"
#include <stdlib.h>
#define INITIAL_GROWTH_SIZE (64)
#define RAND_BELOW(limit) (rand() % (limit))
typedef struct{} afl_t;
/* Use in a struct: creates a name_buf and a name_size variable. */
#define BUF_VAR(type, name) \
type * name##_buf; \
size_t name##_size;
/* this filles in `&structptr->something_buf, &structptr->something_size`. */
#define BUF_PARAMS(struct, name) \
(void **)&struct->name##_buf, &struct->name##_size
typedef struct {
} afl_t;
static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
static s8 interesting_8[] = {INTERESTING_8};
static s8 interesting_8[] = {INTERESTING_8};
static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
@ -111,8 +123,8 @@ static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
break;
case 1:
*(u64 *)(out_buf + byte_idx) =
SWAP64((s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
*(u64 *)(out_buf + byte_idx) = SWAP64(
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
break;
}
@ -265,4 +277,66 @@ static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
}
/* This function calculates the next power of 2 greater or equal its argument.
@return The rounded up power of 2 (if no overflow) or 0 on overflow.
*/
static inline size_t next_pow2(size_t in) {
if (in == 0 || in > (size_t)-1)
return 0; /* avoid undefined behaviour under-/overflow */
size_t out = in - 1;
out |= out >> 1;
out |= out >> 2;
out |= out >> 4;
out |= out >> 8;
out |= out >> 16;
return out + 1;
}
/* This function makes sure *size is > size_needed after call.
It will realloc *buf otherwise.
*size will grow exponentially as per:
https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
Will return NULL and free *buf if size_needed is <1 or realloc failed.
@return For convenience, this function returns *buf.
*/
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
/* No need to realloc */
if (likely(size_needed && *size >= size_needed)) return *buf;
/* No initial size was set */
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
/* grow exponentially */
size_t next_size = next_pow2(size_needed);
/* handle overflow */
if (!next_size) { next_size = size_needed; }
/* alloc */
*buf = realloc(*buf, next_size);
*size = *buf ? next_size : 0;
return *buf;
}
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
static inline void swap_bufs(void **buf1, size_t *size1, void **buf2,
size_t *size2) {
void * scratch_buf = *buf1;
size_t scratch_size = *size1;
*buf1 = *buf2;
*size1 = *size2;
*buf2 = scratch_buf;
*size2 = scratch_size;
}
#undef INITIAL_GROWTH_SIZE
#endif

View File

@ -12,6 +12,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define DATA_SIZE (100)
@ -23,32 +24,47 @@ static const char *commands[] = {
};
typedef struct my_mutator {
afl_t *afl;
// any additional data here!
size_t trim_size_current;
int trimmming_steps;
int cur_step;
// Reused buffers:
BUF_VAR(u8, fuzz);
BUF_VAR(u8, data);
BUF_VAR(u8, havoc);
BUF_VAR(u8, trim);
BUF_VAR(u8, pre_save);
} my_mutator_t;
/**
* Initialize this custom mutator
*
* @param[in] afl a pointer to the internal state object. Can be ignored for now.
* @param[in] seed A seed for this mutator - the same seed should always mutate in the same way.
* @param[in] afl a pointer to the internal state object. Can be ignored for
* now.
* @param[in] seed A seed for this mutator - the same seed should always mutate
* in the same way.
* @return Pointer to the data object this custom mutator instance should use.
* There may be multiple instances of this mutator in one afl-fuzz run!
* Returns NULL on error.
* Return NULL on error.
*/
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
srand(seed); // needed also by surgical_havoc_mutate()
srand(seed); // needed also by surgical_havoc_mutate()
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
if (!data) {
perror("afl_custom_init alloc");
return NULL;
}
data->afl = afl;
return data;
@ -63,14 +79,16 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data
* @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on
* error.
* @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case
* @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t afl_custom_fuzz(my_mutator_t *data, uint8_t **buf, size_t buf_size,
uint8_t *add_buf,
size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
u8 **out_buf, uint8_t *add_buf,
size_t add_buf_size, // add_buf can be NULL
size_t max_size) {
@ -78,9 +96,15 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t **buf, size_t buf_size,
// the fuzzer
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size;
if (mutated_size > buf_size) *buf = realloc(*buf, mutated_size);
// maybe_grow is optimized to be quick for reused buffers.
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size);
if (!mutated_out) {
uint8_t *mutated_out = *buf;
*out_buf = NULL;
perror("custom mutator allocation (maybe_grow)");
return 0; /* afl-fuzz will very likely error out after this. */
}
// Randomly select a command string to add as a header to the packet
memcpy(mutated_out, commands[rand() % 3], 3);
@ -94,6 +118,7 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t **buf, size_t buf_size,
}
*out_buf = mutated_out;
return mutated_size;
}
@ -109,29 +134,36 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t **buf, size_t buf_size,
* @param[in] buf Buffer containing the test case to be executed
* @param[in] buf_size Size of the test case
* @param[out] out_buf Pointer to the buffer containing the test case after
* processing. External library should allocate memory for out_buf. AFL++
* will release the memory after saving the test case.
* @return Size of the output buffer after processing
* processing. External library should allocate memory for out_buf.
* The buf pointer may be reused (up to the given buf_size);
* @return Size of the output buffer after processing or the needed amount.
* A return of 0 indicates an error.
*/
size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf) {
size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size,
uint8_t **out_buf) {
size_t out_buf_size;
uint8_t *pre_save_buf = maybe_grow(BUF_PARAMS(data, pre_save), buf_size + 5);
if (!pre_save_buf) {
out_buf_size = buf_size;
perror("custom mutator realloc failed.");
*out_buf = NULL;
return 0;
// External mutator should allocate memory for `out_buf`
*out_buf = malloc(out_buf_size);
memcpy(*out_buf, buf, out_buf_size);
}
return out_buf_size;
memcpy(pre_save_buf + 5, buf, buf_size);
pre_save_buf[0] = 'A';
pre_save_buf[1] = 'F';
pre_save_buf[2] = 'L';
pre_save_buf[3] = '+';
pre_save_buf[4] = '+';
*out_buf = pre_save_buf;
return buf_size + 5;
}
static uint8_t *trim_buf;
static size_t trim_buf_size;
static int trimmming_steps;
static int cur_step;
/**
* This method is called at the start of each trimming operation and receives
* the initial buffer. It should return the amount of iteration steps possible
@ -151,25 +183,36 @@ static int cur_step;
* @param data pointer returned in afl_custom_init for this fuzz case
* @param buf Buffer containing the test case
* @param buf_size Size of the test case
* @return The amount of possible iteration steps to trim the input
* @return The amount of possible iteration steps to trim the input.
* negative on error.
*/
int afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, size_t buf_size) {
int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
size_t buf_size) {
// We simply trim once
trimmming_steps = 1;
data->trimmming_steps = 1;
cur_step = 0;
trim_buf = buf;
trim_buf_size = buf_size;
data->cur_step = 0;
return trimmming_steps;
if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) {
perror("init_trim grow");
return -1;
}
memcpy(data->trim_buf, buf, buf_size);
data->trim_size_current = buf_size;
return data->trimmming_steps;
}
/**
* This method is called for each trimming operation. It doesn't have any
* arguments because we already have the initial buffer from init_trim and we
* can memorize the current state in global variables. This can also save
* can memorize the current state in *data. This can also save
* reparsing steps for each iteration. It should return the trimmed input
* buffer, where the returned data must not exceed the initial input data in
* length. Returning anything that is larger than the original data (passed
@ -179,18 +222,18 @@ int afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, size_t buf_size) {
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[out] out_buf Pointer to the buffer containing the trimmed test case.
* External library should allocate memory for out_buf. AFL++ will release
* the memory after saving the test case.
* @param[out] out_buf_size Pointer to the size of the trimmed test case
* External library should allocate memory for out_buf.
* AFL++ will not release the memory after saving the test case.
* Keep a ref in *data.
* *out_buf = NULL is treated as error.
* @return Pointer to the size of the trimmed test case
*/
void afl_custom_trim(my_mutator_t *data, uint8_t **out_buf, size_t *out_buf_size) {
size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) {
*out_buf_size = trim_buf_size - 1;
*out_buf = data->trim_buf;
// External mutator should allocate memory for `out_buf`
*out_buf = malloc(*out_buf_size);
// Remove the last byte of the trimming input
memcpy(*out_buf, trim_buf, *out_buf_size);
return data->trim_size_current - 1;
}
@ -204,18 +247,18 @@ void afl_custom_trim(my_mutator_t *data, uint8_t **out_buf, size_t *out_buf_size
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param success Indicates if the last trim operation was successful.
* @return The next trim iteration index (from 0 to the maximum amount of
* steps returned in init_trim)
* steps returned in init_trim). negative ret on failure.
*/
int afl_custom_post_trim(my_mutator_t *data, int success) {
int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
if (success) {
++cur_step;
return cur_step;
++data->cur_step;
return data->cur_step;
}
return trimmming_steps;
return data->trimmming_steps;
}
@ -226,26 +269,40 @@ int afl_custom_post_trim(my_mutator_t *data, int success) {
* (Optional)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[inout] buf Pointer to the input data to be mutated and the mutated
* @param[in] buf Pointer to the input data to be mutated and the mutated
* output
* @param[in] buf_size Size of input data
* @param[out] out_buf The output buffer. buf can be reused, if the content
* fits. *out_buf = NULL is treated as error.
* @param[in] max_size Maximum size of the mutated output. The mutation must
* not produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t afl_custom_havoc_mutation(my_mutator_t *data, uint8_t **buf, size_t buf_size,
size_t max_size) {
size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
u8 **out_buf, size_t max_size) {
if (buf_size == 0) {
*buf = realloc(*buf, 1);
**buf = rand() % 256;
*out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1);
if (!*out_buf) {
perror("custom havoc: maybe_grow");
return 0;
}
**out_buf = rand() % 256;
buf_size = 1;
} else {
// We reuse buf here. It's legal and faster.
*out_buf = buf;
}
size_t victim = rand() % buf_size;
(*buf)[victim] += rand() % 10;
(*out_buf)[victim] += rand() % 10;
return buf_size;
@ -292,7 +349,8 @@ uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) {
* @param filename_new_queue File name of the new queue entry
* @param filename_orig_queue File name of the original queue entry
*/
void afl_custom_queue_new_entry(my_mutator_t *data, const uint8_t *filename_new_queue,
void afl_custom_queue_new_entry(my_mutator_t * data,
const uint8_t *filename_new_queue,
const uint8_t *filename_orig_queue) {
/* Additional analysis on the original or new test case */
@ -306,6 +364,11 @@ void afl_custom_queue_new_entry(my_mutator_t *data, const uint8_t *filename_new_
*/
void afl_custom_deinit(my_mutator_t *data) {
free(data->pre_save_buf);
free(data->havoc_buf);
free(data->data_buf);
free(data->fuzz_buf);
free(data->trim_buf);
free(data);
}

View File

@ -17,6 +17,13 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
import random
COMMANDS = [
b"GET",
b"PUT",
b"DEL",
]
def init(seed):
'''
Called once when AFLFuzz starts up. Used to seed our RNG.
@ -27,6 +34,10 @@ def init(seed):
random.seed(seed)
def deinit():
pass
def fuzz(buf, add_buf, max_size):
'''
Called per fuzzing iteration.
@ -44,8 +55,9 @@ def fuzz(buf, add_buf, max_size):
@rtype: bytearray
@return: A new bytearray containing the mutated data
'''
ret = bytearray(buf)
# Do something interesting with ret
ret = bytearray(100)
ret[:3] = random.choice(COMMANDS)
return ret
@ -164,11 +176,10 @@ def fuzz(buf, add_buf, max_size):
# '''
# Called after adding a new test case to the queue
#
# @type filename_new_queue: str
# @type filename_new_queue: str
# @param filename_new_queue: File name of the new queue entry
#
# @type filename_orig_queue: str
# @param filename_orig_queue: File name of the original queue entry
# '''
# pass