// This simple example just creates random buffer <= 100 filled with 'A' // needs -I /path/to/AFLplusplus/include //#include "custom_mutator_helpers.h" #include #include #include #include #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); }