/** * Perform a raw write on the object cache data * @param file * @param foffset * @param src_buff * @param len * @return */ int elfsh_raw_write(elfshobj_t *file, u_int foffset, void *src_buff, int len) { elfshsect_t *sect; int sect_off; void *dst; int prot; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); sect = elfsh_get_parent_section_by_foffset(file, foffset, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid virtual address", -1); sect_off = foffset - sect->shdr->sh_offset; if (sect_off + len > sect->shdr->sh_size) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Section too short", -1); dst = elfsh_get_anonymous_section(file, sect); if (dst == NULL) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); if (elfsh_is_runtime_mode()) { prot = elfsh_munprotect(file, (eresi_Addr) dst + sect_off, len); memcpy(dst + sect_off, src_buff, len); elfsh_mprotect(file, (eresi_Addr) dst + sect_off, len, prot); } else memcpy(dst + sect_off, src_buff, len); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (len)); }
/** * Perform a raw read on the object cache data * @param file * @param foffset * @param dest_buff * @param len * @return */ int elfsh_raw_read(elfshobj_t *file, u_int foffset, void *dest_buff, int len) { volatile elfshsect_t *sect; volatile void *src; volatile int sect_off; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); sect = elfsh_get_parent_section_by_foffset(file, foffset, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid virtual address", -1); sect_off = foffset - sect->shdr->sh_offset; if (sect_off + len > sect->shdr->sh_size) len -= (sect_off + len - sect->shdr->sh_size); src = elfsh_get_anonymous_section(file, sect); if (src == NULL) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); memcpy(dest_buff, src + sect_off, len); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (len)); }
/** * 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); }
/** * 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); }
/** * @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); }
/** * Load all the part of the binary. * This function should not be used by e2dbg * @param file * @return */ int elfsh_read_obj(elfshobj_t *file) { elfshsect_t *actual; int index; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (file->read) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); if (file->sht == NULL && NULL == elfsh_get_sht(file, NULL)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to grab SHT", -1); if (NULL == elfsh_get_pht(file, NULL) && file->hdr->e_type != ET_REL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to grab PHT", -1); #if __DEBUG_MAP__ puts("[DEBUG:read_obj] Loading all known typed sections\n"); #endif /* Fill multiple relocation sections */ for (index = 0; NULL != (actual = elfsh_get_reloc(file, index, NULL)); index++); /* ** Load sections placed after symtab ** Added for Solaris */ elfsh_get_comments(file); elfsh_get_dwarf(file); elfsh_get_stab(file, NULL); if (file->hdr->e_type == ET_CORE) { elfsh_get_core_notes(file); goto out; } /* ** We cannot use simply elfsh_get_anonymous_section() here ** because the object's section hash ptrs would not be filled. */ elfsh_get_symtab(file, NULL); /* Fixup stuffs in the SHT */ elfsh_fixup(file); elfsh_get_dynsymtab(file, NULL); elfsh_get_stab(file, NULL); elfsh_get_dynamic(file, NULL); elfsh_get_ctors(file, NULL); elfsh_get_dtors(file, NULL); elfsh_get_got(file, NULL); elfsh_get_interp(file); elfsh_get_versymtab(file, NULL); elfsh_get_verneedtab(file, NULL); elfsh_get_verdeftab(file, NULL); elfsh_get_hashtable(file, NULL); //elfsh_get_comments(file); elfsh_get_plt(file, NULL); /* Fill the multiple notes sections */ for (index = 0; NULL != elfsh_get_notes(file, index); index++); /* Loop on the section header table and load all unknown-typed sections */ for (actual = file->sectlist; actual; actual = actual->next) { /* Fix first section size */ if (actual->shdr->sh_size == 0 && actual->next && actual->next->shdr->sh_offset != actual->shdr->sh_offset && actual->next->shdr->sh_addr != actual->shdr->sh_addr) actual->shdr->sh_size = actual->next->shdr->sh_offset - actual->shdr->sh_offset; /* If the section data has to be loaded, load it */ /* In case of bss, only load if BSS data is inserted in the file */ if (actual->data == NULL && actual->shdr->sh_size) { if ((actual->shdr->sh_type == SHT_NOBITS && actual->shdr->sh_offset == actual->next->shdr->sh_offset) || (actual->next != NULL && actual->next->shdr->sh_offset == actual->shdr->sh_offset)) continue; #if __DEBUG_MAP__ printf("[LIBELFSH] Loading anonymous section %15s \n", elfsh_get_section_name(file, actual)); #endif elfsh_get_anonymous_section(file, actual); } } /* Fixup various symbols like dynamic ones that are NULL */ /* Non fatal error */ if (file->secthash[ELFSH_SECTION_DYNSYM]) elfsh_fixup_dynsymtab(file->secthash[ELFSH_SECTION_DYNSYM]); out: /* We close the file descriptor after file mapping so we can open more files */ if (file->fd >= 0) { #if __DEBUG_MAP__ printf("[LIBELFSH] Closing descriptor %d \n", file->fd); #endif XCLOSE(file->fd, -1); /* neutralize file descriptor */ file->fd = -1; } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }