diff options
-rw-r--r-- | custom_mutators/examples/README.md | 3 | ||||
-rw-r--r-- | custom_mutators/examples/elf_header_mutator.c | 679 |
2 files changed, 682 insertions, 0 deletions
diff --git a/custom_mutators/examples/README.md b/custom_mutators/examples/README.md index 655f7a5e..112db243 100644 --- a/custom_mutators/examples/README.md +++ b/custom_mutators/examples/README.md @@ -33,3 +33,6 @@ 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. + +elf_header_mutator.c - example ELF header mutator based on + [LibGolf](https://github.com/xcellerator/libgolf/) diff --git a/custom_mutators/examples/elf_header_mutator.c b/custom_mutators/examples/elf_header_mutator.c new file mode 100644 index 00000000..b985257a --- /dev/null +++ b/custom_mutators/examples/elf_header_mutator.c @@ -0,0 +1,679 @@ +/* + AFL++ Custom Mutator for ELF Headers + Written by @echel0n <melih.sahin@protonmail.com> + based on libgolf.h by @xcellerator + $ gcc -O3 -fPIC -shared -o elf_mutator.so -I ~/AFLplusplus/include/ + */ +#include "afl-fuzz.h" +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <linux/elf.h> + +/* EI_ABIVERSION isn't used anymore and elf.h defines EI_PAD to be 0x09 */ +#define EI_ABIVERSION 0x08 +#define EI_PAD 0x09 +/* Define the Architecture and ISA constants to match those in <linux/elf.h> */ +#define X86_64 EM_X86_64 +#define ARM32 EM_ARM +#define AARCH64 EM_AARCH64 +#define uchar unsigned char +#define DATA_SIZE 0x100 + +/* + * The ELF and Program headers are different sizes depending on 32- and 64-bit + * architectures + * taken from libgolf.h + */ +#define EHDR_T(x) Elf##x##_Ehdr +#define PHDR_T(x) Elf##x##_Phdr +#define EHDR(x) ehdr##x +#define PHDR(x) phdr##x +#define GET_EHDR(x) (&(elf_ptr->EHDR(x))); +#define GET_PHDR(x) (&(elf_ptr->PHDR(x))); +#define REF_EHDR(b, x) ((Elf##b##_Ehdr *)ehdr)->x +#define REF_PHDR(b, x) ((Elf##b##_Phdr *)phdr)->x +int ehdr_size; +int phdr_size; +/* + * This struct holds the bytes that will be executed, and the size. + */ +typedef struct text_segment { + + size_t text_size; + unsigned char *text_segment; + +} TextSegment; + +// example shellcode that exits +// taken from libgolf.h +unsigned char buf[] = {0xb0, 0x3c, 0x31, 0xff, 0x0f, 0x05}; + +/* + * This is the raw ELF file + * - EHDR(xx) is the ELF header + * - PHDR(xx) is the program header + * - text is the text segment + * - filename is the name of the golf'd binary + * - isa is the target architecture (X86_64, ARM32, AARCH64) + * taken from libgolf.h + */ +typedef struct rawbinary_t { + + EHDR_T(32) EHDR(32); + PHDR_T(32) PHDR(32); + EHDR_T(64) EHDR(64); + PHDR_T(64) PHDR(64); + TextSegment text; + char *filename; + int isa; + +} RawBinary; + +/* + * Copy an E_IDENT array into the corresponding fields in the ELF header + * Called by populate_ehdr() + * taken from libgolf.h + */ +int populate_e_ident(RawBinary *elf_ptr, unsigned char e_ident[]) { + + int i; + /* Depending on whether the target ISA is 32- or 64-bit, set e_ident */ + switch (elf_ptr->isa) { + + case X86_64: + case AARCH64: + for (i = 0; i < EI_NIDENT; i++) + elf_ptr->EHDR(64).e_ident[i] = e_ident[i]; + break; + case ARM32: + for (i = 0; i < EI_NIDENT; i++) + elf_ptr->EHDR(32).e_ident[i] = e_ident[i]; + break; + default: + exit(1); + + } + + return 0; + +} + +/* + * Copy bytes from buf[] array into text_segment in ELF struct + * taken from libgolf.h + */ +int copy_text_segment(RawBinary *elf_ptr, unsigned char buf[], int text_size) { + + int i; + + /* Set size of text segment and allocate the buffer */ + elf_ptr->text.text_size = text_size; + elf_ptr->text.text_segment = + malloc(elf_ptr->text.text_size * sizeof(unsigned char)); + + /* Copy the bytes into the text segment buffer */ + for (i = 0; i < elf_ptr->text.text_size; i++) { + + elf_ptr->text.text_segment[i] = buf[i]; + + } + +} + +/* + * Populate the ELF Header with sane values + * Returns a pointer to an EHDR struct + * taken from libgolf.h + */ +void *populate_ehdr(RawBinary *elf_ptr) { + + /* + * Set ehdr_size and phdr_size. Determined by whether target ISA is 32- or + * 64-bit. + */ + switch (elf_ptr->isa) { + + case X86_64: + case AARCH64: + ehdr_size = sizeof(EHDR_T(64)); + phdr_size = sizeof(PHDR_T(64)); + break; + case ARM32: + ehdr_size = sizeof(EHDR_T(32)); + phdr_size = sizeof(PHDR_T(32)); + break; + default: + exit(1); + + }; + + /* Start with the E_IDENT area at the top of the file */ + unsigned char e_ident[EI_NIDENT] = {0}; + + /* Magic Bytes */ + e_ident[EI_MAG0] = 0x7F; + e_ident[EI_MAG1] = 0x45; // E + e_ident[EI_MAG2] = 0x4C; // L + e_ident[EI_MAG3] = 0x46; // F + + /* + * EI_CLASS denotes the architecture: + * ELFCLASS32: 0x01 + * ELFCLASS64: 0x02 + */ + switch (elf_ptr->isa) { + + case X86_64: + case AARCH64: + e_ident[EI_CLASS] = ELFCLASS64; + break; + case ARM32: + e_ident[EI_CLASS] = ELFCLASS32; + break; + default: + exit(1); + + } + + /* + * EI_DATA denotes the endianness: + * ELFDATA2LSB: 0x01 + * ELFDATA2MSB: 0x02 + */ + e_ident[EI_DATA] = ELFDATA2LSB; + + /* EI_VERSION is always 0x01 */ + e_ident[EI_VERSION] = EV_CURRENT; + + /* + * EI_OSABI defines the target OS. Ignored by most modern ELF parsers. + */ + e_ident[EI_OSABI] = ELFOSABI_NONE; + + /* EI_ABIVERSION was for sub-classification. Un-defined since Linux 2.6 */ + e_ident[EI_ABIVERSION] = 0x00; + + /* EI_PAD is currently unused */ + e_ident[EI_PAD] = 0x00; + + /* Copy the E_IDENT section to the ELF struct */ + populate_e_ident(elf_ptr, e_ident); + + /* + * The remainder of the ELF header following E_IDENT follows. + * + * ehdr is a pointer to either an Elf32_Edhr, or Elf64_Ehdr struct. + */ + void *ehdr = NULL; + switch (elf_ptr->isa) { + + case X86_64: + case AARCH64: + ehdr = (&(elf_ptr->EHDR(64))); + break; + case ARM32: + ehdr = (&(elf_ptr->EHDR(32))); + break; + default: + exit(1); + + } + + /* + * Depending on whether the ISA is 32- or 64-bit determines the size of + * many of the fields in the ELF Header. This switch case deals with it. + */ + switch (elf_ptr->isa) { + + // 64-Bit ISAs + case X86_64: + case AARCH64: + /* + * e_type specifies what kind of ELF file this is: + * ET_NONE: 0x00 // Unknown Type + * ET_REL: 0x01 // Relocatable + * ET_EXEC: 0x02 // Executable File + * ET_DYN: 0x03 // Shared Object + * ET_CORE: 0x04 // Core Dump + */ + REF_EHDR(64, e_type) = ET_EXEC; // 0x0002 + + /* e_machine specifies the target ISA */ + REF_EHDR(64, e_machine) = elf_ptr->isa; + + /* e_version is always set of 0x01 for the original ELF spec */ + REF_EHDR(64, e_version) = EV_CURRENT; // 0x00000001 + + /* + * e_entry is the memory address of the entry point + * Set by set_entry_point() after p_vaddr is set in the phdr + */ + REF_EHDR(64, e_entry) = 0x0; + + /* + * e_phoff points to the start of the program header, which + * immediately follows the ELF header + */ + REF_EHDR(64, e_phoff) = ehdr_size; + + /* e_shoff points to the start of the section header table */ + REF_EHDR(64, e_shoff) = 0x00; + + /* e_flags is architecture dependent */ + REF_EHDR(64, e_flags) = 0x0; + + /* e_ehsize contains the size of the ELF header */ + REF_EHDR(64, e_ehsize) = ehdr_size; + + /* e_phentsize is the size of the program header */ + REF_EHDR(64, e_phentsize) = phdr_size; + + /* + * e_phnum contains the number of entries in the program header + * e_phnum * e_phentsize = size of program header table + */ + REF_EHDR(64, e_phnum) = 0x1; + + /* e_shentsize contains the size of a section header entry */ + REF_EHDR(64, e_shentsize) = 0x0; + + /* + * e_shnum contains the number of entries in the section header + * e_shnum * e_shentsize = size of section header table + */ + REF_EHDR(64, e_shnum) = 0x0; + + /* + * e_shstrndx contains the index of the section header table that + * contains the section names + */ + REF_EHDR(64, e_shstrndx) = 0x0; + + break; + // 32-Bit ISAs + case ARM32: + /* + * e_type specifies what kind of ELF file this is: + * ET_NONE: 0x00 // Unknown Type + * ET_REL: 0x01 // Relocatable + * ET_EXEC: 0x02 // Executable File + * ET_DYN: 0x03 // Shared Object + * ET_CORE: 0x04 // Core Dump + */ + REF_EHDR(32, e_type) = ET_EXEC; // 0x0002 + + /* e_machine specifies the target ISA */ + REF_EHDR(32, e_machine) = elf_ptr->isa; + + /* e_version is always set of 0x01 for the original ELF spec */ + REF_EHDR(32, e_version) = EV_CURRENT; // 0x00000001 + + /* + * e_entry is the memory address of the entry point + * Set by set_entry_point() after p_vaddr is set in the phdr + */ + REF_EHDR(32, e_entry) = 0x0; + + /* + * e_phoff points to the start of the program header, which + * immediately follows the ELF header + */ + REF_EHDR(32, e_phoff) = ehdr_size; + + /* e_shoff points to the start of the section header table */ + REF_EHDR(32, e_shoff) = 0x0i; + + /* e_flags is architecture dependent */ + REF_EHDR(32, e_flags) = 0x0; + + /* e_ehsize contains the size of the ELF header */ + REF_EHDR(32, e_ehsize) = ehdr_size; + + /* e_phentsize is the size of the program header */ + REF_EHDR(32, e_phentsize) = phdr_size; + + /* + * e_phnum contains the number of entries in the program header + * e_phnum * e_phentsize = size of program header table + */ + REF_EHDR(32, e_phnum) = 0x1; + + /* e_shentsize contains the size of a section header entry */ + REF_EHDR(32, e_shentsize) = 0x0; + + /* + * e_shnum contains the number of entries in the section header + * e_shnum * e_shentsize = size of section header table + */ + REF_EHDR(32, e_shnum) = 0x0; + + /* + * e_shstrndx contains the index of the section header table that + * contains the section names + */ + REF_EHDR(32, e_shnum) = 0x0; + + break; + + } + + return ehdr; + +} + +/* + * Populate the program headers with sane values + * Returns a pointer to a PHDR struct + * taken from libgolf.h + */ +void *populate_phdr(RawBinary *elf_ptr) { + + /* + * All offsets are relative to the start of the program header (0x40) + * + * phdr is a pointer to either an Elf32_Phdr, or Elf64_Phdr struct. + */ + void *phdr = NULL; + switch (elf_ptr->isa) { + + case X86_64: + case AARCH64: + phdr = (&(elf_ptr->PHDR(64))); + break; + case ARM32: + phdr = (&(elf_ptr->PHDR(32))); + break; + default: + exit(1); + + } + + /* + * Depending on whether the ISA is 32- or 64-bit determines the size of + * many of the fields in the Progra Header. This switch case deals with it. + */ + switch (elf_ptr->isa) { + + // 64-Bit ISAs + case X86_64: + case AARCH64: + /* + * p_type identifies what type of segment this is + * PT_NULL: 0x0 // Unused + * PT_LOAD: 0x1 // Loadable Segment + * PT_DYNAMIC: 0x2 // Dynamic Linker Information + * PT_INTERP: 0x3 // Interpreter Information + * PT_NOTE: 0x4 // Auxiliary Information + * PT_SHLIB: 0x5 // Reserved + * PT_PHDR: 0x6 // Segment with Program Header + * PT_TLS: 0x7 // Thread Local Storage + */ + REF_PHDR(64, p_type) = PT_LOAD; // 0x1 + + /* + * p_flags defines permissions for this section + * PF_R: 0x4 // Read + * PF_W: 0x2 // Write + * PF_X: 0x1 // Execute + */ + REF_PHDR(64, p_flags) = PF_R | PF_X; // 0x5 + + /* + * p_offset is the offset in the file image (relative to the start + * of the program header) for this segment. + */ + REF_PHDR(64, p_offset) = 0x0; + + /* + * p_vaddr is the virtual address where this segment should be loaded + * p_paddr is for the physical address (unused by System V) + */ + REF_PHDR(64, p_vaddr) = 0x400000; + REF_PHDR(64, p_paddr) = 0x400000; + + /* + * p_filesz is the size of the segment in the file image + * p_memsz is the size of the segment in memory + * + * Note: p_filesz doesn't have to equal p_memsz + */ + REF_PHDR(64, p_filesz) = elf_ptr->text.text_size; + REF_PHDR(64, p_memsz) = elf_ptr->text.text_size; + + break; + // 32-Bit ISAs + case ARM32: + /* + * p_type identifies what type of segment this is + * PT_NULL: 0x0 // Unused + * PT_LOAD: 0x1 // Loadable Segment + * PT_DYNAMIC: 0x2 // Dynamic Linker Information + * PT_INTERP: 0x3 // Interpreter Information + * PT_NOTE: 0x4 // Auxiliary Information + * PT_SHLIB: 0x5 // Reserved + * PT_PHDR: 0x6 // Segment with Program Header + * PT_TLS: 0x7 // Thread Local Storage + */ + REF_PHDR(32, p_type) = PT_LOAD; // 0x1 + + /* + * p_flags defines permissions for this section + * PF_R: 0x4 // Read + * PF_W: 0x2 // Write + * PF_X: 0x1 // Execute + */ + REF_PHDR(32, p_flags) = PF_R | PF_X; // 0x5 + + /* + * p_offset is the offset in the file image (relative to the start + * of the program header) for this segment. + */ + REF_PHDR(32, p_offset) = 0x0; + + /* + * p_vaddr is the virtual address where this segment should be loaded + * p_paddr is for the physical address (unused by System V) + */ + REF_PHDR(32, p_vaddr) = 0x10000; + REF_PHDR(32, p_paddr) = 0x10000; + + /* + * p_filesz is the size of the segment in the file image + * p_memsz is the size of the segment in memory + * + * Note: p_filesz doesn't have to equal p_memsz + */ + REF_PHDR(32, p_filesz) = elf_ptr->text.text_size; + REF_PHDR(32, p_memsz) = elf_ptr->text.text_size; + + break; + default: + exit(1); + + } + + /* + * p_align is the memory alignment + * + * Note: p_vaddr = p_offset % p_align + */ + switch (elf_ptr->isa) { + + case X86_64: + REF_PHDR(64, p_align) = 0x400000; + break; + case ARM32: + REF_PHDR(32, p_align) = 0x10000; + break; + case AARCH64: + REF_PHDR(64, p_align) = 0x400000; + break; + + } + + return phdr; + +} + +/* + * e_entry depends on p_vaddr, so has to be set after populate_ehdr() + * and populate_phdr() have been called. + * taken from libgolf.h + */ +int set_entry_point(RawBinary *elf_ptr) { + + /* + * Once the whole ELF file is copied into memory, control is handed to + * e_entry. Relative to the process's virtual memory address, the .text + * segment will be located immediately after the ELF and program header. + * + * ehdr and phdr are pointers to the ELF and Program headers respectively. + * The switch case casts and assigns them to the correct fields of the ELF + * struct, then sets ehdr->e_entry. + */ + void *ehdr, *phdr; + + switch (elf_ptr->isa) { + + case X86_64: + case AARCH64: + ehdr = GET_EHDR(64); + phdr = GET_PHDR(64); + REF_EHDR(64, e_entry) = REF_PHDR(64, p_vaddr) + ehdr_size + phdr_size; + break; + case ARM32: + ehdr = GET_EHDR(32); + phdr = GET_PHDR(32); + REF_EHDR(32, e_entry) = REF_PHDR(32, p_vaddr) + ehdr_size + phdr_size; + break; + default: + exit(1); + + } + + return 0; + +} + +typedef struct my_mutator { + + afl_state_t *afl; + size_t trim_size_current; + int trimmming_steps; + int cur_step; + u8 *mutated_out, *post_process_buf, *trim_buf; + +} my_mutator_t; + +my_mutator_t *afl_custom_init(afl_state_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; + + } + + if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("afl_custom_init malloc"); + return NULL; + + } + + if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("afl_custom_init malloc"); + return NULL; + + } + + if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("afl_custom_init malloc"); + return NULL; + + } + + data->afl = afl; + return data; + +} + +size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *in_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) { + + RawBinary elf_obj; + RawBinary *elf = &elf_obj; + elf->isa = 62; + Elf64_Ehdr *ehdr; + Elf64_Phdr *phdr; + copy_text_segment(elf, buf, sizeof(buf)); + ehdr = populate_ehdr(elf); + phdr = populate_phdr(elf); + set_entry_point(elf); + + size_t mutated_size = ehdr_size + phdr_size + elf->text.text_size; + int pos = 0; + // example fields + ehdr->e_ident[EI_CLASS] = (uint8_t *)(in_buf + pos++); + ehdr->e_ident[EI_DATA] = (uint8_t *)(in_buf + pos++); + ehdr->e_ident[EI_VERSION] = (uint8_t *)(in_buf + pos++); + ehdr->e_ident[EI_OSABI] = (uint8_t *)(in_buf + pos++); + for (int i = 0x8; i < 0x10; ++i) { + + (ehdr->e_ident)[i] = (uint8_t *)(in_buf + pos++); + + } + + ehdr->e_version = (uint32_t *)(in_buf + pos); + pos += 4; + // sections headers + ehdr->e_shoff = (uint64_t *)(in_buf + pos); + pos += 8; + ehdr->e_shentsize = (uint16_t *)(in_buf + pos); + pos += 2; + ehdr->e_shnum = (uint16_t *)(in_buf + pos); + pos += 2; + ehdr->e_shstrndx = (uint16_t *)(in_buf + pos); + pos += 2; + ehdr->e_flags = (uint32_t *)(in_buf + pos); + pos += 4; + // physical addr + phdr->p_paddr = (uint64_t *)(in_buf + pos); + pos += 8; + phdr->p_align = (uint64_t *)(in_buf + pos); + pos += 8; + + /* mimic GEN_ELF() + * Write: + * - ELF Header + * - Program Header + * - Text Segment + */ + memcpy(data->mutated_out, ehdr, ehdr_size); + memcpy(data->mutated_out + ehdr_size, phdr, phdr_size); + memcpy(data->mutated_out + ehdr_size + phdr_size, elf->text.text_segment, + elf->text.text_size); + + *out_buf = data->mutated_out; + return mutated_size; + +} + +void afl_custom_deinit(my_mutator_t *data) { + + free(data->post_process_buf); + free(data->mutated_out); + free(data->trim_buf); + free(data); + +} + |