about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2019-06-20 12:22:46 +0200
committervan Hauser <vh@thc.org>2019-06-20 12:22:46 +0200
commitd10ebd1a6837f9fc42886fd5debe5311784be75a (patch)
tree38a04cb84b2815af7b2f588cb0b1ee04edf0a0b4
parent4e3d921f1a7016755721cec0141ae0978621669f (diff)
downloadafl++-d10ebd1a6837f9fc42886fd5debe5311784be75a.tar.gz
python mutator examples added
-rw-r--r--docs/README3
-rw-r--r--docs/python_mutators.txt5
-rw-r--r--python_mutators/README11
-rw-r--r--python_mutators/common.py37
-rw-r--r--python_mutators/example.py103
-rw-r--r--python_mutators/simple-chunk-replace.py59
6 files changed, 218 insertions, 0 deletions
diff --git a/docs/README b/docs/README
index 9c81a788..cc42d767 100644
--- a/docs/README
+++ b/docs/README
@@ -21,6 +21,9 @@ american fuzzy lop plus plus
   https://github.com/andreafioraldi/afl and got the community patches applied
   to it.
 
+  C. Hoellers Python mutator module support was added too
+  (https://github.com/choeller/afl)
+
   So all in all this is the best-of AFL that is currently out there :-)
 
 
diff --git a/docs/python_mutators.txt b/docs/python_mutators.txt
index 04fd7208..0d4eef1e 100644
--- a/docs/python_mutators.txt
+++ b/docs/python_mutators.txt
@@ -12,6 +12,8 @@ Adding custom mutators to AFL using Python modules
   NOTE: This is for Python 2.7 !
   Anyone who wants to add Python 3.7 support is happily welcome :)
 
+  For an example and a template see ../python_mutators/
+
 
 1) Description and purpose
 --------------------------
@@ -50,6 +52,7 @@ is requested.
 There is also optional support for a trimming API, see the section below for
 further information about this feature.
 
+
 3) How to compile AFLFuzz with Python support
 ---------------------------------------------
 
@@ -91,6 +94,7 @@ AFL_DEBUG       - When combined with AFL_NO_UI, this causes the C trimming code
                   to emit additional messages about the performance and actions
                   of your custom Python trimmer. Use this to see if it works :)
 
+
 5) Order and statistics
 -----------------------
 
@@ -99,6 +103,7 @@ the havoc stage). In the statistics however, it shows up as the third number
 under "havoc". That's because I'm lazy and I didn't want to mess with the UI
 too much ;)
 
+
 6) Trimming support
 -------------------
 
diff --git a/python_mutators/README b/python_mutators/README
new file mode 100644
index 00000000..174c8a64
--- /dev/null
+++ b/python_mutators/README
@@ -0,0 +1,11 @@
+These are example and helper files for the AFL_PYTHON_MODULE feature.
+See docs/python_mutators.txt for more information
+
+
+example.py	- this is the template you can use, the functions are there
+		  but they are empty
+
+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 :)
diff --git a/python_mutators/common.py b/python_mutators/common.py
new file mode 100644
index 00000000..28b8ee80
--- /dev/null
+++ b/python_mutators/common.py
@@ -0,0 +1,37 @@
+#!/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)    
diff --git a/python_mutators/example.py b/python_mutators/example.py
new file mode 100644
index 00000000..d32a7eb2
--- /dev/null
+++ b/python_mutators/example.py
@@ -0,0 +1,103 @@
+#!/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
+
+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)
+    return 0
+
+def fuzz(buf, add_buf):
+    '''
+    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.
+    
+    @rtype: bytearray
+    @return: A new bytearray containing the mutated data
+    '''
+    ret = bytearray(buf)
+    # Do something interesting with ret
+
+    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
diff --git a/python_mutators/simple-chunk-replace.py b/python_mutators/simple-chunk-replace.py
new file mode 100644
index 00000000..218dd4f8
--- /dev/null
+++ b/python_mutators/simple-chunk-replace.py
@@ -0,0 +1,59 @@
+#!/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)
+    return 0
+
+def fuzz(buf, add_buf):
+    '''
+    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.
+    
+    @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