void test(void) { gadget_t gadgets; gadget_init(&gadgets); gadget_add(&gadgets, (unsigned char*)TEST_INSTRS, TEST_INSTRS_LEN, 0); gadget_print(&gadgets); gadget_destroy(&gadgets); }
// Work backwards from an instruction to find the instructions that come before it. // gadgets - the gadget_t for the instruction that comes next. // buffer - the buffer of program code. // offset - the virtual memory address that buffer starts at. // position - the index into buffer of the previously-located instruction. // count - the number of instructions to find (recursively). void work_backwards(gadget_t *gadgets, unsigned char *buffer, uint32_t offset, int position, int count) { if (count <= 0) { return; } int instr_start; int instr_size, instr_actual_size; x86_insn_t instr; // Work back from the known instruction one byte at a time. for (instr_size = 1; instr_size <= MAX_INSTRUCTION_BYTES; instr_size++) { // Disassemble the bytes between our position and the first byte of the // known instruction. instr_start = position - instr_size; if (instr_start < 0) break; instr_actual_size = x86_disasm(buffer + instr_start, instr_size, 0, 0, &instr); /* * If *all* of those bytes make up one instruction, and it's not * a return (gadgets should not contain a RET other than at the end), * add it to the tree. */ if (instr_actual_size == instr_size && !(instr_size == 1 && *(buffer + instr_start) == RET)) { // If we've already seen this sequence of instructions... gadget_t *g; if ((g = gadget_list_find_instr(&gadgets->previous, buffer + instr_start, instr_actual_size)) != NULL) { // ...see if the address of this one is better. if (has_zero_byte(g->virtual_address)) { g->virtual_address = offset + instr_start; } continue; } // Otherwise, add it to the list. gadget_t *newgadget = malloc(sizeof(gadget_t)); gadget_init(newgadget); memcpy(newgadget->instr, buffer + instr_start, instr_actual_size); newgadget->instr_len = instr_size; newgadget->virtual_address = offset + instr_start; // The next instruction (in execution order) is the one this one // comes before. newgadget->next = gadgets; // The instruction we just found is previous (in execution order) to // the one we already know about. gadget_list_add(&gadgets->previous, newgadget); work_backwards(newgadget, buffer, offset, instr_start, count - 1); } } }
int main(int argc, char **argv) { if (argc != 2) { print_usage(); return 1; } if (strcmp(argv[1], "demo") == 0) { test(); return 0; } /* Code below adapted from 'libelf by Example' */ Elf *e; GElf_Phdr phdr; int fd; size_t n, i; if (elf_version(EV_CURRENT) == EV_NONE) { printf("Error with elf library [%s]\n", elf_errmsg(-1)); exit(1); } if ((fd = open(argv[1], O_RDONLY, 0)) < 0) { printf("Error reading file.\n"); exit(1); } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { printf("elf_begin() failed [%s]\n", elf_errmsg(-1)); exit(1); } if (elf_kind(e) != ELF_K_ELF) { printf("File is not an ELF object.\n"); exit(1); } if (elf_getphdrnum(e, &n) != 0) { printf("elf_getphdrnum() failed [%s]\n", elf_errmsg(-1)); exit(1); } /* Initialize libdis */ x86_init(opt_none, NULL, NULL); gadget_t gadgets; gadget_init(&gadgets); /* Loop over each program header (segment) */ for (i = 0; i < n; i++) { if (gelf_getphdr(e, i, &phdr) != &phdr) { printf("gelf_getphdr() failed [%s]\n", elf_errmsg(-1)); exit(1); } /* If the segment is loaded into memory AND is executable */ if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) { /* Load the segment from the ELF file */ unsigned char *buffer = malloc(phdr.p_filesz); lseek(fd, phdr.p_offset, SEEK_SET); ssize_t count = read(fd, buffer, phdr.p_filesz); if (count != phdr.p_filesz) { printf("read sucks.\n"); // FIXME: EINTR etc. exit(1); } /* Add all gadgets in the segment to our gadget tree */ gadget_add(&gadgets, buffer, phdr.p_filesz, phdr.p_vaddr); free(buffer); } } elf_end(e); close(fd); gadget_print(&gadgets); gadget_destroy(&gadgets); return 0; }
// find valid instructions offsets before ret int _ropit_x86_find_gadgets (uint8_t *bytes, int len, int64_t *rets, int n_rets) { int sz_inst; /* size of instruction */ x86_insn_t insn; /* instruction */ int idx_ret, sz_ret; int valid_gadget; // back track instruction count int n_backtrack_inst, n_backtrack_bytes; // start for rop search uint8_t *start, *gadget_start; // disassemble int len_disasm; int sz_dst; char disassembled[DISASSEMBLED_SIZE_MAX] = {0}; // cache FILE *fp_cache; struct cache_t *caches; int idx_caches, n_caches; struct gadget_t *gadgets; int idx_gadgets, n_gadgets; // cache queue struct gadget_cache_queue_t *cache_queue; // count int count_gadgets; // check params if (!bytes || len <= 0) { debug_printf (MESSAGE_ERROR, stderr, "error: _ropit_x86_find_gadgets(): Bytes null or len <= 0\n"); return NULL; } // search rets if (!rets || n_rets <= 0) { debug_printf (MESSAGE_ERROR, stderr, "error: _ropit_x86_find_gadgets(): No rets\n"); return NULL; } // init gadget_cache fp_cache = fopen("tmp/gadget_cache", "w"); if (!fp_cache) { debug_printf (MESSAGE_ERROR, stderr, "error: _ropit_x86_find_gadgets(): Failed open (w)\n"); return NULL; } // init cache_queue cache_queue = NULL; if (!gadget_cache_queue_init(&cache_queue)) { debug_printf (MESSAGE_ERROR, stderr, "error: _ropit_x86_find_gadgets(): Cache queue allocation failed\n"); return NULL; } gadget_cache_queue_set_file (cache_queue, fp_cache); // init gadgets n_gadgets = 1024; gadgets = calloc(sizeof(struct gadget_t), n_gadgets); if (!gadgets) { debug_printf (MESSAGE_ERROR, stderr, "error: _ropit_x86_find_gadgets(): Failed allocating caches\n"); return NULL; } for (idx_gadgets = 0; idx_gadgets < n_gadgets; idx_gadgets++) gadget_init(&(gadgets[idx_gadgets]), DISASSEMBLED_SIZE_MAX); // init caches n_caches = 1; caches = calloc(sizeof(struct cache_t), n_caches); if (!caches) { debug_printf (MESSAGE_ERROR, stderr, "error: _ropit_x86_find_gadgets(): Failed allocating caches\n"); return NULL; } for (idx_caches = 0; idx_caches < n_caches; idx_caches++) { cache_init(&(caches[idx_caches]), 1024); } // init disasm x86_init(opt_none, NULL, NULL); idx_caches = 0; idx_gadgets = 0; count_gadgets = 0; for (idx_ret = 0; idx_ret < n_rets; idx_ret++) { start = bytes + rets[idx_ret]; n_backtrack_inst = 0; n_backtrack_bytes = 0; while ( bytes <= start && start <= bytes + len ) { /* disassemble address */ sz_inst = x86_disasm(start, len - (start - bytes), 0, 0, &insn); x86_oplist_free(&insn); if (sz_inst <= 0) { // printf("not found inst\n"); n_backtrack_bytes++; } else { // printf("found inst\n"); n_backtrack_bytes = 0; valid_gadget = 0; // gadget_start = start; if (gadget_start == bytes + rets[idx_ret]) valid_gadget = 1; else { n_backtrack_inst = 0; // check gadget validity while (bytes <= gadget_start && gadget_start <= bytes + rets[idx_ret]) { /* disassemble address */ sz_inst = x86_disasm(gadget_start, gadget_start - bytes, 0, 0, &insn); x86_oplist_free(&insn); if (sz_inst <= 0) break; else { n_backtrack_inst++; gadget_start += sz_inst; if (gadget_start == bytes + rets[idx_ret]) { valid_gadget = 1; break; } } } } if (valid_gadget == 1) { // ++count_gadgets; // get ret size sz_ret = x86_disasm(bytes + rets[idx_ret], 10, 0, 0, &insn); x86_oplist_free(&insn); // fill gadget structure gadgets[idx_gadgets].ret_addr = rets[idx_ret]; gadgets[idx_gadgets].ret_bytes = rets[idx_ret] + bytes; gadgets[idx_gadgets].address = start - bytes; gadgets[idx_gadgets].len_bytes = (rets[idx_ret] - (start - bytes)) + sz_ret; if (gadgets[idx_gadgets].sz_bytes < gadgets[idx_gadgets].len_bytes) { gadgets[idx_gadgets].bytes = realloc (gadgets[idx_gadgets].bytes, gadgets[idx_gadgets].len_bytes); gadgets[idx_gadgets].sz_bytes = gadgets[idx_gadgets].len_bytes; } memcpy(gadgets[idx_gadgets].bytes, start, gadgets[idx_gadgets].len_bytes); if (cache_add (&(caches[idx_caches]), &(gadgets[idx_gadgets])) == -ERR_CACHE_FULL) { gadget_cache_queue_add (cache_queue, &(caches[idx_caches])); gadget_cache_queue_fwrite_worker (cache_queue); cache_reset (&(caches[idx_caches])); } idx_gadgets = (idx_gadgets + 1) % n_gadgets; } } --start; // maximum intel instruction size is 15 // maximum instructions in a gadget is hardcoded to 8 here /* TODO : Get more gadgets with n_backtrack_inst instructions * Effectively, we stop at the first gadget which has * n_backtrack_inst instructions while there might be multiple * possibilities. */ if (n_backtrack_bytes >= 15 || n_backtrack_inst == 8) break; } } x86_cleanup(); // write remaining gadgets gadget_cache_queue_add (cache_queue, &(caches[idx_caches])); gadget_cache_queue_fwrite_worker (cache_queue); cache_reset (&(caches[idx_caches])); // clean up for (idx_caches = 0; idx_caches < n_caches; idx_caches++) free(caches[idx_caches].objects); free(caches); for (idx_gadgets = 0; idx_gadgets < n_gadgets; idx_gadgets++) { // gadget_free(&(gadgets[idx_gadgets])); // free (gadgets[idx_gadgets].repr); free (gadgets[idx_gadgets].bytes); } free(gadgets); gadget_cache_queue_destroy (&cache_queue); fclose (fp_cache); return count_gadgets; }