int rdis_functions_bounds (struct _rdis * rdis) { struct _map_it * it; int result = 0; for (it = map_iterator(rdis->functions); it != NULL; it = map_it_next(it)) { struct _function * function = map_it_data(it); result |= rdis_function_bounds(rdis, function->address); } return result; }
void rdis_callback (struct _rdis * rdis, int type_mask) { struct _map_it * it; for (it = map_iterator(rdis->callbacks); it != NULL; it = map_it_next(it)) { struct _rdis_callback * rc = map_it_data(it); if ((rc->type_mask & type_mask) == 0) continue; fflush(stdout); rc->callback(rc->data); } }
struct _map * elf32_labels_functions (struct _elf32 * elf32, struct _map * memory, struct _map * functions) { struct _map * labels_map = map_create(); struct _map_it * it; for (it = map_iterator(functions); it != NULL; it = map_it_next(it)) { struct _function * function = map_it_data(it); struct _label * label = elf32_label_address(elf32, memory, function->address); map_insert(labels_map, function->address, label); object_delete(label); } return labels_map; }
struct _graph * elf32_graph_functions (struct _elf32 * elf32, struct _map * memory, struct _map * functions) { struct _graph * graph; struct _wqueue * wqueue; // disassemble from entry point graph = x86_graph(elf32_entry(elf32), memory); struct _map_it * it; wqueue = wqueue_create(); for (it = map_iterator(functions); it != NULL; it = map_it_next(it)) { struct _function * function = map_it_data(it); struct _x86_wqueue * x86w; x86w = x86_wqueue_create(function->address, memory); wqueue_push(wqueue, WQUEUE_CALLBACK(x86_graph_wqueue), x86w); object_delete(x86w); } wqueue_wait(wqueue); while (wqueue_peek(wqueue) != NULL) { graph_merge(graph, wqueue_peek(wqueue)); wqueue_pop(wqueue); } object_delete(wqueue); remove_function_predecessors(graph, functions); graph_reduce(graph); return graph; }
struct _map * elf32_functions_wqueue (struct _elf32 * elf32, struct _map * memory, struct _list * entries) { struct _map * functions = map_create(); struct _wqueue * wqueue = wqueue_create(); struct _list_it * it; for (it = list_iterator(entries); it != NULL; it = it->next) { struct _function * function = it->data; if (map_fetch(functions, function->address) == NULL) map_insert(functions, function->address, function); struct _x86_wqueue * x86w; x86w = x86_wqueue_create(function->address, memory); wqueue_push(wqueue, WQUEUE_CALLBACK(x86_functions_wqueue), x86w); object_delete(x86w); } wqueue_wait(wqueue); struct _map * fmap; while ((fmap = wqueue_peek(wqueue)) != NULL) { struct _map_it * mit; for (mit = map_iterator(fmap); mit != NULL; mit = map_it_next(mit)) { struct _function * function = map_it_data(mit); if (map_fetch(functions, function->address) == NULL) map_insert(functions, function->address, function); } wqueue_pop(wqueue); } object_delete(wqueue); return functions; }
int rdis_update_memory (struct _rdis * rdis, uint64_t address, struct _buffer * buffer) { if (buffer == NULL) return -1; mem_map_set (rdis->memory, address, buffer); // we will regraph functions whose bounds fall within this updated memory, // and all functions whose bounds fall within the bounds of functions to be // regraphed (step 2 simplifies things later on) struct _queue * queue = queue_create(); struct _map_it * it; for (it = map_iterator(rdis->functions); it != NULL; it = map_it_next(it)) { struct _function * function = map_it_data(it); if ( ( (function->bounds.lower >= address) && (function->bounds.lower < address + buffer->size)) || ( (function->bounds.upper >= address) && (function->bounds.upper < address + buffer->size)) || ( (function->bounds.lower <= address) && (function->bounds.upper >= address + buffer->size))) { queue_push(queue, function); } } struct _map * regraph_functions = map_create(); while (queue->size > 0) { struct _function * function = queue_peek(queue); if (map_fetch(regraph_functions, function->address) != NULL) { queue_pop(queue); continue; } printf("adding regraph function %llx\n", (unsigned long long) function->address); map_insert(regraph_functions, function->address, function); for (it = map_iterator(rdis->functions); it != NULL; it = map_it_next(it)) { struct _function * cmp_function = map_it_data(it); if ( ( (cmp_function->bounds.lower >= function->bounds.lower) && (cmp_function->bounds.lower < function->bounds.upper)) || ( (cmp_function->bounds.upper >= function->bounds.lower) && (cmp_function->bounds.upper < function->bounds.upper)) || ( (cmp_function->bounds.lower <= function->bounds.lower) && (cmp_function->bounds.upper >= function->bounds.upper))) queue_push(queue, cmp_function); } } // regraph dem functions struct _graph * new_graph; new_graph = loader_graph_functions(rdis->loader, rdis->memory, regraph_functions); // We are now going to go through all nodes in our regraph functions. We // will copy over comments to the new instructions and then remove the // regraph function nodes from the original graph for (it = map_iterator(regraph_functions); it != NULL; it = map_it_next(it)) { struct _function * function = map_it_data(it); struct _graph * family = graph_family(rdis->graph, function->address); if (family == NULL) continue; struct _graph_it * git; for (git = graph_iterator(family); git != NULL; git = graph_it_next(git)) { struct _list * ins_list = graph_it_data(git); struct _list_it * iit; for (iit = list_iterator(ins_list); iit != NULL; iit = iit->next) { struct _ins * ins = iit->data; if (ins->comment == NULL) continue; struct _ins * new_ins = graph_fetch_ins(new_graph, ins->address); if (ins->size != new_ins->size) continue; if (memcmp(ins->bytes, new_ins->bytes, ins->size) == 0) { printf("copy over comment from instruction at %llx\n", (unsigned long long) ins->address); ins_s_comment(new_ins, ins->comment); } } // add node for deletion struct _index * index = index_create(graph_it_index(git)); queue_push(queue, index); object_delete(index); } object_delete(family); while (queue->size > 0) { struct _index * index = queue_peek(queue); graph_remove_node(rdis->graph, index->index); queue_pop(queue); } } // merge the new graph with the old graph graph_merge(rdis->graph, new_graph); // reset bounds of these functions for (it = map_iterator(regraph_functions); it != NULL; it = map_it_next(it)) { struct _function * function = map_it_data(it); rdis_function_bounds(rdis, function->address); } objects_delete(queue, new_graph, regraph_functions, NULL); rdis_callback(rdis, RDIS_CALLBACK_ALL); return 0; }
int rdis_user_function (struct _rdis * rdis, uint64_t address) { // get a tree of all functions reachable at this address struct _map * functions = loader_function_address(rdis->loader, rdis->memory, address); // add in this address as a new function as well struct _function * function = function_create(address); map_insert(functions, function->address, function); object_delete(function); // for each newly reachable function struct _map_it * mit; for (mit = map_iterator(functions); mit != NULL; mit = map_it_next(mit)) { struct _function * function = map_it_data(mit); uint64_t fitaddress = function->address; // if we already have this function, skip it if (map_fetch(rdis->functions, function->address) != NULL) continue; // add this function to the rdis->function_tree map_insert(rdis->functions, function->address, function); // add label struct _label * label = loader_label_address(rdis->loader, rdis->memory, fitaddress); map_insert(rdis->labels, fitaddress, label); object_delete(label); // if this function is already in our graph, all we need to do is make // sure its a separate node and then remove function predecessors struct _graph_node * node = graph_fetch_node_max(rdis->graph, fitaddress); if (node != NULL) { // already a node, remove function predecessors if (node->index == address) { remove_function_predecessors(rdis->graph, rdis->functions); continue; } // search for instruction with given address struct _list * ins_list = node->data; struct _list_it * it; for (it = list_iterator(ins_list); it != NULL; it = it->next) { struct _ins * ins = it->data; // found instruction if (ins->address == fitaddress) { // create a new instruction list. // add remaining instructions to new list while removing them from // the current list struct _list * new_ins_list = list_create(); while (1) { list_append(new_ins_list, it->data); it = list_remove(ins_list, it); if (it == NULL) break; } // create a new graph node for this new function graph_add_node(rdis->graph, fitaddress, new_ins_list); // all graph successors from old node are added to new node struct _queue * queue = queue_create(); struct _list * successors = graph_node_successors(node); struct _list_it * sit; for (sit = list_iterator(successors); sit != NULL; sit = sit->next) { struct _graph_edge * edge = sit->data; graph_add_edge(rdis->graph, fitaddress, edge->tail, edge->data); queue_push(queue, edge); } object_delete(successors); // and removed from old node while (queue->size > 0) { struct _graph_edge * edge = queue_peek(queue); graph_remove_edge(rdis->graph, edge->head, edge->tail); queue_pop(queue); } object_delete(queue); // that was easy break; } } if (it != NULL) continue; } // we need to create a new graph for this node struct _graph * graph = loader_graph_address(rdis->loader, rdis->memory, fitaddress); graph_merge(rdis->graph, graph); object_delete(graph); } object_delete(functions); rdis_callback(rdis, RDIS_CALLBACK_ALL); return 0; }
struct _map * elf32_functions (struct _elf32 * elf32, struct _map * memory) { struct _list * entries = list_create(); // add the entry point struct _function * function = function_create(elf32_entry(elf32)); list_append(entries, function); object_delete(function); // check for __libc_start_main loader uint64_t target_offset = elf32_entry(elf32) - elf32_base_address(elf32) + 0x17; if (target_offset + 0x10 < elf32->data_size) { uint8_t * data = &(elf32->data[target_offset]); size_t size = elf32->data_size - target_offset; ud_t ud_obj; ud_init (&ud_obj); ud_set_mode (&ud_obj, 32); ud_set_syntax(&ud_obj, UD_SYN_INTEL); ud_set_input_buffer(&ud_obj, data, size); ud_disassemble(&ud_obj); if (ud_obj.mnemonic == UD_Ipush) { printf("found __libc_start_main loader, main at %llx\n", (unsigned long long) udis86_sign_extend_lval(&(ud_obj.operand[0]))); // add main to function tree struct _function * function; function = function_create(udis86_sign_extend_lval(&(ud_obj.operand[0]))); list_append(entries, function); object_delete(function); } else printf("disassembled: %s\n disassembled at %llx\n", ud_insn_asm(&ud_obj), (unsigned long long) target_offset); } struct _map * functions = elf32_functions_wqueue(elf32, memory, entries); // these are the reachable functions struct _map_it * mit; for (mit = map_iterator(functions); mit != NULL; mit = map_it_next(mit)) { struct _function * function = map_it_data(mit); function->flags |= FUNCTION_REACHABLE; } // reset entries object_delete(entries); entries = list_create(); // symbols are easy int sec_i; for (sec_i = 0; sec_i < elf32->ehdr->e_shnum; sec_i++) { Elf32_Shdr * shdr = elf32_shdr(elf32, sec_i); if (shdr == NULL) break; if ((shdr->sh_type != SHT_SYMTAB) && (shdr->sh_type != SHT_DYNSYM)) continue; int sym_i; for (sym_i = 0; sym_i < shdr->sh_size / shdr->sh_entsize; sym_i++) { Elf32_Sym * sym = elf32_section_element(elf32, sec_i, sym_i); if (sym == NULL) break; if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC) continue; if (sym->st_value == 0) continue; struct _function * function = function_create(sym->st_value); list_append(entries, function); object_delete(function); } } struct _map * sym_functions = elf32_functions_wqueue(elf32, memory, entries); for (mit = map_iterator(sym_functions); mit != NULL; mit = map_it_next(mit)) { struct _function * function = map_it_data(mit); if (map_fetch(functions, function->address) == NULL) map_insert(functions, function->address, function); } object_delete(sym_functions); object_delete(entries); return functions; }
struct _graph * recursive_disassemble (const struct _map * mem_map, uint64_t entry, struct _ins * (* ins_callback) (const struct _map *, uint64_t)) { struct _queue * queue = queue_create(); struct _map * map = map_create(); struct _index * index = index_create(entry); queue_push(queue, index); object_delete(index); while (queue->size > 0) { struct _index * index = queue_peek(queue); if (map_fetch(map, index->index)) { queue_pop(queue); continue; } struct _ins * ins = ins_callback(mem_map, index->index); if (ins == NULL) { queue_pop(queue); continue; } map_insert(map, index->index, ins); struct _list_it * lit; for (lit = list_iterator(ins->successors); lit != NULL; lit = lit->next) { struct _ins_value * successor = lit->data; if (successor->type == INS_SUC_CALL) continue; struct _index * index = index_create(successor->address); queue_push(queue, index); object_delete(index); } queue_pop(queue); } object_delete(queue); // create graph nodes struct _graph * graph = graph_create(); struct _map_it * mit; for (mit = map_iterator(map); mit != NULL; mit = map_it_next(mit)) { graph_add_node(graph, map_it_key(mit), map_it_data(mit)); } // create graph edges for (mit = map_iterator(map); mit != NULL; mit = map_it_next(mit)) { struct _ins * ins = map_it_data(mit); struct _list_it * lit; for (lit = list_iterator(ins->successors); lit != NULL; lit = lit->next) { struct _ins_value * successor = lit->data; // don't add call edges if (successor->type == INS_SUC_CALL) continue; graph_add_edge(graph, ins->address, successor->address, successor); } } object_delete(map); return graph; }