├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── mutate.c ├── mutate.h ├── random.c ├── random.h ├── test ├── cases │ ├── 1 │ └── 2 ├── mutate_dir.c ├── mutate_file.c ├── mutate_multi_test.c ├── mutate_stdin.c └── mutate_test.c ├── testcase.c ├── testcase.h ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.o 3 | tags 4 | test/mutate 5 | test/mutate_dir 6 | test/mutate_multi 7 | test/mutate_file 8 | test/mutate_stdin 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, DoI 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = clang # -fsanitize=address -g -fno-omit-frame-pointer# C compiler 2 | CFLAGS = -fPIC -Wall -Werror -O3 # -D DEBUG # C flags 3 | LDFLAGS = -shared # linking flags 4 | RM = rm -f # rm command 5 | TARGET_LIB = libmutator.so # target lib 6 | 7 | SRCS = mutate.c random.c testcase.c util.c # source files 8 | OBJS = $(SRCS:.c=.o) 9 | 10 | .PHONY: all 11 | all: ${TARGET_LIB} 12 | 13 | $(TARGET_LIB): $(OBJS) 14 | $(CC) ${LDFLAGS} -o $@ $^ 15 | 16 | $(SRCS:.c):%.c 17 | $(CC) $(CFLAGS) -MM $< 18 | 19 | .PHONY: clean 20 | clean: 21 | -${RM} ${TARGET_LIB} ${OBJS} $(SRCS:.c=.d) 22 | 23 | .PHONY: test 24 | test: 25 | $(CC) -I./ -L./ -W test/mutate_test.c -o test/mutate -lmutator -lpthread 26 | $(CC) -I./ -L./ -W test/mutate_multi_test.c -o test/mutate_multi -lmutator -lpthread 27 | $(CC) -I./ -L./ -W test/mutate_stdin.c -o test/mutate_stdin -lmutator -lpthread 28 | $(CC) -I./ -L./ -W test/mutate_file.c -o test/mutate_file -lmutator -lpthread 29 | $(CC) -I./ -L./ -W test/mutate_dir.c -o test/mutate_dir -lmutator -lpthread 30 | 31 | .PHONY: install 32 | install: 33 | cp libmutator.so /usr/local/lib/ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Libmutator 2 | 3 | Libmutator is a C library intended to generate random test cases by mutating legitimate test cases. This project is heavily influenced by [honggfuzz](https://github.com/google/honggfuzz), [radamsa](https://gitlab.com/akihe/radamsa) and [AFL](http://lcamtuf.coredump.cx/afl/). 4 | 5 | This project started because I needed a way to go and generate random testcases in a C project and didn't want to include a complete fuzzing suite to do so. 6 | 7 | ## WIP 8 | 9 | Libmutator is still a work-in-progress, future updates, functionality and docs will follow. 10 | 11 | ## Basic usage 12 | 13 | See the test directory for examples of how to use libmutator. 14 | 15 | -------------------------------------------------------------------------------- /mutate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mutate.h" 7 | #include "random.h" 8 | #include "testcase.h" 9 | #include "util.h" 10 | 11 | extern int errno; 12 | int fuzzfactor = 5; 13 | 14 | void (* mutation_functions[])(testcase_t *) = { 15 | mutate_bit, 16 | mutate_bit2, 17 | mutate_bit4, 18 | mutate_byte, 19 | mutate_random, 20 | mutate_insert_byte, 21 | mutate_insert_bytes, 22 | mutate_insert_ascii_bytes, 23 | mutate_shuffle, 24 | mutate_erase, 25 | mutate_inc, 26 | mutate_dec, 27 | mutate_not 28 | }; 29 | 30 | 31 | // mutate a set of testcases 32 | testcase_t * mutate(testcase_list_t * testcase_list){ 33 | // select a test case 34 | testcase_t * testcase = list_testcase_rand(testcase_list); 35 | 36 | // decide whether to splice with another testcase here... 37 | 38 | // perform the mutation 39 | return mutate_testcase(testcase); 40 | } 41 | 42 | // mutate a testcase 43 | testcase_t * mutate_testcase(testcase_t * t){ 44 | int i; 45 | lm_debug("Provided testcase buf: %p\n", t->buf); 46 | 47 | testcase_t * mutated_testcase; 48 | lm_malloc(sizeof(testcase_t), mutated_testcase); 49 | lm_malloc(t->len, mutated_testcase->buf); 50 | mutated_testcase->len = t->len; 51 | memcpy(mutated_testcase->buf, t->buf, t->len); 52 | 53 | if(t->len == 0){ 54 | return NULL; 55 | } 56 | 57 | lm_debug("New testcase buf: %p\n", mutated_testcase->buf); 58 | 59 | // dict stuff goes here... 60 | 61 | // void (* funcs) = mutators->mutator_funcs; 62 | int total_changes = rand_between(1, fuzzfactor); // fuzzfactor sets the maximum number of stacked mutations 63 | for(i = 0; i < total_changes; i++){ 64 | uint64_t f = rand_at_most(sizeof(mutation_functions)/sizeof(*mutation_functions)-1); 65 | lm_debug("Running mutator %lu\n", f); 66 | mutation_functions[f](mutated_testcase); 67 | } 68 | 69 | return mutated_testcase; 70 | } 71 | 72 | void mutate_set_fuzzfactor(int ff){ 73 | // pthread lock here 74 | lm_debug("Setting fuzz factor to %d\n", ff); 75 | fuzzfactor = ff; 76 | } 77 | 78 | /* 79 | * Bit mutations 80 | */ 81 | 82 | // flip a random bit 83 | inline void mutate_bit(testcase_t * t){ 84 | uint64_t bit = rand_at_most((t->len<<3)-1); 85 | FLIP_BIT(t->buf, bit); 86 | } 87 | 88 | // flip 2 adjacent bits 89 | void mutate_bit2(testcase_t * t){ 90 | uint64_t bit = rand_at_most((t->len<<3)-2); 91 | 92 | FLIP_BIT(t->buf, bit); 93 | FLIP_BIT(t->buf, bit+1); 94 | } 95 | 96 | // flip 4 adjacent bits 97 | void mutate_bit4(testcase_t * t){ 98 | uint64_t bit = rand_at_most((t->len<<3)-4); 99 | 100 | FLIP_BIT(t->buf, bit); 101 | FLIP_BIT(t->buf, bit+1); 102 | FLIP_BIT(t->buf, bit+2); 103 | FLIP_BIT(t->buf, bit+3); 104 | } 105 | 106 | /* 107 | * Byte mutations 108 | */ 109 | 110 | // set a random byte 111 | void mutate_byte(testcase_t * t){ 112 | uint64_t off = rand_at_most(t->len-1); 113 | lm_debug("setting %lu\n", off); 114 | t->buf[off] = (uint8_t)rand_next(); 115 | } 116 | 117 | // set a randomly sized chunk of buf to random values 118 | void mutate_random(testcase_t * t){ 119 | uint64_t offset = rand_at_most(t->len - 1); 120 | uint64_t len = rand_between(1, t->len - offset); 121 | rand_buf(&t->buf[offset], len); 122 | } 123 | 124 | // insert a random byte 125 | void mutate_insert_byte(testcase_t * t){ 126 | uint64_t off = rand_at_most(t->len - 1); 127 | 128 | lm_debug("inserting at %lu\n", off); 129 | t->buf = realloc(t->buf, t->len+1); 130 | if(t->buf == NULL){ 131 | fatal("realloc() failed"); 132 | } 133 | 134 | memmove(t->buf + off + 1, t->buf + off, t->len-off); 135 | t->buf[off] = (uint8_t)rand_next(); 136 | t->len += 1; 137 | } 138 | 139 | // insert up to t->len random bytes 140 | void mutate_insert_bytes(testcase_t * t){ 141 | uint64_t off = rand_at_most(t->len - 1); 142 | uint64_t len = rand_at_most(t->len); 143 | 144 | lm_debug("inserting at %lu len %lu\n", off, len); 145 | t->buf = realloc(t->buf, t->len+len); 146 | if(t->buf == NULL){ 147 | fatal("realloc() failed"); 148 | } 149 | 150 | memmove(t->buf+off+len, t->buf + off, t->len-off); 151 | uint64_t i; 152 | for(i = 0; i < len; i++){ 153 | t->buf[off+i] = (uint8_t)rand_next(); 154 | } 155 | 156 | t->len += len; 157 | } 158 | 159 | // insert up to t->len of printable ASCII bytes 160 | void mutate_insert_ascii_bytes(testcase_t * t){ 161 | uint64_t off = rand_at_most(t->len -1); 162 | uint64_t len = rand_at_most(t->len); 163 | 164 | lm_debug("inserting ASCII bytes at %lu len %lu\n", off, len); 165 | t->buf = realloc(t->buf, t->len+len); 166 | 167 | if(t->buf == NULL){ 168 | fatal("realloc() failed"); 169 | } 170 | 171 | memmove(t->buf+off+len, t->buf +off, t->len-off); 172 | uint64_t i; 173 | for(i = 0; i < len; i++){ 174 | t->buf[off+i] = (uint8_t)rand_between(32,136); 175 | } 176 | 177 | t->len += len; 178 | } 179 | 180 | 181 | // shuffle a block of bytes, no more than 8 bytes 182 | void mutate_shuffle(testcase_t * t){ 183 | if(t->len <= 1){ 184 | return; 185 | } 186 | else if (t->len == 2){ 187 | uint8_t a = t->buf[1]; 188 | t->buf[1] = t->buf[0]; 189 | t->buf[0] = a; 190 | return; 191 | } 192 | 193 | uint64_t len = (t->len > 8) ? rand_between(2, 8) : rand_between(2, t->len-1); 194 | uint64_t offset = rand_at_most(t->len - len); 195 | 196 | lm_debug("shuffling len: %lu offset: %lu\n", len, offset); 197 | 198 | if(unlikely((len + offset) > t->len)){ 199 | fatal("mutate_shuffle out of bounds"); 200 | } 201 | 202 | uint64_t i; 203 | for(i = 0; i < len-1; i++){ 204 | uint64_t off = rand_at_most(len-1); 205 | uint8_t a = t->buf[off+offset]; 206 | t->buf[off+offset] = t->buf[i+offset]; 207 | t->buf[i+offset] = a; 208 | } 209 | } 210 | 211 | // erase a random number of bytes, no more than len/2 212 | void mutate_erase(testcase_t * t){ 213 | uint64_t len = rand_at_most(t->len/2); 214 | uint64_t offset = rand_at_most(t->len - len); 215 | 216 | if(unlikely((len + offset) > t->len)){ 217 | fatal("mutate_erase out of bounds: len: %lu offset: %lu", len, offset); 218 | } 219 | 220 | lm_debug("Erasing len %lu at offset %lu\n", len, offset); 221 | memmove(t->buf + offset, t->buf + offset + len, t->len - offset - len); 222 | t->len -= len; 223 | } 224 | 225 | /* 226 | * Arithmetic mutations 227 | */ 228 | 229 | // increment a random byte 230 | void mutate_inc(testcase_t * t){ 231 | uint64_t offset = rand_at_most(t->len-1); 232 | lm_debug("Incrementing byte %lu\n", offset); 233 | t->buf[offset] += 1; 234 | } 235 | 236 | // decrement a random byte 237 | void mutate_dec(testcase_t * t){ 238 | uint64_t offset = rand_at_most(t->len-1); 239 | lm_debug("Decrementing byte %lu\n", offset); 240 | t->buf[offset] -= 1; 241 | } 242 | 243 | // invert a random byte 244 | void mutate_not(testcase_t * t){ 245 | uint64_t offset = rand_at_most(t->len-1); 246 | lm_debug("Inverting byte %lu\n", offset); 247 | t->buf[offset] =~ t->buf[offset]; 248 | } 249 | -------------------------------------------------------------------------------- /mutate.h: -------------------------------------------------------------------------------- 1 | #include "testcase.h" 2 | 3 | #ifndef MUTATE_H 4 | #define MUTATE_H 5 | 6 | #define FLIP_BIT(_ar, _b) do { \ 7 | uint8_t* _arf = (uint8_t*)(_ar); \ 8 | uint32_t _bf = (_b); \ 9 | _arf[_bf >> 3] ^= (1 << (_bf &7)); \ 10 | } while (0) 11 | 12 | extern void mutate_init(); 13 | extern testcase_t * mutate(testcase_list_t * t); 14 | extern testcase_t * mutate_testcase(testcase_t * t); 15 | extern void mutate_set_fuzzfactor(int ff); 16 | extern void mutate_bit(testcase_t * t); 17 | extern void mutate_bit2(testcase_t * t); 18 | extern void mutate_bit4(testcase_t * t); 19 | extern void mutate_byte(testcase_t * t); 20 | extern void mutate_random(testcase_t * t); 21 | extern void mutate_insert_byte(testcase_t * t); 22 | extern void mutate_insert_bytes(testcase_t * t); 23 | extern void mutate_insert_ascii_bytes(testcase_t * t); 24 | extern void mutate_shuffle(testcase_t * t); 25 | extern void mutate_erase(testcase_t * t); 26 | extern void mutate_inc(testcase_t * t); 27 | extern void mutate_dec(testcase_t * t); 28 | extern void mutate_not(testcase_t * t); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util.h" 12 | 13 | // xoroshiro128+ 14 | static __thread pthread_once_t r_tonce = PTHREAD_ONCE_INIT; 15 | static __thread uint64_t r_state[2]; 16 | static int seeded = 0; 17 | 18 | static inline uint64_t rotl(const uint64_t x, int k) { 19 | return (x << k) | (x >> (64 - k)); 20 | } 21 | 22 | void init_seed(){ 23 | lm_debug("seeding from /dev/urandom\n"); 24 | int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); 25 | if (fd == -1) { 26 | fatal("Couldn't open /dev/urandom for reading"); 27 | } 28 | if (read(fd, (uint8_t*)r_state, sizeof(r_state)) != sizeof(r_state)) { 29 | fatal("Couldn't read '%zu' bytes from /dev/urandom", sizeof(r_state)); 30 | } 31 | close(fd); 32 | // printf("[!] Seeded\n"); 33 | } 34 | 35 | void manual_seed(unsigned int seed){ 36 | lm_debug("manually seeding with %u\n", seed); 37 | srand(seed); 38 | int r1 = rand(); 39 | int r2 = rand(); 40 | lm_debug("rand values %d %d\n", r1, r2); 41 | r_state[0] = r1; 42 | r_state[1] = r2; 43 | seeded = 1; 44 | } 45 | 46 | uint64_t rand_next(void) { 47 | if(seeded == 0){ 48 | pthread_once(&r_tonce, init_seed); 49 | } 50 | 51 | const uint64_t s0 = r_state[0]; 52 | uint64_t s1 = r_state[1]; 53 | const uint64_t result = s0 + s1; 54 | 55 | s1 ^= s0; 56 | r_state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b 57 | r_state[1] = rotl(s1, 36); // c 58 | 59 | return result; 60 | } 61 | 62 | // returns a random number between 0 and n 63 | uint64_t rand_at_most(uint64_t n){ 64 | if(n == 0){ 65 | return 0; 66 | } 67 | return rand_next() % (n+1); 68 | } 69 | 70 | // returns a random number between min and max 71 | uint64_t rand_between(uint64_t min, uint64_t max){ 72 | if(min > max){ 73 | fatal("Min > Max"); 74 | } 75 | if(max == UINT64_MAX){ 76 | return rand_next(); 77 | } 78 | return rand_at_most(max - min) + min; 79 | } 80 | 81 | // populates a buffer with random bytes 82 | void rand_buf(uint8_t * buf, uint64_t size){ 83 | uint64_t i; 84 | 85 | if(size == 0){ 86 | fatal("rand_buf called with size == 0"); 87 | } 88 | 89 | for(i = 0; i < size; i++){ 90 | buf[i] = (uint8_t)rand_next(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /random.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef RANDOM_H 3 | #define RANDOM_H 4 | 5 | extern uint64_t random_next(void); 6 | extern void init_seed(); 7 | void manual_seed(unsigned int seed); 8 | extern uint64_t rand_next(void); 9 | extern uint64_t rand_at_most(uint64_t n); 10 | extern uint64_t rand_between(uint64_t min, uint64_t max); 11 | extern void rand_buf(uint8_t * buf, uint64_t size); 12 | 13 | #endif -------------------------------------------------------------------------------- /test/cases/1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denandz/libmutator/5f1ea0a8b6673de0191d837865d26b4cc7a11549/test/cases/1 -------------------------------------------------------------------------------- /test/cases/2: -------------------------------------------------------------------------------- 1 | test files test.... smiles? 2 | -------------------------------------------------------------------------------- /test/mutate_dir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../mutate.h" 5 | #include "../testcase.h" 6 | #include "util.h" 7 | 8 | // load all the files in a directory into a testcase list, perform 20 mutations 9 | int main(){ 10 | 11 | testcase_list_t * t = list_testcase_load_dir("./cases"); 12 | 13 | int i; 14 | for(i = 0; i < 20; i++){ 15 | testcase_t * mutated = mutate(t); 16 | dump_hex(mutated->buf, mutated->len); 17 | testcase_free(mutated); 18 | } 19 | 20 | list_testcase_free(t); 21 | 22 | return 1; 23 | } 24 | -------------------------------------------------------------------------------- /test/mutate_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "mutate.h" 9 | #include "random.h" 10 | #include "util.h" 11 | #include 12 | 13 | extern int errno; 14 | 15 | int main(int argc, char ** argv){ 16 | if(argc < 2) 17 | fatal("run with ./mutate_file "); 18 | 19 | int fd; 20 | fd = open(argv[1], O_RDWR); 21 | if(fd == -1){ 22 | fatal("open failed: %s", strerror(errno)); 23 | } 24 | 25 | FILE * fp; 26 | if((fp = fopen(argv[1], "r"))== NULL){ 27 | fatal("[!] Error: Could not open file %s: %s\n", argv[1], strerror(errno)); 28 | } 29 | 30 | uint8_t * file; 31 | long bufsize; 32 | 33 | if (fseek(fp, 0L, SEEK_END) == 0) { 34 | bufsize = ftell(fp); 35 | if (bufsize == -1){ 36 | fatal("[!] Error with ftell: %s", strerror(errno)); 37 | } 38 | else if(bufsize == 0){ // handle empty file 39 | fatal("empty file"); 40 | } 41 | 42 | // Go back to the start of the file. 43 | if (fseek(fp, 0L, SEEK_SET) != 0){ 44 | fatal("[!] Error: could not fseek: %s\n", strerror(errno)); 45 | } 46 | 47 | // Read the entire file into memory. 48 | lm_malloc(bufsize, file); 49 | if(fread(file, sizeof(char), bufsize, fp) != (unsigned long)bufsize){ 50 | fatal("[!] Error: fread"); 51 | } 52 | 53 | if ( ferror( fp ) != 0 ){ 54 | fatal("[!] Error: fread: %s\n", strerror(errno)); 55 | } 56 | } 57 | else{ 58 | fatal("fseek"); 59 | } 60 | fclose(fp); 61 | 62 | #ifdef DEBUG 63 | dump_hex(file, bufsize); 64 | #endif 65 | 66 | testcase_t * a = testcase_load(file, bufsize); 67 | testcase_t * b = mutate_testcase(a); 68 | 69 | #ifdef DEBUG 70 | dump_hex(b->buf, b->len); 71 | #endif 72 | 73 | fwrite(a->buf, a->len, sizeof(uint8_t), stdout); 74 | 75 | testcase_free(a); 76 | testcase_free(b); 77 | free(file); 78 | return 1; 79 | } 80 | -------------------------------------------------------------------------------- /test/mutate_multi_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../mutate.h" 5 | #include "../testcase.h" 6 | #include "util.h" 7 | 8 | // mutate a packet, expect a different packet back 9 | int main(){ 10 | char packet1[] = "test this packet, what will happen?!"; 11 | char packet2[] = "another test packet"; 12 | char packet3[] = "do lizards like, know that they're lizards?"; 13 | 14 | testcase_t * a = testcase_load((uint8_t *)packet1, sizeof(packet1)); 15 | printf("testcase len %lu loaded len %lu\n", sizeof(packet1), a->len); 16 | testcase_t * b = testcase_load((uint8_t *)packet2, sizeof(packet2)); 17 | printf("testcase len %lu loaded len %lu\n", sizeof(packet2), b->len); 18 | testcase_t * c = testcase_load((uint8_t *)packet3, sizeof(packet3)); 19 | printf("testcase len %lu loaded len %lu\n", sizeof(packet3), c->len); 20 | 21 | // init the testcase list 22 | testcase_list_t * list = testcase_list_init(); 23 | list_testcase_add(list, a); 24 | list_testcase_add(list, b); 25 | list_testcase_add(list, c); 26 | 27 | // set the fuzzfactor 28 | mutate_set_fuzzfactor(5); 29 | 30 | int i; 31 | for(i = 0; i < 20; i++){ 32 | testcase_t * mutated = mutate(list); 33 | dump_hex(mutated->buf, mutated->len); 34 | testcase_free(mutated); 35 | } 36 | 37 | list_testcase_free(list); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /test/mutate_stdin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "mutate.h" 7 | #include "random.h" 8 | #include "util.h" 9 | 10 | // mutate a packet, expect a different packet back 11 | int main(){ 12 | uint8_t * input; 13 | lm_malloc(1024, input); 14 | memset(input, 0x00, 1024); 15 | uint64_t i = 0; 16 | uint64_t bufflen = 1024; 17 | 18 | while(1){ 19 | int c = getchar(); 20 | if(c == EOF) 21 | break; 22 | input[i] = c; 23 | i++; 24 | if(i > bufflen){ 25 | input = realloc(input, bufflen+1024); 26 | bufflen += 1024; 27 | } 28 | } 29 | 30 | lm_debug("Got input len %lu\n", i); 31 | 32 | dump_hex(input, i); 33 | 34 | testcase_t * a = testcase_load(input, i); 35 | testcase_t * b = mutate_testcase(a); 36 | 37 | printf("len: %lu\n", b->len); 38 | dump_hex(b->buf, b->len); 39 | 40 | free(input); 41 | testcase_free(a); 42 | testcase_free(b); 43 | return 1; 44 | } 45 | -------------------------------------------------------------------------------- /test/mutate_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mutate.h" 5 | #include "util.h" 6 | 7 | // mutate a packet, expect a different packet back 8 | int main(){ 9 | char packet[] = "test this packet, what will happen?!"; 10 | testcase_t * a = testcase_load((uint8_t *)packet, sizeof(packet)); 11 | testcase_t * b; 12 | 13 | printf("testcase len %lu loaded len %lu\n", sizeof(packet), a->len); 14 | 15 | int i; 16 | for(i = 0; i < 8; i++){ 17 | b = mutate_testcase(a); 18 | dump_hex(b->buf, b->len); 19 | testcase_free(b); 20 | } 21 | 22 | testcase_free(a); 23 | return 1; 24 | } 25 | -------------------------------------------------------------------------------- /testcase.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "random.h" 8 | #include "testcase.h" 9 | #include "util.h" 10 | 11 | extern int error; 12 | 13 | // load a testcase into a testcase_t structure 14 | testcase_t * testcase_load(uint8_t * buf, uint64_t len){ 15 | testcase_t * t; 16 | lm_malloc(sizeof(testcase_t), t); 17 | memset(t, 0x00, sizeof(testcase_t)); 18 | 19 | lm_malloc(len, t->buf); 20 | memcpy(t->buf, buf, len); 21 | t->len = len; //= t->orig_len; 22 | 23 | lm_debug("loaded testcase with len %lu\n", len); 24 | return t; 25 | } 26 | 27 | // load a file into a testcase_t structure 28 | testcase_t * testcase_load_file(char * path){ 29 | long bufsize; 30 | uint8_t * filebuf; 31 | FILE * fp; 32 | 33 | if((fp = fopen(path, "r"))== NULL){ 34 | fatal("Could not open file %s: %s\n", path, strerror(errno)); 35 | } 36 | 37 | if (fseek(fp, 0L, SEEK_END) == 0) { 38 | bufsize = ftell(fp); 39 | if (bufsize == -1){ 40 | fatal("Error with ftell: %s", strerror(errno)); 41 | return NULL; 42 | } 43 | else if(bufsize == 0){ // handle empty file 44 | fatal("empty file"); 45 | return NULL; 46 | } 47 | 48 | // Go back to the start of the file. 49 | if (fseek(fp, 0L, SEEK_SET) != 0){ 50 | fatal("Could not fseek: %s\n", strerror(errno)); 51 | return NULL; 52 | } 53 | 54 | // Read the entire file into memory. 55 | lm_malloc(bufsize, filebuf); 56 | if(fread(filebuf, sizeof(char), bufsize, fp) != (unsigned long)bufsize){ 57 | fatal("fread"); 58 | } 59 | 60 | if ( ferror( fp ) != 0 ){ 61 | fatal("fread: %s\n", strerror(errno)); 62 | } 63 | } 64 | else{ 65 | fatal("Could not fseek: %s\n", strerror(errno)); 66 | } 67 | 68 | fclose(fp); 69 | testcase_t * r = testcase_load(filebuf, bufsize); 70 | free(filebuf); 71 | return r; 72 | } 73 | 74 | // free() the testcase 75 | void testcase_free(testcase_t * t){ 76 | free(t->buf); 77 | t->buf = NULL; 78 | free(t); 79 | t = NULL; 80 | } 81 | 82 | // Create a testcase list 83 | testcase_list_t * testcase_list_init(){ 84 | testcase_list_t * testcase_list; 85 | lm_malloc(sizeof(testcase_list_t), testcase_list); 86 | memset(testcase_list, 0x00, sizeof(testcase_list_t)); 87 | testcase_list->count = 0; 88 | 89 | return testcase_list; 90 | } 91 | 92 | // Add testcase to list 93 | void list_testcase_add(testcase_list_t * list, testcase_t * t){ 94 | ++list->count; 95 | lm_debug("New list count: %u\n", list->count); 96 | 97 | list->testcases = realloc(list->testcases, sizeof(testcase_t)*list->count+1); 98 | if(list->testcases == NULL){ 99 | fatal("realloc failed"); 100 | } 101 | 102 | list->testcases[list->count-1] = t; 103 | } 104 | 105 | // return a random testcase from the list 106 | testcase_t * list_testcase_rand(testcase_list_t * list){ 107 | uint32_t entry = rand_at_most(list->count-1); 108 | lm_debug("selecting testcase %u. list->count %u\n", entry, list->count); 109 | return list->testcases[entry]; 110 | } 111 | 112 | // splice two testcases together 113 | testcase_t * splice_testcase(testcase_t * a, testcase_t * b){ 114 | uint64_t off = rand_at_most(a->len - 1); 115 | uint8_t * buf; 116 | testcase_t * t; 117 | 118 | lm_malloc(sizeof(b->len)+off, buf); 119 | 120 | memcpy(buf, a->buf, off); 121 | memcpy(buf+off, b->buf, b->len); 122 | t = testcase_load(buf, off+b->len); 123 | 124 | free(buf); 125 | return t; 126 | } 127 | 128 | // load all testcases from a given dir 129 | testcase_list_t * list_testcase_load_dir(char * path){ 130 | testcase_list_t * t = testcase_list_init(); 131 | uint8_t * buf; 132 | DIR * dir; 133 | struct dirent *ents; 134 | 135 | if((dir = opendir(path)) == NULL){ 136 | fatal("Could not open directory %s: %s\n", path, strerror(errno)); 137 | } 138 | 139 | while ((ents = readdir(dir)) != NULL){ 140 | // The below assumes a filesystem that supports returning types in dirent structs. 141 | if(ents->d_type != DT_REG) 142 | continue; 143 | 144 | FILE * fp; 145 | char file_path[PATH_MAX]; 146 | 147 | snprintf(file_path, PATH_MAX, "%s/%s", path, ents->d_name); 148 | if((fp = fopen(file_path, "r"))== NULL){ 149 | fatal("Could not open file %s: %s\n", file_path, strerror(errno)); 150 | } 151 | 152 | if (fseek(fp, 0L, SEEK_END) == 0) { 153 | long bufsize = ftell(fp); 154 | if (bufsize == -1){ 155 | fatal("Error in ftell: %s", strerror(errno)); 156 | } 157 | else if(bufsize == 0){ // handle empty file 158 | fclose(fp); 159 | continue; 160 | } 161 | 162 | // Go back to the start of the file. 163 | if (fseek(fp, 0L, SEEK_SET) != 0){ 164 | fatal("Could not fseek: %s\n", strerror(errno)); 165 | } 166 | 167 | // Read the entire file into memory. 168 | lm_malloc(bufsize, buf); 169 | if(fread(buf, sizeof(uint8_t), bufsize, fp) != (size_t)bufsize){ 170 | fatal("fread"); 171 | } 172 | 173 | if ( ferror( fp ) != 0 ){ 174 | fatal("fread: %s\n", strerror(errno)); 175 | } 176 | 177 | testcase_t * tcase = testcase_load(buf, bufsize); 178 | list_testcase_add(t, tcase); 179 | free(buf); 180 | } 181 | 182 | fclose(fp); 183 | } 184 | 185 | closedir(dir); 186 | 187 | if(t->count == 0){ // no cases found 188 | list_testcase_free(t); 189 | return NULL; 190 | } 191 | 192 | return t; // place holder 193 | } 194 | 195 | void list_testcase_free(testcase_list_t * t){ 196 | uint32_t i; 197 | for(i = 0; i < t->count; i++){ 198 | testcase_free(t->testcases[i]); 199 | } 200 | 201 | free(t->testcases); 202 | free(t); 203 | } 204 | -------------------------------------------------------------------------------- /testcase.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef TESTCASE_H 4 | #define TESTCASE_H 5 | 6 | typedef struct { 7 | uint8_t * buf; // testcase buffer 8 | uint64_t len; // length of the testcase 9 | uint64_t num; // number of mutations performed 10 | } testcase_t; 11 | 12 | typedef struct { 13 | uint32_t count; 14 | testcase_t ** testcases; 15 | } testcase_list_t; 16 | 17 | void testcase_reset(testcase_t * t); 18 | testcase_t * testcase_load(uint8_t * buf, uint64_t len); 19 | testcase_t * testcase_load_file(char * path); 20 | void testcase_free(testcase_t * t); 21 | testcase_list_t * testcase_list_init(); 22 | void list_testcase_add(testcase_list_t * list, testcase_t * t); 23 | testcase_t * list_testcase_rand(testcase_list_t * list); 24 | testcase_list_t * list_testcase_load_dir(char * path); 25 | void list_testcase_free(testcase_list_t * t); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util.h" 3 | 4 | // from https://gist.github.com/ccbrown/9722406 5 | void dump_hex(const void* data, size_t size) { 6 | char ascii[17]; 7 | size_t i, j; 8 | ascii[16] = '\0'; 9 | for (i = 0; i < size; i++) { 10 | printf("%02X ", ((unsigned char*)data)[i]); 11 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 12 | ascii[i % 16] = ((unsigned char*)data)[i]; 13 | } else { 14 | ascii[i % 16] = '.'; 15 | } 16 | if ((i+1) % 8 == 0 || i+1 == size) { 17 | printf(" "); 18 | if ((i+1) % 16 == 0) { 19 | printf("| %s \n", ascii); 20 | } else if (i+1 == size) { 21 | ascii[(i+1) % 16] = '\0'; 22 | if ((i+1) % 16 <= 8) { 23 | printf(" "); 24 | } 25 | for (j = (i+1) % 16; j < 16; ++j) { 26 | printf(" "); 27 | } 28 | printf("| %s \n", ascii); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef UTIL_H 5 | #define UTIL_H 6 | 7 | #define RED "\x1B[31m" 8 | #define GRN "\x1B[32m" 9 | #define YEL "\x1B[33m" 10 | #define BLU "\x1B[34m" 11 | #define MAG "\x1B[35m" 12 | #define CYN "\x1B[36m" 13 | #define WHT "\x1B[37m" 14 | #define RESET "\x1B[0m" 15 | 16 | #define fatal(x...) \ 17 | do { \ 18 | fprintf(stderr, RED "[!] ERROR: " RESET); \ 19 | fprintf(stderr, x); \ 20 | fprintf(stderr, "\n Location : %s(), %s:%d\n\n", \ 21 | __FUNCTION__, __FILE__, __LINE__); \ 22 | exit(0);\ 23 | } while(0) 24 | 25 | 26 | 27 | #define lm_malloc(len,ptr) \ 28 | do { \ 29 | if(NULL == (ptr = malloc(len))){\ 30 | fatal("[!] Malloc failed\n"); \ 31 | }\ 32 | } while(0) 33 | 34 | #define unlikely(expr) __builtin_expect(!!(expr), 0) 35 | #define likely(expr) __builtin_expect(!!(expr), 1) 36 | #ifdef DEBUG 37 | #define lm_debug(x...) \ 38 | do { \ 39 | fprintf(stderr, CYN "[.] DEBUG: " RESET); \ 40 | fprintf(stderr, x); \ 41 | } while(0) 42 | #else 43 | #define lm_debug(x...) 44 | #endif 45 | 46 | extern void dump_hex(const void* data, size_t size); 47 | 48 | #endif 49 | --------------------------------------------------------------------------------