| // 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> |
| |
| #include "afl-fuzz.h" |
| #include "gramfuzz.h" |
| |
| #define MUTATORS 4 // Specify the total number of mutators |
| |
| typedef struct my_mutator { |
| |
| afl_state_t *afl; |
| |
| u8 * mutator_buf; |
| u8 * unparsed_input; |
| Array *mutated_walk; |
| Array *orig_walk; |
| |
| IdxMap_new *statemap; // Keeps track of the statemap |
| UT_array ** recurIdx; |
| // Get_Dupes_Ret* getdupesret; // Recursive feature map |
| int recurlen; |
| |
| int mut_alloced; |
| int orig_alloced; |
| int mut_idx; // Signals the current mutator being used, used to cycle through |
| // each mutator |
| |
| unsigned int seed; |
| |
| } my_mutator_t; |
| |
| state *create_pda(u8 *automaton_file) { |
| |
| struct json_object *parsed_json; |
| state * pda; |
| json_object * source_obj, *attr; |
| int arraylen, ii, ii2, trigger_len, error; |
| |
| printf("\n[GF] Automaton file passed:%s", automaton_file); |
| // parsed_json = |
| // json_object_from_file("./gramfuzz/php_gnf_processed_full.json"); |
| parsed_json = json_object_from_file(automaton_file); |
| |
| // Getting final state |
| source_obj = json_object_object_get(parsed_json, "final_state"); |
| printf("\t\nFinal=%s\n", json_object_get_string(source_obj)); |
| final_state = atoi(json_object_get_string(source_obj)); |
| |
| // Getting initial state |
| source_obj = json_object_object_get(parsed_json, "init_state"); |
| init_state = atoi(json_object_get_string(source_obj)); |
| printf("\tInit=%s\n", json_object_get_string(source_obj)); |
| |
| // Getting number of states |
| source_obj = json_object_object_get(parsed_json, "numstates"); |
| numstates = atoi(json_object_get_string(source_obj)) + 1; |
| printf("\tNumStates=%d\n", numstates); |
| |
| // Allocate state space for each pda state |
| pda = (state *)calloc(atoi(json_object_get_string(source_obj)) + 1, |
| sizeof(state)); |
| |
| // Getting PDA representation |
| source_obj = json_object_object_get(parsed_json, "pda"); |
| enum json_type type; |
| json_object_object_foreach(source_obj, key, val) { |
| |
| state * state_ptr; |
| trigger *trigger_ptr; |
| int offset; |
| |
| // Get the correct offset into the pda to store state information |
| state_ptr = pda; |
| offset = atoi(key); |
| state_ptr += offset; |
| // Store state string |
| state_ptr->state_name = offset; |
| |
| // Create trigger array of structs |
| trigger_len = json_object_array_length(val); |
| state_ptr->trigger_len = trigger_len; |
| trigger_ptr = (trigger *)calloc(trigger_len, sizeof(trigger)); |
| state_ptr->ptr = trigger_ptr; |
| |
| for (ii = 0; ii < trigger_len; ii++) { |
| |
| json_object *obj = json_object_array_get_idx(val, ii); |
| // Get all the trigger trigger attributes |
| attr = json_object_array_get_idx(obj, 0); |
| (trigger_ptr)->id = strdup(json_object_get_string(attr)); |
| |
| attr = json_object_array_get_idx(obj, 1); |
| trigger_ptr->dest = atoi(json_object_get_string(attr)); |
| |
| attr = json_object_array_get_idx(obj, 2); |
| if (!strcmp("\\n", json_object_get_string(attr))) { |
| |
| trigger_ptr->term = strdup("\n"); |
| |
| } else { |
| |
| trigger_ptr->term = strdup(json_object_get_string(attr)); |
| |
| } |
| |
| trigger_ptr->term_len = strlen(trigger_ptr->term); |
| trigger_ptr++; |
| |
| } |
| |
| } |
| |
| // Delete the JSON object |
| json_object_put(parsed_json); |
| |
| return pda; |
| |
| } |
| |
| my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { |
| |
| my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); |
| if (!data) { |
| |
| perror("afl_custom_init alloc"); |
| return NULL; |
| |
| } |
| |
| if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) { |
| |
| perror("mutator_buf alloc"); |
| return NULL; |
| |
| } |
| |
| data->afl = afl; |
| global_afl = afl; // dirty |
| data->seed = seed; |
| |
| data->mut_alloced = 0; |
| data->orig_alloced = 0; |
| data->mut_idx = 0; |
| data->recurlen = 0; |
| |
| // data->mutator_buf = NULL; |
| // data->unparsed_input = NULL; |
| // data->mutated_walk = NULL; |
| // data->orig_walk = NULL; |
| // |
| // data->statemap = NULL; // Keeps track of the statemap |
| // data->recur_idx = NULL; // Will keep track of recursive feature indices |
| // u32 recur_len = 0; // The number of recursive features |
| // data->mutator_buf = NULL; |
| |
| char *automaton_file = getenv("GRAMATRON_AUTOMATION"); |
| if (automaton_file) { |
| |
| pda = create_pda(automaton_file); |
| |
| } else { |
| |
| fprintf(stderr, |
| "\nError: GrammaTron needs an automation json file set in " |
| "GRAMATRON_AUTOMATION\n"); |
| exit(-1); |
| |
| } |
| |
| 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, |
| size_t max_size) { |
| |
| u8 *unparsed_input; |
| |
| // Pick a mutator |
| // int choice = rand() % MUTATORS; |
| // data->mut_idx = 1; |
| // GC old mutant |
| if (data->mut_alloced) { |
| |
| free(data->mutated_walk->start); |
| free(data->mutated_walk); |
| data->mut_alloced = 0; |
| |
| }; |
| |
| // printf("\nChoice:%d", choice); |
| |
| if (data->mut_idx == 0) { // Perform random mutation |
| data->mutated_walk = performRandomMutation(pda, data->orig_walk); |
| data->mut_alloced = 1; |
| |
| } else if (data->mut_idx == 1 && |
| |
| data->recurlen) { // Perform recursive mutation |
| data->mutated_walk = |
| doMult(data->orig_walk, data->recurIdx, data->recurlen); |
| data->mut_alloced = 1; |
| |
| } else if (data->mut_idx == 2) { // Perform splice mutation |
| |
| // we cannot use the supplied splice data so choose a new random file |
| u32 tid = rand_below(global_afl, data->afl->queued_items); |
| struct queue_entry *q = data->afl->queue_buf[tid]; |
| |
| // Read the input representation for the splice candidate |
| u8 * automaton_fn = alloc_printf("%s.aut", q->fname); |
| Array *spliceCandidate = read_input(pda, automaton_fn); |
| |
| if (spliceCandidate) { |
| |
| data->mutated_walk = |
| performSpliceOne(data->orig_walk, data->statemap, spliceCandidate); |
| data->mut_alloced = 1; |
| free(spliceCandidate->start); |
| free(spliceCandidate); |
| |
| } else { |
| |
| data->mutated_walk = gen_input(pda, NULL); |
| data->mut_alloced = 1; |
| |
| } |
| |
| ck_free(automaton_fn); |
| |
| } else { // Generate an input from scratch |
| |
| data->mutated_walk = gen_input(pda, NULL); |
| data->mut_alloced = 1; |
| |
| } |
| |
| // Cycle to the next mutator |
| if (data->mut_idx == MUTATORS - 1) |
| data->mut_idx = |
| 0; // Wrap around if we have reached end of the mutator list |
| else |
| data->mut_idx += 1; |
| |
| // Unparse the mutated automaton walk |
| if (data->unparsed_input) { free(data->unparsed_input); } |
| data->unparsed_input = unparse_walk(data->mutated_walk); |
| *out_buf = data->unparsed_input; |
| |
| return data->mutated_walk->inputlen; |
| |
| } |
| |
| /** |
| * Create the automaton-based representation for the corresponding input |
| * |
| * @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 |
| */ |
| u8 afl_custom_queue_new_entry(my_mutator_t * data, |
| const uint8_t *filename_new_queue, |
| const uint8_t *filename_orig_queue) { |
| |
| // get the filename |
| u8 * automaton_fn, *unparsed_input; |
| Array *new_input; |
| s32 fd; |
| |
| automaton_fn = alloc_printf("%s.aut", filename_new_queue); |
| // Check if this method is being called during initialization |
| |
| // fprintf(stderr, "new: %s, old: %s, auto: %s\n", |
| // filename_new_queue,filename_orig_queue,automaton_fn); |
| |
| if (filename_orig_queue) { |
| |
| write_input(data->mutated_walk, automaton_fn); |
| |
| } else { |
| |
| new_input = gen_input(pda, NULL); |
| write_input(new_input, automaton_fn); |
| |
| // Update the placeholder file |
| if (unlink(filename_new_queue)) { |
| |
| PFATAL("Unable to delete '%s'", filename_new_queue); |
| |
| } |
| |
| unparsed_input = unparse_walk(new_input); |
| fd = open(filename_new_queue, O_WRONLY | O_CREAT | O_TRUNC, |
| S_IRUSR | S_IWUSR); |
| if (fd < 0) { PFATAL("Failed to update file '%s'", filename_new_queue); } |
| int written = write(fd, unparsed_input, new_input->inputlen + 1); |
| close(fd); |
| |
| free(new_input->start); |
| free(new_input); |
| free(unparsed_input); |
| |
| } |
| |
| ck_free(automaton_fn); |
| |
| return 1; |
| |
| } |
| |
| /** |
| * Get the corresponding tree representation for the candidate that is to be |
| * mutated |
| * |
| * @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) { |
| |
| // get the filename |
| u8 * automaton_fn = alloc_printf("%s.aut", filename); |
| IdxMap_new *statemap_ptr; |
| terminal * term_ptr; |
| int state; |
| |
| // TODO: I don't think we need to update pointers when reading back |
| // Probably build two different versions of read_input one for flushing |
| // inputs to disk and the other that |
| if (data->orig_alloced) { |
| |
| free(data->orig_walk->start); |
| free(data->orig_walk); |
| data->orig_alloced = 0; |
| |
| } |
| |
| if (data->statemap) { |
| |
| for (int x = 0; x < numstates; x++) { |
| |
| utarray_free(data->statemap[x].nums); |
| |
| } |
| |
| free(data->statemap); |
| |
| } |
| |
| if (data->recurIdx) { |
| |
| data->recurlen = 0; |
| free(data->recurIdx); |
| |
| } |
| |
| data->orig_walk = read_input(pda, automaton_fn); |
| data->orig_alloced = 1; |
| |
| // Create statemap for the fuzz candidate |
| IdxMap_new *statemap_start = |
| (IdxMap_new *)malloc(sizeof(IdxMap_new) * numstates); |
| for (int x = 0; x < numstates; x++) { |
| |
| statemap_ptr = &statemap_start[x]; |
| utarray_new(statemap_ptr->nums, &ut_int_icd); |
| |
| } |
| |
| int offset = 0; |
| while (offset < data->orig_walk->used) { |
| |
| term_ptr = &data->orig_walk->start[offset]; |
| state = term_ptr->state; |
| statemap_ptr = &statemap_start[state]; |
| utarray_push_back(statemap_ptr->nums, &offset); |
| offset += 1; |
| |
| } |
| |
| data->statemap = statemap_start; |
| |
| // Create recursive feature map (if it exists) |
| data->recurIdx = malloc(sizeof(UT_array *) * numstates); |
| // Retrieve the duplicated states |
| offset = 0; |
| while (offset < numstates) { |
| |
| statemap_ptr = &data->statemap[offset]; |
| int length = utarray_len(statemap_ptr->nums); |
| if (length >= 2) { |
| |
| data->recurIdx[data->recurlen] = statemap_ptr->nums; |
| data->recurlen += 1; |
| |
| } |
| |
| offset += 1; |
| |
| } |
| |
| // data->getdupesret = get_dupes(data->orig_walk, &data->recurlen); |
| |
| ck_free(automaton_fn); |
| return 1; |
| |
| } |
| |
| /** |
| * Deinitialize everything |
| * |
| * @param data The data ptr from afl_custom_init |
| */ |
| |
| void afl_custom_deinit(my_mutator_t *data) { |
| |
| free(data->mutator_buf); |
| free(data); |
| |
| } |
| |