/** * @brief Shift relocation tables at some point for allowing non-present symbol resolving * mostly applied on section injection for ET_DYN objects. * @param file The host file where shift must happens. * @param diff Absolute difference to shift to. * @param relplt Section descriptor for the .relplt section. * @param limit Upper limit address beyond which shifting must not be performed (or ELFSH_NOLIMIT) * @return Always 0. */ int elfsh_shift_ia32_relocs(elfshobj_t *file, eresi_Addr diff, elfshsect_t *relplt, eresi_Addr limit) { elfsh_Rel *l; int index; eresi_Addr reloff; elfshsect_t *parent; elfsh_SAddr off; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); for (index = 0; index < relplt->shdr->sh_size / sizeof(elfsh_Rel); index++) { l = (elfsh_Rel *) relplt->data + index; if (ELFSH_NOLIMIT == limit || elfsh_get_reloffset((elfsh_Rel *) l) >= limit) { reloff = elfsh_get_reloffset((elfsh_Rel *) l); reloff += diff; elfsh_set_reloffset((elfsh_Rel *) l, reloff); /* Shifting memory -pointed- by the relative relocation */ if (elfsh_get_reltype(l) != R_386_RELATIVE) continue; parent = elfsh_get_parent_section(file, reloff, &off); if (strstr(parent->name, "got") || strstr(parent->name, "bss") || strstr(parent->name, "elfsh")) continue; *(eresi_Addr *) ((char *) parent->data + off) += diff; } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Remap section's whoose type is a data array (GOT, CTORS, DTORS ..) * @param file * @param array * @param size * @param diff * @return */ int elfsh_reloc_array(elfshobj_t *file, eresi_Addr *array, u_int size, eresi_Addr diff) { elfshsect_t *parent; u_int index; u_int count; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (file == NULL || array == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL paramater", -1); for (count = index = 0; index < size; index++) { parent = elfsh_get_parent_section(file, array[index], NULL); if (parent != NULL && parent->shdr->sh_addr != NULL && array[index] != NULL) { array[index] += diff; count++; } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (count)); }
/** * Get the parent object of a breakpoint. * Thats needed for the mprotect stuff inside the breakpoint handler * @param addr * @return */ elfshobj_t *e2dbg_get_parent_object(eresi_Addr addr) { elfsh_Phdr *cur; elfshobj_t *curfile; elfshsect_t *cursect; char **keys; int index; int keynbr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); keys = hash_get_keys(&world.curjob->loaded, &keynbr); for (index = 0; index < keynbr; index++) { curfile = hash_get(&world.curjob->loaded, keys[index]); cursect = elfsh_get_parent_section(curfile, addr, NULL); if (cursect) { cur = elfsh_get_parent_segment(curfile, cursect); if (cur) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, curfile); } } /* Parent object not found */ PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get parent object addr", (NULL)); }
/** * Remap the .dynamic section * @param sect * @param diff * @return */ int elfsh_reloc_dynamic(elfshsect_t *sect, eresi_Addr diff) { elfshsect_t *parent; elfsh_Dyn *dyn; u_int index; u_int count; eresi_Addr val; u_int nbr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (sect == NULL || sect->shdr == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); else if (sect->shdr->sh_type != SHT_DYNAMIC) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unexpected section type", -1); nbr = sect->shdr->sh_size / sizeof(elfsh_Dyn); for (dyn = elfsh_readmem(sect), count = index = 0; index < nbr; index++) { val = elfsh_get_dynentry_val(dyn + index); parent = elfsh_get_parent_section(sect->parent, val, NULL); if (val && parent != NULL && parent->shdr->sh_addr != NULL) { elfsh_set_dynentry_val(dyn + index, val + diff); count++; } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (count)); }
/** * Remap sections of type SHT_REL and SHT_RELA * @param sect * @param diff * @return */ int elfsh_reloc_rel(elfshsect_t *sect, eresi_Addr diff) { elfshsect_t *parent; elfsh_Rel *rel; u_int index; u_int count; u_int nbr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (sect == NULL || sect->shdr == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); else if (sect->shdr->sh_type != SHT_REL && sect->shdr->sh_type != SHT_RELA) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unexpected section type", -1); nbr = sect->shdr->sh_size / sizeof(elfsh_Rel); for (rel = elfsh_readmem(sect), count = index = 0; index < nbr; index++) { parent = elfsh_get_parent_section(sect->parent, rel[index].r_offset, NULL); if (rel[index].r_offset && parent != NULL && parent->shdr->sh_addr != NULL) { rel[index].r_offset += diff; count++; } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (count)); }
/** * Retreive the file offset giving the virtual address * @param file * @param sym * @return */ int elfsh_get_symbol_foffset(elfshobj_t *file, elfsh_Sym *sym) { elfshsect_t *sect; char *name; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* If the symbol is a section, then look at the sht instead */ if (elfsh_get_symbol_type(sym) == STT_SECTION) { name = elfsh_get_symbol_name(file, sym); sect = elfsh_get_section_by_name(file, name, NULL, NULL, NULL); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (sect ? elfsh_get_section_foffset(sect->shdr) : 0)); } /* get our parent section and compute the file offset */ if (sym == NULL || file == NULL || NULL == sym->st_value) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); sect = elfsh_get_parent_section(file, sym->st_value, NULL); if (sect == NULL) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (sect->shdr->sh_offset + (sym->st_value - sect->shdr->sh_addr))); }
/** * @brief Get the buffered address from the real virtual address */ void *elfsh_get_raw_by_addr(elfshobj_t *current, eresi_Addr addr, void *buf, u_int size) { elfshsect_t *sect; elfsh_SAddr offset; /* This happens when the object is a ERESI variable or when we request an address in runtime that is not part of any section */ sect = elfsh_get_parent_section(current, (eresi_Addr) addr, &offset); if (!sect) return ((void *) addr); /* In debug mode, we return a pointer on the runtime data */ if (elfsh_is_runtime_mode()) { if (!elfsh_section_is_runtime(sect)) return ((void *) sect->parent->rhdr.base + sect->shdr->sh_addr + offset); else if (!sect->shdr->sh_addr) return ((void *) sect->data + offset); else return ((void *) sect->shdr->sh_addr + offset); } /* Else we return a pointer on the cache data, doing a copy if requested */ else { if (buf && size) memcpy(buf, (char *) sect->data + offset, size); return ((void *) sect->data + offset); } }
/** * Called from elfsh_fixup_symtab * When trying to inject part of the libc, some bss symbols have a wrong sctndx * @param symtab * @return */ elfshsect_t *elfsh_fixup_sctndx(elfshsect_t *symtab) { int index; elfsh_Sym *sym; elfsh_SAddr offset; elfsh_Shdr *shdr; elfshsect_t *sct; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); //return (symtab); // XXX sym = symtab->data; shdr = symtab->parent->sht + symtab->index; for (index = 0; index < shdr->sh_size / sizeof(elfsh_Sym); index++) { if (elfsh_get_symbol_link(sym + index) != SHN_COMMON) { if (elfsh_get_symbol_type(sym + index) == STT_SECTION) continue; sct = elfsh_get_parent_section(symtab->parent, elfsh_get_symbol_value(sym + index), &offset); if (sct == NULL) { sct = elfsh_get_section_by_index(symtab->parent, elfsh_get_symbol_link(sym + index), NULL, NULL); if (sct && elfsh_get_section_type(sct->shdr) == SHT_NOBITS) { #if __DEBUG_MAP__ printf(" [*] Symbol [%s] sctndx changed from %u to SHN_COMMON\n", elfsh_get_symbol_name(symtab->parent, sym + index), elfsh_get_symbol_link(sym + index)); #endif elfsh_set_symbol_link(sym + index, SHN_COMMON); continue; } } if (sct && elfsh_get_section_type(sct->shdr) == SHT_NOBITS) { elfsh_set_symbol_link(sym + index, SHN_COMMON); #if __DEBUG_MAP__ printf(" [*] Symbol [%s] sctndx changed to SHN_COMMON\n", elfsh_get_symbol_name(symtab->parent, sym + index)); #endif } } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, symtab); }
/** * Symbol resolving handler for libasm. * Runtime compatible * @param data * @param vaddr * @param buf * @param maxlen */ void asm_do_resolve(void *data, eresi_Addr vaddr, char *buf, u_int maxlen) { elfshobj_t *file; elfshsect_t *parent; char *name; elfsh_SAddr off; int len; char *sep; uint32_t addr; /* Retreive the nearest symbol */ file = data; parent = elfsh_get_parent_section(file, vaddr, 0); if (NULL != parent && parent->shdr->sh_addr) name = revm_resolve(file, vaddr, &off); else name = NULL; /* Print the symbol name in 'buf' so that libasm can print it */ len = (NULL == name ? 10 : strlen(name) + 25); if (name != NULL && *name != 0x00) { sep = (off > 0 ? " + " : off < 0 ? " - " : ""); len = snprintf(buf, maxlen - 1, "%s%s%s%s", revm_colorfieldstr("<"), revm_colortypestr(name), (off ? revm_colorfieldstr(sep) : ""), (off ? "" : revm_colorfieldstr(">"))); if (off) snprintf(buf + len, maxlen - len - 1, "%s%s", revm_colornumber("%u", (u_int) off), revm_colorfieldstr(">")); } /* We currently only disassemble architecture with 32bits address space, even when 64b proc */ else { addr = (uint32_t) vaddr; snprintf(buf, maxlen, "0x%X", addr); } }
/** * @param str * @param bp * @param pc * @param parent * @return */ void bpdebug(char *str, elfshbp_t *bp, eresi_Addr pc, elfshobj_t *parent) { eresi_Addr addr; int off; char *name; elfshsect_t *sect; elfsh_Sym *sym; off = (bp ? 0 : e2dbgworld.stoppedthread->count == E2DBG_BREAK_HIT ? 3 : 6); addr = (bp ? bp->addr : pc - off); fprintf(stderr, "%s (PC = %08X) ::: parent = %s (BP DESCRIPTOR = %08X) \n", str, pc, (parent ? parent->name : "NO PARENT NAME"), (eresi_Addr) bp); if (!bp) return; sect = elfsh_get_parent_section(parent, addr, NULL); name = revm_resolve(parent, addr, &off); sym = elfsh_get_metasym_by_value(parent, addr, &off, ELFSH_LOWSYM); revm_object_display(sect, sym, 16, off, 0, addr, name, REVM_VIEW_DISASM, 0, 0); }
/** * @brief Put write capability on the zone * @param file * @param addr * @param sz * @return */ int elfsh_munprotect_userland(elfshobj_t *file, eresi_Addr addr, uint32_t sz) { elfshsect_t *sect; elfsh_Phdr *phdr; int retval; int prot; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (!elfsh_is_runtime_mode()) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); sect = elfsh_get_parent_section(file, addr, NULL); if (!sect) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find parent section", -1); phdr = elfsh_get_parent_segment(file, sect); prot = 0; if (elfsh_segment_is_readable(phdr)) prot |= PROT_READ; if (elfsh_segment_is_writable(phdr)) prot |= PROT_WRITE; if (elfsh_segment_is_executable(phdr)) prot |= PROT_EXEC; retval = mprotect((void *) (addr - addr % getpagesize()), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC); if (retval != 0) { perror("munprotect"); PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Failed mprotect", -1); } /* Return the original rights */ PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, prot); }
/** * @brief On MIPS there is no .plt section : call to libraries are done * using an indirect jump on .got value directly from .text. If * we want to be able to call the original function from the hook * function, we need to create a plt-like section and mirror the * Global Offset Table (.got). * * By default, .got points in some code stub standing in .text. This * code stubs looks much like a PLT. Indeed, there is a kind of PLT * on MIPS but it is embedded in the .text section. What we do in this * function is to disambiguize .plt and .text, so that we can apply * ALTPLT technique as well on MIPS. * * Unlike on ALPHA, we cannot move relocations from .got to .alt.got * since MIPS binaries have no relocation tables in ET_EXEC objects. * What we do instead is changing the processor specific DT_PLTGOT * entry in the .dynamic section and continue to rely on the ALTPLT * technique (call's the original functions using the injected 'old_' * symbol) just like ALTPLT redirection on other architectures. -mm * * @param file Host file. * @return Success (0) or Error (-1). */ int elfsh_build_plt(elfshobj_t *file) { elfshsect_t *text; elfsh_SAddr off; char buff[16] = {0x00}; eresi_Addr pltaddr = 0; eresi_Addr pltend = 0; elfsh_Shdr start; elfsh_Shdr plt; elfshsect_t *enew; eresi_Addr lsize; unsigned int size; char *data; char *tdata; unsigned int idx; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* First checks */ text = elfsh_get_parent_section(file, elfsh_get_entrypoint(file->hdr), &off); if (!text) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find parent section from entry point", -1); if (!elfsh_get_anonymous_section(file, text)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get an anonymous section", -1); /* ** Find the embedded plt by searching the nop;nop;nop;nop; signature ** that delimit the beginning and the end of plt. This is MIPS specific ** since only MIPS needs this. */ tdata = elfsh_readmem(text); for (off = 0; off < text->shdr->sh_size; off += 4) if (!memcmp(tdata + off, buff, sizeof(buff))) { pltaddr = text->shdr->sh_addr + off + 16; for (off += 16; off < text->shdr->sh_size; off += 4) if (!memcmp(tdata + off, buff, 16)) { pltend = text->shdr->sh_addr + off + 16; goto found; } PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find PLT end", -1); } found: idx = text->index; if (!pltaddr) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find PLT start", -1); /* ** Now create additional section header table entries so that we reduce the ** MIPS model to our standard ALTPLT/ALTGOT model. ** ** Do not use the elfsh_insert_*_section() since we want no address space ** shifting. */ /* .start */ lsize = pltaddr - text->shdr->sh_addr; size = (unsigned int) lsize; start = elfsh_create_shdr(0, SHT_PROGBITS, SHF_EXECINSTR | SHF_ALLOC, text->shdr->sh_addr, text->shdr->sh_offset, size, 0, 0, 0, 0); enew = elfsh_create_section(ELFSH_SECTION_NAME_START); XALLOC(__FILE__, __FUNCTION__, __LINE__,data, size, -1); memcpy(data, tdata, size); elfsh_insert_shdr(file, start, idx, enew->name, 0); elfsh_add_section(file, enew, idx, data, ELFSH_SHIFTING_MIPSPLT); file->secthash[ELFSH_SECTION_MIPSTART] = enew; /* .plt */ lsize = pltend - pltaddr; size = (unsigned int) lsize; plt = elfsh_create_shdr(0, SHT_PROGBITS, SHF_EXECINSTR | SHF_ALLOC, start.sh_addr + start.sh_size, start.sh_offset + start.sh_size, size, 0, 0, 0, 0); enew = elfsh_create_section(ELFSH_SECTION_NAME_PLT); XALLOC(__FILE__, __FUNCTION__, __LINE__,data, size, -1); memcpy(data, tdata + start.sh_size, size); elfsh_insert_shdr(file, plt, idx + 1, enew->name, 0); elfsh_add_section(file, enew, idx + 1, data, ELFSH_SHIFTING_MIPSPLT); /* Shift .text data, sh_offset, sh_addr, and sh_size correctly */ text->shdr->sh_offset += (start.sh_size + plt.sh_size); text->shdr->sh_addr += (start.sh_size + plt.sh_size); memmove(tdata, tdata + start.sh_size + plt.sh_size, text->shdr->sh_size - (start.sh_size + plt.sh_size)); text->shdr->sh_size -= (start.sh_size + plt.sh_size); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Return the dynamic symbol name giving its value, * Fill 'offset' with the difference between sym->st_value and 'value' * @param file * @param value * @param offset * @return */ char *elfsh_reverse_symbol(elfshobj_t *file, eresi_Addr value, elfsh_SAddr *offset) { elfshsect_t *sect; elfsh_Sym *sorted; int num; int index; char *str; int best; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (!value || value == 0xFFFFFFFF) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameter", NULL); if (file == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", NULL); /* handle dynamic case */ if (elfsh_is_runtime_mode()) value -= file->rhdr.base; /* If there is no symtab, resolve using SHT */ if (elfsh_get_symtab(file, &num) == NULL) { sect = elfsh_get_parent_section(file, value, offset); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "No parent section", NULL); *offset = (elfsh_SAddr) (sect->shdr->sh_addr - value); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (elfsh_get_section_name(file, sect))); } /* Else use the sorted-by-address symbol table to match what we want */ if (file->secthash[ELFSH_SECTION_SYMTAB]->altdata == NULL) elfsh_sync_sorted_symtab(file->secthash[ELFSH_SECTION_SYMTAB]); sorted = file->secthash[ELFSH_SECTION_SYMTAB]->altdata; /* Now find the best symbol -- type is more important than offset */ for (str = NULL, best = index = 0; index < num; index++) if (sorted[index].st_value <= value && DUMPABLE(sorted + index)) { if (best && !BESTYPE(sorted + index, sorted + best)) continue; *offset = (elfsh_SAddr) (value - sorted[index].st_value); best = index; str = elfsh_get_symbol_name(file, sorted + index); if (!*str) str = NULL; } if (str) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, str); PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "No valid symbol interval", NULL); }
/** * Search a call for a given address * @param file target file * @param addr supose to be a function */ int elfsh_addr_is_called(elfshobj_t *file, eresi_Addr addr) { int ret; int index; asm_instr instr; elfsh_SAddr foffset; elfsh_Word len; char *base; asm_processor proc; eresi_Addr base_vaddr; u_char found = 0; elfshsect_t *text; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (!file) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameter", -1); /* Search entrypoint section, our address must be in this section */ text = elfsh_get_parent_section(file, elfsh_get_entrypoint(file->hdr), &foffset); if (!text) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find parent section from entry point", -1); if (!elfsh_get_anonymous_section(file, text)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get an anonymous section", -1); base = elfsh_readmem(text); len = text->shdr->sh_size; /* Get the virtual address */ base_vaddr = (elfsh_is_runtime_mode() && !elfsh_section_is_runtime(text) ? file->rhdr.base + elfsh_get_section_addr(text->shdr) : elfsh_get_section_addr(text->shdr)); /* Our address is valid ? */ if (!INTERVAL(base_vaddr, addr, (base_vaddr + len))) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Not in entrypoint section", -4); /* Setup asm_processor structure */ if (etrace_setup_proc(file, &proc) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Failed during proc structure setup", -1); /* Despite the fact that we choose the right architecture to init asm, Our approach is totally architecture independant as we search using global type ASM_TYPE_CALLPROC and we know that op[0].imm will contain a relative value. */ for (index = 0; index < len; index += ret) { /* Read an instruction */ if ((ret = asm_read_instr(&instr, (u_char *) (base + index), len - index, &proc))) { /* Global assembler filter */ if ((instr.type & ASM_TYPE_CALLPROC) && instr.op[0].imm != 0) { /* Found the correct call */ if (base_vaddr + index + instr.op[0].imm + instr.len == addr) { found = 1; break; } } } if (ret <= 0) ret = 1; } if (!found) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "No call found", -3); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Print the chosen symbol table * @param file * @param sect * @param tab * @param num * @param regx * @param get_symname * @return */ int ds(elfshobj_t *file, elfshsect_t *sect, u_int num, regex_t *regx, char *(*get_symname)(elfshobj_t *f, elfsh_Sym *s)) { elfsh_Sym *table; char *name; char *type; char *bind; u_int typenum; u_int bindnum; u_int foff; u_int index; char *sect_name; char buff[512]; char off[50]; char type_unk[ERESI_MEANING + 1]; char bind_unk[ERESI_MEANING + 1]; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sort the table if necessary */ if (world.state.sort != NULL) switch (*world.state.sort) { case ELFSH_SORT_BY_ADDR: table = sect->altdata; break; case ELFSH_SORT_BY_SIZE: table = sect->terdata; break; default: PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unknown sort mode", -1); } /* Avoid reading inexistant memory in the process for .symtab */ else table = (elfsh_Sym *) (sect->shdr->sh_addr ? elfsh_readmem(sect) : sect->data); /* Browse symtab */ for (index = 0; index < num; index++) { /* Retreive names */ typenum = elfsh_get_symbol_type(table + index); bindnum = elfsh_get_symbol_bind(table + index); type = (char *) (typenum > ELFSH_SYMTYPE_MAX ? revm_build_unknown(type_unk, "type", typenum) : elfsh_sym_type[typenum].desc); bind = (char *) (bindnum >= ELFSH_SYMBIND_MAX ? revm_build_unknown(bind_unk, "type", bindnum) : elfsh_sym_bind[bindnum].desc); name = get_symname(world.curjob->curfile, table + index); sect_name = NULL; sect = elfsh_get_parent_section(world.curjob->curfile, table[index].st_value, NULL); if (sect == NULL && table[index].st_shndx) sect = elfsh_get_section_by_index(world.curjob->curfile, table[index].st_shndx, NULL, NULL); if (sect != NULL) sect_name = elfsh_get_section_name(world.curjob->curfile, sect); /* Fixup names */ if (name == NULL || *name == 0) name = ELFSH_NULL_STRING; if (type == NULL || *type == 0) type = ELFSH_NULL_STRING; if (bind == NULL || *bind == 0) bind = ELFSH_NULL_STRING; if (sect_name == NULL) sect_name = ELFSH_NULL_STRING; foff = (!table[index].st_value ? 0 : elfsh_get_foffset_from_vaddr(world.curjob->curfile, table[index].st_value)); if (sect && sect->shdr->sh_addr != table[index].st_value) snprintf(off, sizeof(off), " + %s", revm_colornumber("%u", (u_int) (table[index].st_value - sect->shdr->sh_addr))); else *off = '\0'; /* Different output depending on the quiet flag */ if (!world.state.revm_quiet) { snprintf(buff, sizeof(buff), " %s %s %s %s %s%s " "%s%s %s%s %s%s => %s%s\n", revm_colornumber("[%03u]", index), revm_coloraddress(XFMT, (eresi_Addr) elfsh_get_symbol_value(table + index) + file->rhdr.base), revm_colortypestr_fmt("%-8s", type), revm_colorstr_fmt("%-40s", name), revm_colorfieldstr("size:"), revm_colornumber("%010u", elfsh_get_symbol_size(table + index)), revm_colorfieldstr("foffset:"), revm_colornumber("%06u", foff), revm_colorfieldstr("scope:"), revm_colortypestr_fmt("%-6s", bind), revm_colorfieldstr("sctndx:"), revm_colornumber("%02u", elfsh_get_symbol_link(table + index)), revm_colorstr(sect_name), off); } else { snprintf(buff, sizeof(buff), " %s %s %s %s %s%s %s%s %s%-6s\n", revm_colornumber("[%03u]", index), revm_coloraddress(XFMT, (eresi_Addr) elfsh_get_symbol_value(table + index) + file->rhdr.base), revm_colortypestr_fmt("%-8s", type), revm_colorstr_fmt("%-15s", name), revm_colorfieldstr("sz:"), revm_colornumber("%06u", elfsh_get_symbol_size(table + index)), revm_colorfieldstr("foff:"), revm_colornumber("%06u", foff), revm_colorfieldstr("scop:"), revm_colortypestr_fmt("%-6s", bind)); } if (regx == NULL || (regx != NULL && regexec(regx, buff, 0, 0, 0) == 0)) { /* If the user ask quit, we just break */ if (revm_output(buff) == -1) break; } revm_endline(); } revm_endline(); revm_output("\n"); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * @brief Link function layer * @param ctxt mjollnir context structure * @param str source address * @param dst destination address * @param ret return address */ int mjr_link_func_call(mjrcontext_t *ctxt, eresi_Addr src, eresi_Addr dst, eresi_Addr ret) { container_t *fun; char *tmpstr; eresi_Addr tmpaddr; elfshsect_t *dstsect; u_char scope; u_char isnew; #if __DEBUG_FLOW__ mjrfunc_t *tmpfunc; mjrfunc_t *tmpfunc2; //char *md5; #endif PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); #if __DEBUG_LINKS__ fprintf(D_DESC, "[D] %s: src:"XFMT" dst:"XFMT" ret:"XFMT"\n", __FUNCTION__, src, dst, ret); #endif /* Check if we are not pointing into BSS */ dstsect = elfsh_get_parent_section(ctxt->obj, dst, NULL); if (!dstsect || !dstsect->data) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); scope = (!dstsect || !strcmp(dstsect->name, ELFSH_SECTION_NAME_PLT) ? MJR_LINK_SCOPE_GLOBAL : MJR_LINK_SCOPE_LOCAL); /* Link/Prepare function layer. We use an intermediate variable, else the compiler optimize too hard and that make segfault (bug in gcc ?) */ tmpaddr = dst; fun = mjr_function_get_by_vaddr(ctxt, tmpaddr); if (!fun) { tmpstr = _vaddr2str(tmpaddr); fun = mjr_create_function_container(ctxt, tmpaddr, 0, tmpstr, NULL, NULL); mjr_function_register(ctxt, tmpaddr, fun); mjr_function_symbol(ctxt, fun); isnew = 1; } else isnew = 0; /* Add links between functions */ if (ctxt->curfunc) { #if __DEBUG_FLOW__ tmpfunc = (mjrfunc_t *) ctxt->curfunc->data; tmpfunc2 = (mjrfunc_t *) fun->data; fprintf(stderr, " [*] Linking function " XFMT " to func " XFMT "\n", tmpfunc->vaddr, tmpfunc2->vaddr); #endif mjr_container_add_link(ctxt, fun, ctxt->curfunc->id, MJR_LINK_FUNC_RET, scope, CONTAINER_LINK_IN); mjr_container_add_link(ctxt, ctxt->curfunc, fun->id, MJR_LINK_FUNC_CALL, scope, CONTAINER_LINK_OUT); } /* Fingerprint function */ /* ** XXX: this segfaults on 64bits architecture - MAX_FUNC_LEN should ** not be used. Instead, we should fingerprint all functions with ** their infered size at the moment of saving debug sections. ** md5 = mjr_fingerprint_function(ctxt, tmpaddr, MJR_FPRINT_TYPE_MD5); tmpfunc = (mjrfunc_t *) fun->data; if (md5) memcpy(tmpfunc->md5, md5, sizeof(tmpfunc->md5)); */ #if __DEBUG_FLOW__ tmpfunc = fun->data; #endif if (scope == MJR_LINK_SCOPE_LOCAL && isnew) { elist_push(ctxt->func_stack, fun); ctxt->curfunc = fun; #if __DEBUG_FLOW__ fprintf(stderr, " [*] New CURFUNC @ %s \n", tmpfunc->name); } else if (!isnew) fprintf(stderr, " ******** ALREADY SEEN FUNCTION : NOT CHANGING CURFUNC @ %s \n", tmpfunc->name); else fprintf(stderr, " ******** GLOBAL FUNCTION CALL : NOT CHANGING CURFUNC @ %s \n", tmpfunc->name); #else }
/** * The Real routine that handles each thread-specific breakpoint state machine. * * breakpoint case: * * 0-1: restore old instr, enable step, set bp addr in ->past, reset pc to orig bp addr * 1-2: do nothing (just executed instr) * 2-3: reinstall, resetstep, thread_contall, curthread->was_step = 0, * stoppedthread->count = NONE; e2dbgworld.curbp = NULL; stoppedthread = RUNNING * * stepping case: * * 0-1: print current instr; reinstall * stoppedthread->state = BREAKING; thread_contall; * was_step = 0; count = NONE; curbp = NULL; stoppedthread->state = RUNNING */ void e2dbg_breakpoint_process() { char buf[32]; elfshbp_t *bp; int prot; char *name; elfsh_SAddr off; int ret; asm_instr ptr; char *s; eresi_Addr *pc; eresi_Addr savedpc; u_int bpsz; elfshsect_t *sect; elfshobj_t *parent; elfsh_Sym *sym; #if __DEBUG_BP__ fprintf(stderr, " [D] Entering breakpoint handler\n"); #endif /* Set all registers as variables and get PC */ e2dbg_user_hooks_install(); e2dbg_getregs(); pc = e2dbg_getpc(); parent = e2dbg_get_parent_object((eresi_Addr) *pc); bpsz = elfsh_get_breaksize(parent); /* Print variables and registers on breakpoints */ //if (!world.state.revm_quiet) //cmd_vlist(); /* Try to find the breakpoint at current instruction pointer */ #if __DEBUG_BP__ fprintf(stderr, " [D] At PC = %08X : Trying to find breakpoint at addr %08X (bpsize = %u)\n", *pc, *pc - bpsz, bpsz); #endif snprintf(buf, sizeof(buf), XFMT, *pc - bpsz); bp = hash_get(&e2dbgworld.bp, buf); #if __DEBUG_BP__ if (bp) fprintf(stderr, " [D] Saved instruction BYTE = %02X and PC-BPSZ BYTE = %02X \n", bp->savedinstr[0], *((u_char *) *pc - bpsz)); else fprintf(stderr, " [D] No BP found at %08X ! \n", *pc); #endif /* Case of processing breakpoint, or if we are single-stepping */ if (!bp || (bp->savedinstr[0] == *((u_char *) *pc - bpsz))) { /* We are single-stepping, display the instruction at $pc */ if (e2dbgworld.stoppedthread->step) { #if __DEBUG_BP__ fprintf(stderr, " [D] Single-stepping -IS- enabled \n"); #endif ret = asm_read_instr(&ptr, (u_char *) *pc, 16, world.curjob->proc); if (!ret) ret++; sect = elfsh_get_parent_section(parent, (eresi_Addr) *pc, NULL); name = revm_resolve(parent, (eresi_Addr) *pc, &off); off = 0; sym = elfsh_get_metasym_by_value(parent, (eresi_Addr) *pc, &off, ELFSH_LOWSYM); #if __DEBUG_BP__ fprintf(stderr, " [D] Found parent = %08X (%s) in step (name = %s, parentsect = %s) off = %u\n", (eresi_Addr) parent, parent->name, name, sect->name, off); #endif revm_instr_display(-1, *pc, 0, 20, name, off, (char *) *pc); e2dbg_display(e2dbgworld.displaycmd, e2dbgworld.displaynbr); if (!e2dbgworld.stoppedthread->trace) e2dbg_entry(NULL); else e2dbg_watch(); e2dbg_breakpoint_reinstall(); return; } #if __DEBUG_BP__ fprintf(stderr, " [D] Single-stepping is -NOT- enabled \n"); #endif /* Here starts the real stuff ** ** count == E2DBG_BREAK_EXEC -> execute restored instruction ** count == E2DBG_BREAK_FINISHED -> restore breakpoint ** count > E2DBG_BREAK_MAX -> e2dbg is getting debugged by a third party debugger */ e2dbgworld.stoppedthread->count++; #if __DEBUG_BP__ fprintf(stderr, " [C] Count %u -> %u for thread ID %u \n", e2dbgworld.stoppedthread->count - 1, e2dbgworld.stoppedthread->count, ((unsigned int) e2dbgworld.stoppedthread->tid)); #endif /* execute the previously restored instruction */ if (e2dbgworld.stoppedthread->count == E2DBG_BREAK_EXEC) { #if __DEBUG_BP__ printf(" [D] At PC = 0x%X : Debuggee executed restored instruction \n", *pc); #endif return; } /* Suggested by andrewg, useful when debugging valgrind */ if (e2dbgworld.stoppedthread->count > E2DBG_BREAK_MAX) { printf(".::- E2DBG WARNING -::.\n" "Breakpoint triggered at location " AFMT " which we don't know about.\n\n" "This may be an anti-debug trick or the program may be inside another\n" "debugger. Exiting (DEBUG: count = " UFMT ", step is off)\n\n", *pc - bpsz, e2dbgworld.stoppedthread->count); return; } e2dbg_breakpoint_reinstall(); /* remove trace flag */ #if __DEBUG_BP__ fprintf(stderr, " [D] At PC = " AFMT " Resetting STEP mode\n", *pc); #endif e2dbg_resetstep(); return; } /* Case of newly hit breakpoint */ else { name = revm_resolve(parent, (eresi_Addr) *pc - bpsz, &off); s = (e2dbg_is_watchpoint(bp) ? "Watch" : "Break"); #if __DEBUG_BP__ fprintf(stderr, " [C] Count set to 1 (HIT) for thread ID %u \n", (unsigned int) e2dbgworld.stoppedthread->tid); #endif if (off) printf(" [*] %spoint found at " XFMT " <%s + " DFMT "> in thread %u \n\n", s, *pc - bpsz, name, off, (unsigned int) e2dbgworld.stoppedthread->tid); else printf(" [*] %spoint found at " XFMT " <%s> in thread %u \n\n", s, *pc - bpsz, name, (unsigned int) e2dbgworld.stoppedthread->tid); revm_doswitch(parent->id); mjr_set_current_context(&world.mjr_session, parent->name); *pc -= bpsz; prot = elfsh_munprotect(bp->obj, *pc, bpsz); memcpy((u_char *) *pc, bp->savedinstr, bpsz); elfsh_mprotect(bp->obj, *pc, bpsz, prot); e2dbg_setstep(); #if __DEBUG_BP__ fprintf(stderr, " [D] Setting Step mode \n"); #endif e2dbgworld.stoppedthread->past = *pc; e2dbgworld.stoppedthread->count = E2DBG_BREAK_HIT; e2dbgworld.curbp = bp; bp->tid = (uint32_t) e2dbgworld.stoppedthread->tid; #if __DEBUG_BP__ fprintf(stderr, " [D] Reset break \n"); #endif if (bp->cmdnbr) e2dbg_display(bp->cmd, bp->cmdnbr); else e2dbg_display(e2dbgworld.displaycmd, e2dbgworld.displaynbr); #if __DEBUG_BP__ fprintf(stderr, " [D] After BP display \n"); #endif #if __DEBUG_BP__ fprintf(stderr, " [D] PC before entry is addr 0x%X \n", *pc); #endif savedpc = *pc; e2dbg_entry(NULL); *pc = savedpc; #if __DEBUG_BP__ fprintf(stderr, " [D] At PC = 0x%X : returned from BP handler with step enabled\n", *pc); #endif } }
/** * Get function addr list from call search basic * @param file target file * @param addr address list */ int elfsh_addr_get_func_list(elfshobj_t *file, eresi_Addr **addr) { int ret; int index; asm_instr instr; elfsh_SAddr foffset; elfsh_Word len; char *base; asm_processor proc; eresi_Addr base_vaddr, caddr; u_char found = 0; elfshsect_t *text; eresi_Addr *vaddr; const int astep = 20; u_int apos = 0; btree_t *broot = NULL; u_int diff; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (!file || !addr) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameters", -1); /* Search entrypoint section, our address must be in this section */ text = elfsh_get_parent_section(file, elfsh_get_entrypoint(file->hdr), &foffset); if (!text) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find parent section from entry point", -1); if (!elfsh_get_anonymous_section(file, text)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get an anonymous section", -1); base = elfsh_readmem(text); len = text->shdr->sh_size; /* Get the virtual address */ base_vaddr = (elfsh_is_runtime_mode() && !elfsh_section_is_runtime(text) ? file->rhdr.base + elfsh_get_section_addr(text->shdr) : elfsh_get_section_addr(text->shdr)); /* Setup asm_processor structure */ if (etrace_setup_proc(file, &proc) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Failed during proc structure setup", -1); XALLOC(__FILE__, __FUNCTION__, __LINE__, vaddr, sizeof(eresi_Addr)*astep, -1); /* Despite the fact that we choose the right architecture to init asm, Our approach is totally architecture independant as we search using global type ASM_TYPE_CALLPROC and we know that op[0].imm will contain a relative value. */ for (index = 0; index < len; index += ret) { /* Read an instruction */ if ((ret = asm_read_instr(&instr, (u_char *) (base + index), len - index, &proc))) { /* Global assembler filter */ if ((instr.type & ASM_TYPE_CALLPROC) && instr.op[0].imm != 0) { caddr = base_vaddr + index + instr.op[0].imm + instr.len; /* Found a call check its local */ if (INTERVAL(base_vaddr, caddr, base_vaddr + len)) { found = 1; diff = (u_int) caddr; /* Avoid double entrie */ if (btree_get_elem(broot, diff) != NULL) goto next; btree_insert(&broot, diff, (void *)0x1); /* Next will be the last of the current list then realloc */ if ((apos+1) % astep == 0) { XREALLOC(__FILE__, __FUNCTION__, __LINE__, vaddr, vaddr, sizeof(eresi_Addr)*(apos+1+astep), -1); /* Blank new elements */ memset(&vaddr[apos], 0x00, astep*sizeof(eresi_Addr)); } vaddr[apos++] = caddr; } } } next: if (ret <= 0) ret = 1; } /* If nothing found we free allocated buffer and return an error */ if (!found) { XFREE(__FILE__, __FUNCTION__, __LINE__, vaddr); PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "No call internal found", -3); } btree_free(broot, 0); *addr = vaddr; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/* Find the correct location for a breakpoint. Avoid putting breakpoints on plt entries when possible */ eresi_Addr e2dbg_breakpoint_find_addr(char *str) { elfsh_Sym *sym; elfsh_Sym *bsym; elfshsect_t *sect; elfshobj_t *parent; char **keys; int keynbr; int index; eresi_Addr addr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); sym = bsym = NULL; /* Sometimes we fix symbols on the disk : we avoid mprotect */ /* Only return success early if not a plt symbol */ keys = NULL; parent = world.curjob->curfile; sym = elfsh_get_metasym_by_name(parent, str); if (!sym || !sym->st_value) { elfsh_toggle_mode(); sym = elfsh_get_metasym_by_name(parent, str); elfsh_toggle_mode(); } if (sym && parent->hdr->e_type == ET_DYN) sym->st_value += parent->rhdr.base; if (sym && sym->st_value) { sect = elfsh_get_parent_section(parent, sym->st_value, NULL); if (!elfsh_is_plt(parent, sect)) goto end; } /* Try to look in other objects */ keys = hash_get_keys(&world.curjob->loaded, &keynbr); for (index = 0; index < keynbr; index++) { if (strstr(keys[index], E2DBG_ARGV0)) continue; parent = hash_get(&world.curjob->loaded, keys[index]); bsym = elfsh_get_metasym_by_name(parent, str); if (!bsym || !bsym->st_value) { elfsh_toggle_mode(); bsym = elfsh_get_metasym_by_name(parent, str); elfsh_toggle_mode(); if (bsym && bsym->st_value) { sect = elfsh_get_parent_section(parent, bsym->st_value, NULL); if (!elfsh_is_plt(parent, sect)) { sym = bsym; if (strstr(parent->name, "libc.so")) goto end; } if (!sym) sym = bsym; } } else { sect = elfsh_get_parent_section(parent, bsym->st_value, NULL); if (!elfsh_is_plt(parent, sect)) { sym = bsym; if (strstr(parent->name, "libc.so")) goto end; } if (!sym) sym = bsym; } #if __DEBUG_BP__ printf("BPSym after %-30s = " XFMT "\n", parent->name, (eresi_Addr) sym); #endif } /* Return error or success, dont forget to free the keys */ end: if (keys) hash_free_keys(keys); if (!sym || !sym->st_value) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "No symbol by that name in the current file", 0); addr = sym->st_value; if (elfsh_get_objtype(parent->hdr) == ET_DYN) { #if __DEBUG_BP__ printf(" [*] Adding base addr " XFMT " of ET_DYN %s\n", parent->rhdr.base, parent->name); #endif addr += parent->rhdr.base; } #if __DEBUG_BP__ printf(" [*] Will set breakpoint on " XFMT " (parent = %s) \n", addr, parent->name); #endif PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, addr); }