mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-16 03:48:08 +00:00
renamed examples/ to utils/
This commit is contained in:
7
utils/custom_mutators/Makefile
Normal file
7
utils/custom_mutators/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
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
|
35
utils/custom_mutators/README.md
Normal file
35
utils/custom_mutators/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Examples for the custom mutator
|
||||
|
||||
These are example and helper files for the custom mutator feature.
|
||||
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 python2.7 to compile python2 scripts!
|
||||
|
||||
simple_example.c - most simplest example. generates a random sized buffer
|
||||
filled with 'A'
|
||||
|
||||
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
|
||||
|
||||
example.py - this is the template you can use, the functions are there but they
|
||||
are empty
|
||||
|
||||
post_library_gif.so.c - fix a fuzz input to ensure it is valid for GIF
|
||||
|
||||
post_library_png.so.c - fix a fuzz input to ensure it is valid for PNG
|
||||
|
||||
simple-chunk-replace.py - this is a simple example where chunks are replaced
|
||||
|
||||
common.py - this can be used for common functions and helpers.
|
||||
the examples do not use this though. But you can :)
|
||||
|
||||
wrapper_afl_min.py - mutation of XML documents, loads XmlMutatorMin.py
|
||||
|
||||
XmlMutatorMin.py - module for XML mutation
|
||||
|
||||
custom_mutator_helpers.h is an header that defines some helper routines
|
||||
like surgical_havoc_mutate() that allow to perform a randomly chosen
|
||||
mutation from a subset of the havoc mutations.
|
||||
If you do so, you have to specify -I /path/to/AFLplusplus/include when
|
||||
compiling.
|
332
utils/custom_mutators/XmlMutatorMin.py
Normal file
332
utils/custom_mutators/XmlMutatorMin.py
Normal file
@ -0,0 +1,332 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
""" Mutation of XML documents, should be called from one of its wrappers (CLI, AFL, ...) """
|
||||
|
||||
from __future__ import print_function
|
||||
from copy import deepcopy
|
||||
from lxml import etree as ET
|
||||
import random, re, io
|
||||
|
||||
|
||||
###########################
|
||||
# The XmlMutatorMin class #
|
||||
###########################
|
||||
|
||||
class XmlMutatorMin:
|
||||
|
||||
"""
|
||||
Optionals parameters:
|
||||
seed Seed used by the PRNG (default: "RANDOM")
|
||||
verbose Verbosity (default: False)
|
||||
"""
|
||||
|
||||
def __init__(self, seed="RANDOM", verbose=False):
|
||||
|
||||
""" Initialize seed, database and mutators """
|
||||
|
||||
# Verbosity
|
||||
self.verbose = verbose
|
||||
|
||||
# Initialize PRNG
|
||||
self.seed = str(seed)
|
||||
if self.seed == "RANDOM":
|
||||
random.seed()
|
||||
else:
|
||||
if self.verbose:
|
||||
print("Static seed '%s'" % self.seed)
|
||||
random.seed(self.seed)
|
||||
|
||||
# Initialize input and output documents
|
||||
self.input_tree = None
|
||||
self.tree = None
|
||||
|
||||
# High-level mutators (no database needed)
|
||||
hl_mutators_delete = ["del_node_and_children", "del_node_but_children", "del_attribute", "del_content"] # Delete items
|
||||
hl_mutators_fuzz = ["fuzz_attribute"] # Randomly change attribute values
|
||||
|
||||
# Exposed mutators
|
||||
self.hl_mutators_all = hl_mutators_fuzz + hl_mutators_delete
|
||||
|
||||
def __parse_xml(self, xml):
|
||||
|
||||
""" Parse an XML string. Basic wrapper around lxml.parse() """
|
||||
|
||||
try:
|
||||
# Function parse() takes care of comments / DTD / processing instructions / ...
|
||||
tree = ET.parse(io.BytesIO(xml))
|
||||
except ET.ParseError:
|
||||
raise RuntimeError("XML isn't well-formed!")
|
||||
except LookupError as e:
|
||||
raise RuntimeError(e)
|
||||
|
||||
# Return a document wrapper
|
||||
return tree
|
||||
|
||||
def __exec_among(self, module, functions, min_times, max_times):
|
||||
|
||||
""" Randomly execute $functions between $min and $max times """
|
||||
|
||||
for i in xrange(random.randint(min_times, max_times)):
|
||||
# Function names are mangled because they are "private"
|
||||
getattr(module, "_XmlMutatorMin__" + random.choice(functions))()
|
||||
|
||||
def __serialize_xml(self, tree):
|
||||
|
||||
""" Serialize a XML document. Basic wrapper around lxml.tostring() """
|
||||
|
||||
return ET.tostring(tree, with_tail=False, xml_declaration=True, encoding=tree.docinfo.encoding)
|
||||
|
||||
def __ver(self, version):
|
||||
|
||||
""" Helper for displaying lxml version numbers """
|
||||
|
||||
return ".".join(map(str, version))
|
||||
|
||||
def reset(self):
|
||||
|
||||
""" Reset the mutator """
|
||||
|
||||
self.tree = deepcopy(self.input_tree)
|
||||
|
||||
def init_from_string(self, input_string):
|
||||
|
||||
""" Initialize the mutator from a XML string """
|
||||
|
||||
# Get a pointer to the top-element
|
||||
self.input_tree = self.__parse_xml(input_string)
|
||||
|
||||
# Get a working copy
|
||||
self.tree = deepcopy(self.input_tree)
|
||||
|
||||
def save_to_string(self):
|
||||
|
||||
""" Return the current XML document as UTF-8 string """
|
||||
|
||||
# Return a text version of the tree
|
||||
return self.__serialize_xml(self.tree)
|
||||
|
||||
def __pick_element(self, exclude_root_node=False):
|
||||
|
||||
""" Pick a random element from the current document """
|
||||
|
||||
# Get a list of all elements, but nodes like PI and comments
|
||||
elems = list(self.tree.getroot().iter(tag=ET.Element))
|
||||
|
||||
# Is the root node excluded?
|
||||
if exclude_root_node:
|
||||
start = 1
|
||||
else:
|
||||
start = 0
|
||||
|
||||
# Pick a random element
|
||||
try:
|
||||
elem_id = random.randint(start, len(elems) - 1)
|
||||
elem = elems[elem_id]
|
||||
except ValueError:
|
||||
# Should only occurs if "exclude_root_node = True"
|
||||
return (None, None)
|
||||
|
||||
return (elem_id, elem)
|
||||
|
||||
def __fuzz_attribute(self):
|
||||
|
||||
""" Fuzz (part of) an attribute value """
|
||||
|
||||
# Select a node to modify
|
||||
(rand_elem_id, rand_elem) = self.__pick_element()
|
||||
|
||||
# Get all the attributes
|
||||
attribs = rand_elem.keys()
|
||||
|
||||
# Is there attributes?
|
||||
if len(attribs) < 1:
|
||||
if self.verbose:
|
||||
print("No attribute: can't replace!")
|
||||
return
|
||||
|
||||
# Pick a random attribute
|
||||
rand_attrib_id = random.randint(0, len(attribs) - 1)
|
||||
rand_attrib = attribs[rand_attrib_id]
|
||||
|
||||
# We have the attribute to modify
|
||||
# Get its value
|
||||
attrib_value = rand_elem.get(rand_attrib)
|
||||
# print("- Value: " + attrib_value)
|
||||
|
||||
# Should we work on the whole value?
|
||||
func_call = "(?P<func>[a-zA-Z:\-]+)\((?P<args>.*?)\)"
|
||||
p = re.compile(func_call)
|
||||
l = p.findall(attrib_value)
|
||||
if random.choice((True, False)) and l:
|
||||
# Randomly pick one the function calls
|
||||
(func, args) = random.choice(l)
|
||||
# Split by "," and randomly pick one of the arguments
|
||||
value = random.choice(args.split(','))
|
||||
# Remove superfluous characters
|
||||
unclean_value = value
|
||||
value = value.strip(" ").strip("'")
|
||||
# print("Selected argument: [%s]" % value)
|
||||
else:
|
||||
value = attrib_value
|
||||
|
||||
# For each type, define some possible replacement values
|
||||
choices_number = ( \
|
||||
"0", \
|
||||
"11111", \
|
||||
"-128", \
|
||||
"2", \
|
||||
"-1", \
|
||||
"1/3", \
|
||||
"42/0", \
|
||||
"1094861636 idiv 1.0", \
|
||||
"-1123329771506872 idiv 3.8", \
|
||||
"17=$numericRTF", \
|
||||
str(3 + random.randrange(0, 100)), \
|
||||
)
|
||||
|
||||
choices_letter = ( \
|
||||
"P" * (25 * random.randrange(1, 100)), \
|
||||
"%s%s%s%s%s%s", \
|
||||
"foobar", \
|
||||
)
|
||||
|
||||
choices_alnum = ( \
|
||||
"Abc123", \
|
||||
"020F0302020204030204", \
|
||||
"020F0302020204030204" * (random.randrange(5, 20)), \
|
||||
)
|
||||
|
||||
# Fuzz the value
|
||||
if random.choice((True,False)) and value == "":
|
||||
|
||||
# Empty
|
||||
new_value = value
|
||||
|
||||
elif random.choice((True,False)) and value.isdigit():
|
||||
|
||||
# Numbers
|
||||
new_value = random.choice(choices_number)
|
||||
|
||||
elif random.choice((True,False)) and value.isalpha():
|
||||
|
||||
# Letters
|
||||
new_value = random.choice(choices_letter)
|
||||
|
||||
elif random.choice((True,False)) and value.isalnum():
|
||||
|
||||
# Alphanumeric
|
||||
new_value = random.choice(choices_alnum)
|
||||
|
||||
else:
|
||||
|
||||
# Default type
|
||||
new_value = random.choice(choices_alnum + choices_letter + choices_number)
|
||||
|
||||
# If we worked on a substring, apply changes to the whole string
|
||||
if value != attrib_value:
|
||||
# No ' around empty values
|
||||
if new_value != "" and value != "":
|
||||
new_value = "'" + new_value + "'"
|
||||
# Apply changes
|
||||
new_value = attrib_value.replace(unclean_value, new_value)
|
||||
|
||||
# Log something
|
||||
if self.verbose:
|
||||
print("Fuzzing attribute #%i '%s' of tag #%i '%s'" % (rand_attrib_id, rand_attrib, rand_elem_id, rand_elem.tag))
|
||||
|
||||
# Modify the attribute
|
||||
rand_elem.set(rand_attrib, new_value.decode("utf-8"))
|
||||
|
||||
def __del_node_and_children(self):
|
||||
|
||||
""" High-level minimizing mutator
|
||||
Delete a random node and its children (i.e. delete a random tree) """
|
||||
|
||||
self.__del_node(True)
|
||||
|
||||
def __del_node_but_children(self):
|
||||
|
||||
""" High-level minimizing mutator
|
||||
Delete a random node but its children (i.e. link them to the parent of the deleted node) """
|
||||
|
||||
self.__del_node(False)
|
||||
|
||||
def __del_node(self, delete_children):
|
||||
|
||||
""" Called by the __del_node_* mutators """
|
||||
|
||||
# Select a node to modify (but the root one)
|
||||
(rand_elem_id, rand_elem) = self.__pick_element(exclude_root_node=True)
|
||||
|
||||
# If the document includes only a top-level element
|
||||
# Then we can't pick a element (given that "exclude_root_node = True")
|
||||
|
||||
# Is the document deep enough?
|
||||
if rand_elem is None:
|
||||
if self.verbose:
|
||||
print("Can't delete a node: document not deep enough!")
|
||||
return
|
||||
|
||||
# Log something
|
||||
if self.verbose:
|
||||
but_or_and = "and" if delete_children else "but"
|
||||
print("Deleting tag #%i '%s' %s its children" % (rand_elem_id, rand_elem.tag, but_or_and))
|
||||
|
||||
if delete_children is False:
|
||||
# Link children of the random (soon to be deleted) node to its parent
|
||||
for child in rand_elem:
|
||||
rand_elem.getparent().append(child)
|
||||
|
||||
# Remove the node
|
||||
rand_elem.getparent().remove(rand_elem)
|
||||
|
||||
def __del_content(self):
|
||||
|
||||
""" High-level minimizing mutator
|
||||
Delete the attributes and children of a random node """
|
||||
|
||||
# Select a node to modify
|
||||
(rand_elem_id, rand_elem) = self.__pick_element()
|
||||
|
||||
# Log something
|
||||
if self.verbose:
|
||||
print("Reseting tag #%i '%s'" % (rand_elem_id, rand_elem.tag))
|
||||
|
||||
# Reset the node
|
||||
rand_elem.clear()
|
||||
|
||||
def __del_attribute(self):
|
||||
|
||||
""" High-level minimizing mutator
|
||||
Delete a random attribute from a random node """
|
||||
|
||||
# Select a node to modify
|
||||
(rand_elem_id, rand_elem) = self.__pick_element()
|
||||
|
||||
# Get all the attributes
|
||||
attribs = rand_elem.keys()
|
||||
|
||||
# Is there attributes?
|
||||
if len(attribs) < 1:
|
||||
if self.verbose:
|
||||
print("No attribute: can't delete!")
|
||||
return
|
||||
|
||||
# Pick a random attribute
|
||||
rand_attrib_id = random.randint(0, len(attribs) - 1)
|
||||
rand_attrib = attribs[rand_attrib_id]
|
||||
|
||||
# Log something
|
||||
if self.verbose:
|
||||
print("Deleting attribute #%i '%s' of tag #%i '%s'" % (rand_attrib_id, rand_attrib, rand_elem_id, rand_elem.tag))
|
||||
|
||||
# Delete the attribute
|
||||
rand_elem.attrib.pop(rand_attrib)
|
||||
|
||||
def mutate(self, min=1, max=5):
|
||||
|
||||
""" Execute some high-level mutators between $min and $max times, then some medium-level ones """
|
||||
|
||||
# High-level mutation
|
||||
self.__exec_among(self, self.hl_mutators_all, min, max)
|
||||
|
40
utils/custom_mutators/common.py
Normal file
40
utils/custom_mutators/common.py
Normal file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
'''
|
||||
Module containing functions shared between multiple AFL modules
|
||||
|
||||
@author: Christian Holler (:decoder)
|
||||
|
||||
@license:
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
@contact: choller@mozilla.com
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import random
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def randel(l):
|
||||
if not l:
|
||||
return None
|
||||
return l[random.randint(0, len(l)-1)]
|
||||
|
||||
|
||||
def randel_pop(l):
|
||||
if not l:
|
||||
return None
|
||||
return l.pop(random.randint(0, len(l)-1))
|
||||
|
||||
|
||||
def write_exc_example(data, exc):
|
||||
exc_name = re.sub(r'[^a-zA-Z0-9]', '_', repr(exc))
|
||||
|
||||
if not os.path.exists(exc_name):
|
||||
with open(exc_name, 'w') as f:
|
||||
f.write(data)
|
342
utils/custom_mutators/custom_mutator_helpers.h
Normal file
342
utils/custom_mutators/custom_mutator_helpers.h
Normal file
@ -0,0 +1,342 @@
|
||||
#ifndef CUSTOM_MUTATOR_HELPERS
|
||||
#define CUSTOM_MUTATOR_HELPERS
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INITIAL_GROWTH_SIZE (64)
|
||||
|
||||
#define RAND_BELOW(limit) (rand() % (limit))
|
||||
|
||||
/* 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 fills 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 s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
|
||||
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
|
||||
|
||||
switch (RAND_BELOW(12)) {
|
||||
|
||||
case 0: {
|
||||
|
||||
/* Flip a single bit somewhere. Spooky! */
|
||||
|
||||
s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8);
|
||||
|
||||
out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 1: {
|
||||
|
||||
/* Set byte to interesting value. */
|
||||
|
||||
u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))];
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] = val;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 2: {
|
||||
|
||||
/* Set word to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)];
|
||||
break;
|
||||
case 1:
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 3: {
|
||||
|
||||
/* Set dword to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
|
||||
break;
|
||||
case 1:
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 4: {
|
||||
|
||||
/* Set qword to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 8) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 7) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u64 *)(out_buf + byte_idx) =
|
||||
(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)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 5: {
|
||||
|
||||
/* Randomly subtract from byte. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 6: {
|
||||
|
||||
/* Randomly add to byte. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 7: {
|
||||
|
||||
/* Randomly subtract from word, random endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u16 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 8: {
|
||||
|
||||
/* Randomly add to word, random endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u16 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 9: {
|
||||
|
||||
/* Randomly subtract from dword, random endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u32 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 10: {
|
||||
|
||||
/* Randomly add to dword, random endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u32 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 11: {
|
||||
|
||||
/* Just set a random byte to a random value. Because,
|
||||
why not. We use XOR with 1-255 to eliminate the
|
||||
possibility of a no-op. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 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 afl_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
|
||||
|
376
utils/custom_mutators/example.c
Normal file
376
utils/custom_mutators/example.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
New Custom Mutator for AFL++
|
||||
Written by Khaled Yakdan <yakdan@code-intelligence.de>
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
Shengtuo Hu <h1994st@gmail.com>
|
||||
Dominik Maier <mail@dmnk.co>
|
||||
*/
|
||||
|
||||
// You need to use -I /path/to/AFLplusplus/include
|
||||
#include "custom_mutator_helpers.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DATA_SIZE (100)
|
||||
|
||||
static const char *commands[] = {
|
||||
|
||||
"GET",
|
||||
"PUT",
|
||||
"DEL",
|
||||
|
||||
};
|
||||
|
||||
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, post_process);
|
||||
|
||||
} 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.
|
||||
* @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!
|
||||
* Return NULL on error.
|
||||
*/
|
||||
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform custom mutations on a given input
|
||||
*
|
||||
* (Optional for now. Required in the future)
|
||||
*
|
||||
* @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,
|
||||
u8 **out_buf, uint8_t *add_buf,
|
||||
size_t add_buf_size, // add_buf can be NULL
|
||||
size_t max_size) {
|
||||
|
||||
// Make sure that the packet size does not exceed the maximum size expected by
|
||||
// the fuzzer
|
||||
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_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) {
|
||||
|
||||
*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);
|
||||
|
||||
// Mutate the payload of the packet
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
|
||||
// Randomly perform one of the (no len modification) havoc mutations
|
||||
surgical_havoc_mutate(mutated_out, 3, mutated_size);
|
||||
|
||||
}
|
||||
|
||||
*out_buf = mutated_out;
|
||||
return mutated_size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A post-processing function to use right before AFL writes the test case to
|
||||
* disk in order to execute the target.
|
||||
*
|
||||
* (Optional) If this functionality is not needed, simply don't define this
|
||||
* function.
|
||||
*
|
||||
* @param[in] data pointer returned in afl_custom_init for this fuzz case
|
||||
* @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.
|
||||
* 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_post_process(my_mutator_t *data, uint8_t *buf,
|
||||
size_t buf_size, uint8_t **out_buf) {
|
||||
|
||||
uint8_t *post_process_buf =
|
||||
maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
|
||||
if (!post_process_buf) {
|
||||
|
||||
perror("custom mutator realloc failed.");
|
||||
*out_buf = NULL;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
memcpy(post_process_buf + 5, buf, buf_size);
|
||||
post_process_buf[0] = 'A';
|
||||
post_process_buf[1] = 'F';
|
||||
post_process_buf[2] = 'L';
|
||||
post_process_buf[3] = '+';
|
||||
post_process_buf[4] = '+';
|
||||
|
||||
*out_buf = post_process_buf;
|
||||
|
||||
return buf_size + 5;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* on this input (e.g. if your input has n elements and you want to remove
|
||||
* them one by one, return n, if you do a binary search, return log(n),
|
||||
* and so on...).
|
||||
*
|
||||
* If your trimming algorithm doesn't allow you to determine the amount of
|
||||
* (remaining) steps easily (esp. while running), then you can alternatively
|
||||
* return 1 here and always return 0 in post_trim until you are finished and
|
||||
* no steps remain. In that case, returning 1 in post_trim will end the
|
||||
* trimming routine. The whole current index/max iterations stuff is only used
|
||||
* to show progress.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @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.
|
||||
* negative on error.
|
||||
*/
|
||||
int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
|
||||
size_t buf_size) {
|
||||
|
||||
// We simply trim once
|
||||
data->trimmming_steps = 1;
|
||||
|
||||
data->cur_step = 0;
|
||||
|
||||
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 *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
|
||||
* to init_trim) will result in a fatal abort of AFLFuzz.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @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 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
|
||||
*/
|
||||
size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) {
|
||||
|
||||
*out_buf = data->trim_buf;
|
||||
|
||||
// Remove the last byte of the trimming input
|
||||
return data->trim_size_current - 1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after each trim operation to inform you if your
|
||||
* trimming step was successful or not (in terms of coverage). If you receive
|
||||
* a failure here, you should reset your input to the last known good state.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @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). negative ret on failure.
|
||||
*/
|
||||
int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
|
||||
|
||||
if (success) {
|
||||
|
||||
++data->cur_step;
|
||||
return data->cur_step;
|
||||
|
||||
}
|
||||
|
||||
return data->trimmming_steps;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a single custom mutation on a given input.
|
||||
* This mutation is stacked with the other muatations in havoc.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @param[in] data pointer returned in afl_custom_init for this fuzz case
|
||||
* @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, u8 *buf, size_t buf_size,
|
||||
u8 **out_buf, size_t max_size) {
|
||||
|
||||
if (buf_size == 0) {
|
||||
|
||||
*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;
|
||||
(*out_buf)[victim] += rand() % 10;
|
||||
|
||||
return buf_size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the probability (in percentage) that afl_custom_havoc_mutation
|
||||
* is called in havoc. By default it is 6 %.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @param[in] data pointer returned in afl_custom_init for this fuzz case
|
||||
* @return The probability (0-100).
|
||||
*/
|
||||
uint8_t afl_custom_havoc_mutation_probability(my_mutator_t *data) {
|
||||
|
||||
return 5; // 5 %
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the fuzzer should fuzz the queue entry or not.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @param[in] data pointer returned in afl_custom_init for this fuzz case
|
||||
* @param filename File name of the test case in the queue entry
|
||||
* @return Return True(1) if the fuzzer will fuzz the queue entry, and
|
||||
* False(0) otherwise.
|
||||
*/
|
||||
uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) {
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow for additional analysis (e.g. calling a different tool that does a
|
||||
* different kind of coverage and saves this for the custom mutator).
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @param data pointer returned in afl_custom_init for this fuzz case
|
||||
* @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,
|
||||
const uint8_t *filename_orig_queue) {
|
||||
|
||||
/* Additional analysis on the original or new test case */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize everything
|
||||
*
|
||||
* @param data The data ptr from afl_custom_init
|
||||
*/
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->post_process_buf);
|
||||
free(data->havoc_buf);
|
||||
free(data->data_buf);
|
||||
free(data->fuzz_buf);
|
||||
free(data->trim_buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
186
utils/custom_mutators/example.py
Normal file
186
utils/custom_mutators/example.py
Normal file
@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
'''
|
||||
Example Python Module for AFLFuzz
|
||||
|
||||
@author: Christian Holler (:decoder)
|
||||
|
||||
@license:
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
@contact: choller@mozilla.com
|
||||
'''
|
||||
|
||||
import random
|
||||
|
||||
|
||||
COMMANDS = [
|
||||
b"GET",
|
||||
b"PUT",
|
||||
b"DEL",
|
||||
b"AAAAAAAAAAAAAAAAA",
|
||||
]
|
||||
|
||||
|
||||
def init(seed):
|
||||
'''
|
||||
Called once when AFLFuzz starts up. Used to seed our RNG.
|
||||
|
||||
@type seed: int
|
||||
@param seed: A 32-bit random value
|
||||
'''
|
||||
random.seed(seed)
|
||||
|
||||
|
||||
def deinit():
|
||||
pass
|
||||
|
||||
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
'''
|
||||
Called per fuzzing iteration.
|
||||
|
||||
@type buf: bytearray
|
||||
@param buf: The buffer that should be mutated.
|
||||
|
||||
@type add_buf: bytearray
|
||||
@param add_buf: A second buffer that can be used as mutation source.
|
||||
|
||||
@type max_size: int
|
||||
@param max_size: Maximum size of the mutated output. The mutation must not
|
||||
produce data larger than max_size.
|
||||
|
||||
@rtype: bytearray
|
||||
@return: A new bytearray containing the mutated data
|
||||
'''
|
||||
ret = bytearray(100)
|
||||
|
||||
ret[:3] = random.choice(COMMANDS)
|
||||
|
||||
return ret
|
||||
|
||||
# Uncomment and implement the following methods if you want to use a custom
|
||||
# trimming algorithm. See also the documentation for a better API description.
|
||||
|
||||
# def init_trim(buf):
|
||||
# '''
|
||||
# Called per trimming iteration.
|
||||
#
|
||||
# @type buf: bytearray
|
||||
# @param buf: The buffer that should be trimmed.
|
||||
#
|
||||
# @rtype: int
|
||||
# @return: The maximum number of trimming steps.
|
||||
# '''
|
||||
# global ...
|
||||
#
|
||||
# # Initialize global variables
|
||||
#
|
||||
# # Figure out how many trimming steps are possible.
|
||||
# # If this is not possible for your trimming, you can
|
||||
# # return 1 instead and always return 0 in post_trim
|
||||
# # until you are done (then you return 1).
|
||||
#
|
||||
# return steps
|
||||
#
|
||||
# def trim():
|
||||
# '''
|
||||
# Called per trimming iteration.
|
||||
#
|
||||
# @rtype: bytearray
|
||||
# @return: A new bytearray containing the trimmed data.
|
||||
# '''
|
||||
# global ...
|
||||
#
|
||||
# # Implement the actual trimming here
|
||||
#
|
||||
# return bytearray(...)
|
||||
#
|
||||
# def post_trim(success):
|
||||
# '''
|
||||
# Called after each trimming operation.
|
||||
#
|
||||
# @type success: bool
|
||||
# @param success: Indicates if the last trim operation was successful.
|
||||
#
|
||||
# @rtype: int
|
||||
# @return: The next trim index (0 to max number of steps) where max
|
||||
# number of steps indicates the trimming is done.
|
||||
# '''
|
||||
# global ...
|
||||
#
|
||||
# if not success:
|
||||
# # Restore last known successful input, determine next index
|
||||
# else:
|
||||
# # Just determine the next index, based on what was successfully
|
||||
# # removed in the last step
|
||||
#
|
||||
# return next_index
|
||||
#
|
||||
# def post_process(buf):
|
||||
# '''
|
||||
# Called just before the execution to write the test case in the format
|
||||
# expected by the target
|
||||
#
|
||||
# @type buf: bytearray
|
||||
# @param buf: The buffer containing the test case to be executed
|
||||
#
|
||||
# @rtype: bytearray
|
||||
# @return: The buffer containing the test case after
|
||||
# '''
|
||||
# return buf
|
||||
#
|
||||
# def havoc_mutation(buf, max_size):
|
||||
# '''
|
||||
# Perform a single custom mutation on a given input.
|
||||
#
|
||||
# @type buf: bytearray
|
||||
# @param buf: The buffer that should be mutated.
|
||||
#
|
||||
# @type max_size: int
|
||||
# @param max_size: Maximum size of the mutated output. The mutation must not
|
||||
# produce data larger than max_size.
|
||||
#
|
||||
# @rtype: bytearray
|
||||
# @return: A new bytearray containing the mutated data
|
||||
# '''
|
||||
# return mutated_buf
|
||||
#
|
||||
# def havoc_mutation_probability():
|
||||
# '''
|
||||
# Called for each `havoc_mutation`. Return the probability (in percentage)
|
||||
# that `havoc_mutation` is called in havoc. Be default it is 6%.
|
||||
#
|
||||
# @rtype: int
|
||||
# @return: The probability (0-100)
|
||||
# '''
|
||||
# return prob
|
||||
#
|
||||
# def queue_get(filename):
|
||||
# '''
|
||||
# Called at the beginning of each fuzz iteration to determine whether the
|
||||
# test case should be fuzzed
|
||||
#
|
||||
# @type filename: str
|
||||
# @param filename: File name of the test case in the current queue entry
|
||||
#
|
||||
# @rtype: bool
|
||||
# @return: Return True if the custom mutator decides to fuzz the test case,
|
||||
# and False otherwise
|
||||
# '''
|
||||
# return True
|
||||
#
|
||||
# def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
# '''
|
||||
# Called after adding a new test case to the queue
|
||||
#
|
||||
# @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
|
165
utils/custom_mutators/post_library_gif.so.c
Normal file
165
utils/custom_mutators/post_library_gif.so.c
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
american fuzzy lop++ - postprocessor library example
|
||||
--------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
Edited by Dominik Maier, 2020
|
||||
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Postprocessor libraries can be passed to afl-fuzz to perform final cleanup
|
||||
of any mutated test cases - for example, to fix up checksums in PNG files.
|
||||
|
||||
Please heed the following warnings:
|
||||
|
||||
1) In almost all cases, it is more productive to comment out checksum logic
|
||||
in the targeted binary (as shown in ../libpng_no_checksum/). One possible
|
||||
exception is the process of fuzzing binary-only software in QEMU mode.
|
||||
|
||||
2) The use of postprocessors for anything other than checksums is
|
||||
questionable and may cause more harm than good. AFL is normally pretty good
|
||||
about dealing with length fields, magic values, etc.
|
||||
|
||||
3) Postprocessors that do anything non-trivial must be extremely robust to
|
||||
gracefully handle malformed data and other error conditions - otherwise,
|
||||
they will crash and take afl-fuzz down with them. Be wary of reading past
|
||||
*len and of integer overflows when calculating file offsets.
|
||||
|
||||
In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really,
|
||||
honestly know what you're doing =)
|
||||
|
||||
With that out of the way: the postprocessor library is passed to afl-fuzz
|
||||
via AFL_POST_LIBRARY. The library must be compiled with:
|
||||
|
||||
gcc -shared -Wall -O3 post_library.so.c -o post_library.so
|
||||
|
||||
AFL will call the afl_custom_post_process() function for every mutated output
|
||||
buffer. From there, you have three choices:
|
||||
|
||||
1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
|
||||
and return the original `len`.
|
||||
|
||||
2) If you want to skip this test case altogether and have AFL generate a
|
||||
new one, return 0 or set `*out_buf = NULL`.
|
||||
Use this sparingly - it's faster than running the target program
|
||||
with patently useless inputs, but still wastes CPU time.
|
||||
|
||||
3) If you want to modify the test case, allocate an appropriately-sized
|
||||
buffer, move the data into that buffer, make the necessary changes, and
|
||||
then return the new pointer as out_buf. Return an appropriate len
|
||||
afterwards.
|
||||
|
||||
Note that the buffer will *not* be freed for you. To avoid memory leaks,
|
||||
you need to free it or reuse it on subsequent calls (as shown below).
|
||||
|
||||
*** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
|
||||
|
||||
Aight. The example below shows a simple postprocessor that tries to make
|
||||
sure that all input files start with "GIF89a".
|
||||
|
||||
PS. If you don't like C, you can try out the unix-based wrapper from
|
||||
Ben Nagy instead: https://github.com/bnagy/aflfix
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Header that must be present at the beginning of every test case: */
|
||||
|
||||
#define HEADER "GIF89a"
|
||||
|
||||
typedef struct post_state {
|
||||
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
|
||||
} post_state_t;
|
||||
|
||||
void *afl_custom_init(void *afl) {
|
||||
|
||||
post_state_t *state = malloc(sizeof(post_state_t));
|
||||
if (!state) {
|
||||
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
state->buf = calloc(sizeof(unsigned char), 4096);
|
||||
if (!state->buf) {
|
||||
|
||||
free(state);
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
/* The actual postprocessor routine called by afl-fuzz: */
|
||||
|
||||
size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
|
||||
unsigned int len, unsigned char **out_buf) {
|
||||
|
||||
/* Skip execution altogether for buffers shorter than 6 bytes (just to
|
||||
show how it's done). We can trust len to be sane. */
|
||||
|
||||
if (len < strlen(HEADER)) return 0;
|
||||
|
||||
/* Do nothing for buffers that already start with the expected header. */
|
||||
|
||||
if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Allocate memory for new buffer, reusing previous allocation if
|
||||
possible. */
|
||||
|
||||
*out_buf = realloc(data->buf, len);
|
||||
|
||||
/* If we're out of memory, the most graceful thing to do is to return the
|
||||
original buffer and give up on modifying it. Let AFL handle OOM on its
|
||||
own later on. */
|
||||
|
||||
if (!*out_buf) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Copy the original data to the new location. */
|
||||
|
||||
memcpy(*out_buf, in_buf, len);
|
||||
|
||||
/* Insert the new header. */
|
||||
|
||||
memcpy(*out_buf, HEADER, strlen(HEADER));
|
||||
|
||||
/* Return the new len. It hasn't changed, so it's just len. */
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Gets called afterwards */
|
||||
void afl_custom_deinit(post_state_t *data) {
|
||||
|
||||
free(data->buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
163
utils/custom_mutators/post_library_png.so.c
Normal file
163
utils/custom_mutators/post_library_png.so.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
american fuzzy lop++ - postprocessor for PNG
|
||||
------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
Adapted to the new API, 2020 by Dominik Maier
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
See post_library.so.c for a general discussion of how to implement
|
||||
postprocessors. This specific postprocessor attempts to fix up PNG
|
||||
checksums, providing a slightly more complicated example than found
|
||||
in post_library.so.c.
|
||||
|
||||
Compile with:
|
||||
|
||||
gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* A macro to round an integer up to 4 kB. */
|
||||
|
||||
#define UP4K(_i) ((((_i) >> 12) + 1) << 12)
|
||||
|
||||
typedef struct post_state {
|
||||
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
|
||||
} post_state_t;
|
||||
|
||||
void *afl_custom_init(void *afl) {
|
||||
|
||||
post_state_t *state = malloc(sizeof(post_state_t));
|
||||
if (!state) {
|
||||
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
state->buf = calloc(sizeof(unsigned char), 4096);
|
||||
if (!state->buf) {
|
||||
|
||||
free(state);
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
|
||||
unsigned int len,
|
||||
const unsigned char **out_buf) {
|
||||
|
||||
unsigned char *new_buf = (unsigned char *)in_buf;
|
||||
unsigned int pos = 8;
|
||||
|
||||
/* Don't do anything if there's not enough room for the PNG header
|
||||
(8 bytes). */
|
||||
|
||||
if (len < 8) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we
|
||||
don't have that, we can bail out. */
|
||||
|
||||
while (pos + 12 <= len) {
|
||||
|
||||
unsigned int chunk_len, real_cksum, file_cksum;
|
||||
|
||||
/* Chunk length is the first big-endian dword in the chunk. */
|
||||
|
||||
chunk_len = ntohl(*(uint32_t *)(in_buf + pos));
|
||||
|
||||
/* Bail out if chunk size is too big or goes past EOF. */
|
||||
|
||||
if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break;
|
||||
|
||||
/* Chunk checksum is calculated for chunk ID (dword) and the actual
|
||||
payload. */
|
||||
|
||||
real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4));
|
||||
|
||||
/* The in-file checksum is the last dword past the chunk data. */
|
||||
|
||||
file_cksum = *(uint32_t *)(in_buf + pos + 8 + chunk_len);
|
||||
|
||||
/* If the checksums do not match, we need to fix the file. */
|
||||
|
||||
if (real_cksum != file_cksum) {
|
||||
|
||||
/* First modification? Make a copy of the input buffer. Round size
|
||||
up to 4 kB to minimize the number of reallocs needed. */
|
||||
|
||||
if (new_buf == in_buf) {
|
||||
|
||||
if (len <= data->size) {
|
||||
|
||||
new_buf = data->buf;
|
||||
|
||||
} else {
|
||||
|
||||
new_buf = realloc(data->buf, UP4K(len));
|
||||
if (!new_buf) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
data->buf = new_buf;
|
||||
data->size = UP4K(len);
|
||||
memcpy(new_buf, in_buf, len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum;
|
||||
|
||||
}
|
||||
|
||||
/* Skip the entire chunk and move to the next one. */
|
||||
|
||||
pos += 12 + chunk_len;
|
||||
|
||||
}
|
||||
|
||||
*out_buf = new_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Gets called afterwards */
|
||||
void afl_custom_deinit(post_state_t *data) {
|
||||
|
||||
free(data->buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
64
utils/custom_mutators/simple-chunk-replace.py
Normal file
64
utils/custom_mutators/simple-chunk-replace.py
Normal file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
'''
|
||||
Simple Chunk Cross-Over Replacement Module for AFLFuzz
|
||||
|
||||
@author: Christian Holler (:decoder)
|
||||
|
||||
@license:
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
@contact: choller@mozilla.com
|
||||
'''
|
||||
|
||||
import random
|
||||
|
||||
|
||||
def init(seed):
|
||||
'''
|
||||
Called once when AFLFuzz starts up. Used to seed our RNG.
|
||||
|
||||
@type seed: int
|
||||
@param seed: A 32-bit random value
|
||||
'''
|
||||
# Seed our RNG
|
||||
random.seed(seed)
|
||||
|
||||
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
'''
|
||||
Called per fuzzing iteration.
|
||||
|
||||
@type buf: bytearray
|
||||
@param buf: The buffer that should be mutated.
|
||||
|
||||
@type add_buf: bytearray
|
||||
@param add_buf: A second buffer that can be used as mutation source.
|
||||
|
||||
@type max_size: int
|
||||
@param max_size: Maximum size of the mutated output. The mutation must not
|
||||
produce data larger than max_size.
|
||||
|
||||
@rtype: bytearray
|
||||
@return: A new bytearray containing the mutated data
|
||||
'''
|
||||
# Make a copy of our input buffer for returning
|
||||
ret = bytearray(buf)
|
||||
|
||||
# Take a random fragment length between 2 and 32 (or less if add_buf is shorter)
|
||||
fragment_len = random.randint(1, min(len(add_buf), 32))
|
||||
|
||||
# Determine a random source index where to take the data chunk from
|
||||
rand_src_idx = random.randint(0, len(add_buf) - fragment_len)
|
||||
|
||||
# Determine a random destination index where to put the data chunk
|
||||
rand_dst_idx = random.randint(0, len(buf))
|
||||
|
||||
# Make the chunk replacement
|
||||
ret[rand_dst_idx:rand_dst_idx + fragment_len] = add_buf[rand_src_idx:rand_src_idx + fragment_len]
|
||||
|
||||
# Return data
|
||||
return ret
|
74
utils/custom_mutators/simple_example.c
Normal file
74
utils/custom_mutators/simple_example.c
Normal file
@ -0,0 +1,74 @@
|
||||
// This simple example just creates random buffer <= 100 filled with 'A'
|
||||
// needs -I /path/to/AFLplusplus/include
|
||||
#include "custom_mutator_helpers.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _FIXED_CHAR
|
||||
#define _FIXED_CHAR 0x41
|
||||
#endif
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
|
||||
// Reused buffers:
|
||||
BUF_VAR(u8, fuzz);
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
|
||||
srand(seed);
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
if (!data) {
|
||||
|
||||
perror("afl_custom_init alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->afl = afl;
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
int size = (rand() % 100) + 1;
|
||||
if (size > max_size) size = max_size;
|
||||
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
|
||||
if (!mutated_out) {
|
||||
|
||||
*out_buf = NULL;
|
||||
perror("custom mutator allocation (maybe_grow)");
|
||||
return 0; /* afl-fuzz will very likely error out after this. */
|
||||
|
||||
}
|
||||
|
||||
memset(mutated_out, _FIXED_CHAR, size);
|
||||
|
||||
*out_buf = mutated_out;
|
||||
return size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize everything
|
||||
*
|
||||
* @param data The data ptr from afl_custom_init
|
||||
*/
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->fuzz_buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
118
utils/custom_mutators/wrapper_afl_min.py
Normal file
118
utils/custom_mutators/wrapper_afl_min.py
Normal file
@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from XmlMutatorMin import XmlMutatorMin
|
||||
|
||||
# Default settings (production mode)
|
||||
|
||||
__mutator__ = None
|
||||
__seed__ = "RANDOM"
|
||||
__log__ = False
|
||||
__log_file__ = "wrapper.log"
|
||||
|
||||
|
||||
# AFL functions
|
||||
def log(text):
|
||||
"""
|
||||
Logger
|
||||
"""
|
||||
|
||||
global __seed__
|
||||
global __log__
|
||||
global __log_file__
|
||||
|
||||
if __log__:
|
||||
with open(__log_file__, "a") as logf:
|
||||
logf.write("[%s] %s\n" % (__seed__, text))
|
||||
|
||||
|
||||
def init(seed):
|
||||
"""
|
||||
Called once when AFL starts up. Seed is used to identify the AFL instance in log files
|
||||
"""
|
||||
|
||||
global __mutator__
|
||||
global __seed__
|
||||
|
||||
# Get the seed
|
||||
__seed__ = seed
|
||||
|
||||
# Create a global mutation class
|
||||
try:
|
||||
__mutator__ = XmlMutatorMin(__seed__, verbose=__log__)
|
||||
log("init(): Mutator created")
|
||||
except RuntimeError as e:
|
||||
log("init(): Can't create mutator: %s" % e.message)
|
||||
|
||||
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
"""
|
||||
Called for each fuzzing iteration.
|
||||
"""
|
||||
|
||||
global __mutator__
|
||||
|
||||
# Do we have a working mutator object?
|
||||
if __mutator__ is None:
|
||||
log("fuzz(): Can't fuzz, no mutator available")
|
||||
return buf
|
||||
|
||||
# Try to use the AFL buffer
|
||||
via_buffer = True
|
||||
|
||||
# Interpret the AFL buffer (an array of bytes) as a string
|
||||
if via_buffer:
|
||||
try:
|
||||
buf_str = str(buf)
|
||||
log("fuzz(): AFL buffer converted to a string")
|
||||
except Exception:
|
||||
via_buffer = False
|
||||
log("fuzz(): Can't convert AFL buffer to a string")
|
||||
|
||||
# Load XML from the AFL string
|
||||
if via_buffer:
|
||||
try:
|
||||
__mutator__.init_from_string(buf_str)
|
||||
log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str))
|
||||
except Exception:
|
||||
via_buffer = False
|
||||
log("fuzz(): Can't initialize mutator with AFL buffer")
|
||||
|
||||
# If init from AFL buffer wasn't succesful
|
||||
if not via_buffer:
|
||||
log("fuzz(): Returning unmodified AFL buffer")
|
||||
return buf
|
||||
|
||||
# Sucessful initialization -> mutate
|
||||
try:
|
||||
__mutator__.mutate(max=5)
|
||||
log("fuzz(): Input mutated")
|
||||
except Exception:
|
||||
log("fuzz(): Can't mutate input => returning buf")
|
||||
return buf
|
||||
|
||||
# Convert mutated data to a array of bytes
|
||||
try:
|
||||
data = bytearray(__mutator__.save_to_string())
|
||||
log("fuzz(): Mutated data converted as bytes")
|
||||
except Exception:
|
||||
log("fuzz(): Can't convert mutated data to bytes => returning buf")
|
||||
return buf
|
||||
|
||||
# Everything went fine, returning mutated content
|
||||
log("fuzz(): Returning %d bytes" % len(data))
|
||||
return data
|
||||
|
||||
|
||||
# Main (for debug)
|
||||
if __name__ == '__main__':
|
||||
|
||||
__log__ = True
|
||||
__log_file__ = "/dev/stdout"
|
||||
__seed__ = "RANDOM"
|
||||
|
||||
init(__seed__)
|
||||
|
||||
in_1 = bytearray("<foo ddd='eeee'>ffff<a b='c' d='456' eee='ffffff'>zzzzzzzzzzzz</a><b yyy='YYY' zzz='ZZZ'></b></foo>")
|
||||
in_2 = bytearray("<abc abc123='456' abcCBA='ppppppppppppppppppppppppppppp'/>")
|
||||
out = fuzz(in_1, in_2)
|
||||
print(out)
|
Reference in New Issue
Block a user