/** * Remap a section which type is SHT_SYMTAB or SHT_DYNSYM * @param s * @param diff * @return */ int elfsh_reloc_symtab(elfshsect_t *s, eresi_Addr diff) { elfsh_Sym *symtab; u_int i; eresi_Addr vaddr; u_int count; eresi_Addr base; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (s == NULL || s->shdr == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); else if (s->shdr->sh_type != SHT_SYMTAB && s->shdr->sh_type != SHT_DYNSYM) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Section is not a symbol table", -1); symtab = elfsh_readmem(s); base = elfsh_get_object_baseaddr(s->parent); for (count = i = 0; i < s->shdr->sh_size / sizeof(elfsh_Sym); i++) { vaddr = elfsh_get_symbol_value(symtab + i); if (vaddr > base) { elfsh_set_symbol_value(symtab + i, vaddr + diff); count++; } } /* Synchronize the symtab hash table */ elfsh_sync_sorted_symtab(s); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (count)); }
/** * Remove a symbol * This function is not e2dbg safe * @param symtab * @param name * @return */ int elfsh_remove_symbol(elfshsect_t *symtab, char *name) { elfsh_Sym *ret; elfsh_Sym *tab; elfsh_Sym *enew; u_int off; u_int movedsz; hash_t *uptable = NULL; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (symtab == NULL || name == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameters", -1); ret = elfsh_get_symbol_by_name(symtab->parent, name); if (ret == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unknown symbol", -1); /* Do it */ tab = symtab->data; off = (u_long) ret - (u_long) tab; movedsz = symtab->shdr->sh_size - off - sizeof(elfsh_Sym); if (movedsz) memcpy((char *) symtab->data + off, (char *) symtab->data + off + sizeof(elfsh_Sym), movedsz); symtab->shdr->sh_size -= sizeof(elfsh_Sym); symtab->curend -= sizeof(elfsh_Sym); XALLOC(__FILE__, __FUNCTION__, __LINE__,enew, symtab->shdr->sh_size, -1); memcpy(enew, tab, symtab->shdr->sh_size); XFREE(__FILE__, __FUNCTION__, __LINE__,tab); symtab->data = enew; /* We just cant remove the string because of ELF string table format */ elfsh_sync_sorted_symtab(symtab); /* Update hashtable */ switch(symtab->shdr->sh_type) { case SHT_SYMTAB: uptable = &symtab->parent->symhash; break; case SHT_DYNSYM: uptable = &symtab->parent->dynsymhash; break; } if (uptable && uptable->ent) hash_del(uptable, name); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * The intermediate pass of theglobal algorithm for ET_REL injection * We fuze symbol tables from the ET_REL and the host binary * * @param file * @param rel * @return */ int elfsh_fuse_etrel_symtab(elfshobj_t *file, elfshobj_t *rel) { elfshsect_t *sect; elfsh_Sym newsym; elfsh_Half type; u_int index; char sctname[BUFSIZ]; elfsh_Sym *sym; int symnbr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); sym = elfsh_get_symtab(rel, &symnbr); for (index = 0; index < symnbr; index++) { type = elfsh_get_symbol_type(sym + index); /* Avoid non-injectable symbols */ if (type != STT_FUNC && type != STT_OBJECT) continue; if (sym[index].st_shndx >= rel->hdr->e_shnum) continue; /* Find target section in ET_REL */ sect = elfsh_get_section_by_index(rel, sym[index].st_shndx, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find extracted section in ET_REL\n", -1); /* Filter symbols using source section */ if (sect->shdr->sh_type != SHT_PROGBITS || !sect->shdr->sh_size || !elfsh_get_section_allocflag(sect->shdr)) continue; /* Find corresponding inserted section in ET_EXEC */ snprintf(sctname, sizeof(sctname), "%s%s", rel->name, sect->name); sect = elfsh_get_section_by_name(file, sctname, NULL, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find inserted section in ET_EXEC\n", -1); #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Injected ET_REL symbol %-20s ["XFMT"] \n", elfsh_get_symbol_name(rel, sym + index), (eresi_Addr) (sect->shdr->sh_addr + sym[index].st_value)); #endif /* Add symbol in host file */ newsym = elfsh_create_symbol(sect->shdr->sh_addr + sym[index].st_value, sym[index].st_size, elfsh_get_symbol_type(sym + index), elfsh_get_symbol_bind(sym + index), 0, sect->index); if (elfsh_insert_symbol(file->secthash[ELFSH_SECTION_SYMTAB], &newsym, elfsh_get_symbol_name(rel, sym + index)) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to insert ET_REL symbol", -1); } /* Resynchronize sorted instances of symbol table */ if (elfsh_sync_sorted_symtab(file->secthash[ELFSH_SECTION_SYMTAB]) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to synchronize host symtab", -1); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * This function traces the entry point and save the last push argument until * a call is found. This allow to fetch the main address in an OS-dependent * manner */ eresi_Addr mjr_trace_start(mjrcontext_t *context, u_char *buf, u_int len, eresi_Addr vaddr) { eresi_Addr main_addr; container_t *main_container; container_t *tmpcntnr; u_int dis; elfsh_Sym *sym; elfsh_Sym bsym; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (!context || !buf) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameters", 0); if (!(elfsh_get_objtype(elfsh_get_hdr(context->obj)) == ET_EXEC)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Object is not ET_EXEC", 0); #if defined(__DEBUG_MJOLLNIR__) printf(" [*] _start found at 0x%lx\n", (unsigned long) vaddr); #endif // sym = elfsh_get_symbol_by_name(context->obj, "main"); sym = elfsh_get_metasym_by_name(context->obj, "main"); if (sym && sym->st_value) { main_addr = sym->st_value; dis = 0; } else main_addr = mjr_find_main(context->obj, &context->proc, buf, len, vaddr, &dis); if (main_addr == -1) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Could not find address of main", 0); if (main_addr != vaddr) { tmpcntnr = mjr_create_function_container(context, vaddr, 0, "_start", 0, NULL); mjr_function_register(context, vaddr, tmpcntnr); #if defined(__DEBUG_MJOLLNIR__) fprintf(stderr, " [D] Creating MAIN Func at " XFMT "\n", main_addr); #endif main_container = mjr_create_function_container(context, main_addr, 0, "main", 0, NULL); mjr_function_register(context, main_addr, main_container); mjr_container_add_link(context, tmpcntnr, main_container->id, MJR_LINK_FUNC_CALL, MJR_LINK_SCOPE_LOCAL, CONTAINER_LINK_OUT); mjr_container_add_link(context, main_container, tmpcntnr->id, MJR_LINK_FUNC_RET, MJR_LINK_SCOPE_LOCAL, CONTAINER_LINK_IN); mjr_link_block_call(context, vaddr, main_addr, vaddr + dis); /* Create symbols for main */ /* Then we create the symbol for the bloc and returns */ if (!sym || !sym->st_value) { bsym = elfsh_create_symbol(main_addr, 0, STT_FUNC, 0, 0, 0); elfsh_insert_symbol(context->obj->secthash[ELFSH_SECTION_SYMTAB], &bsym, "main"); elfsh_sync_sorted_symtab(context->obj->secthash[ELFSH_SECTION_SYMTAB]); } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, main_addr); }
/** * @brief Copy the PLT of an ET_EXEC object for the ALTPLT technique. * and the GOT of an ET_EXEC object for the ALTGOT technique. * @param file Host file. * @param mod Always inject sections with size being a multiple of mod. * @return Success (0) or Error (-1). */ int elfsh_relink_plt(elfshobj_t *file, u_int mod) { elfshsect_t *got; elfshsect_t *plt; elfshsect_t *symtab; elfshsect_t *dynsym; elfshsect_t *prolog; elfshsect_t *extplt = NULL; elfshsect_t *altgot = NULL; /* shut the nice talkative */ elfshsect_t *enew = NULL; /* compiler also know as gcc */ elfsh_Shdr hdr; elfsh_Sym *sym; elfsh_Sym newsym; char buf[BUFSIZ]; u_int off; u_int entsz; int mode; eresi_Addr addr; char *prologdata; u_int sz; char *name; u_char ostype; eresi_Addr diff; u_int extplt_size; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Get PLT */ if (file->secthash[ELFSH_SECTION_ALTPLT] != NULL) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); plt = elfsh_get_plt(file, NULL); if (NULL == plt) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "PLT section not found", -1); entsz = elfsh_get_pltentsz(file); if (entsz < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Failed to get PLT entry size", -1); /* Get GOT (recent ld call it .got.plt) */ got = elfsh_get_gotsct(file); if (NULL == got) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "GOT section not found", -1); /* Get symtabs */ if (NULL == elfsh_get_dynsymtab(file, NULL)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "DYNSYM not found", -1); if (NULL == elfsh_get_symtab(file, NULL)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "SYMTAB not found", -1); /* Some fingerprint */ ostype = elfsh_get_ostype(file); if (ostype == ELFSH_OS_ERROR) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid OS target", -1); /* Insert alternative .plt */ dynsym = file->secthash[ELFSH_SECTION_DYNSYM]; symtab = file->secthash[ELFSH_SECTION_SYMTAB]; /* FreeBSD and BeoS is incompatible with pre-interp injection */ /* Solaris needs self-mutating code for ALTPLT technique */ /* %gp offsets on ALPHA/MIPS requires data injection */ ELFSH_SELECT_INJECTION(file,NULL,mode); /* Map .alt.plt.prolog on ALPHA, or .alt.got.prolog on MIPS */ if (FILE_IS_MIPS(file) || FILE_IS_ALPHA64(file)) { if (FILE_IS_MIPS(file)) { name = ELFSH_SECTION_NAME_ALTGOTPROLOG; sz = 28; } else { name = ELFSH_SECTION_NAME_ALTPLTPROLOG; sz = 48; } prolog = elfsh_create_section(name); hdr = elfsh_create_shdr(0, SHT_PROGBITS, SHF_EXECINSTR | SHF_ALLOC, 0, 0, sz, 0, 0, 0, 0); XALLOC(__FILE__, __FUNCTION__, __LINE__, prologdata, sz, -1); if (elfsh_insert_mapped_section(file, prolog, hdr, prologdata, mode, mod) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".alt.{plt,got}.prolog insertion failed", -1); enew = elfsh_get_section_by_name(file, name, NULL, NULL, NULL); if (enew == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".alt.{plt,got}.prolog insertion failed", -1); file->secthash[ELFSH_SECTION_ALTPLTPROLOG] = enew; } /* Map .alt.plt (or .pad.got on MIPS) On MIPS we use .pad.got in order to align .alt.got on a 0x1000 bound boundary. On ALPHA and SPARC, .alt.plt will be relocated instead of .plt */ sz = plt->shdr->sh_size; if (FILE_IS_MIPS(file)) { addr = enew->shdr->sh_addr + enew->shdr->sh_size; if ((addr - (got->shdr->sh_addr)) % 1024) sz = 1024 - ((addr - (got->shdr->sh_addr)) % 1024); XALLOC(__FILE__, __FUNCTION__, __LINE__, prologdata, sz, -1); memset(prologdata, 0x00, sz); name = ELFSH_SECTION_NAME_PADGOT; } else { XALLOC(__FILE__, __FUNCTION__, __LINE__, prologdata, sz, -1); memcpy(prologdata, elfsh_readmem(plt), sz); name = ELFSH_SECTION_NAME_ALTPLT; } enew = elfsh_create_section(name); hdr = elfsh_create_shdr(0, SHT_PROGBITS, SHF_EXECINSTR | SHF_ALLOC, 0, 0, sz, 0, 0, 0, 0); if (elfsh_insert_mapped_section(file, enew, hdr, prologdata, mode, mod) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".alt.plt|.pad.got insertion failed", -1); enew = elfsh_get_section_by_name(file, name, NULL, NULL, NULL); if (enew == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".alt.plt|.pad.got insertion failed", -1); file->secthash[ELFSH_SECTION_ALTPLT] = enew; /* Map .alt.got (all architectures except SPARC) */ /* On IA32, remap GOT with a doubled size for non-present symbol resolving */ if (FILE_IS_MIPS(file) || FILE_IS_ALPHA64(file) || FILE_IS_IA32(file)) { sz = (FILE_IS_MIPS(file) ? got->shdr->sh_size : FILE_IS_IA32(file) ? got->shdr->sh_size * 4 : plt->shdr->sh_size / elfsh_get_pltentsz(file) * sizeof(eresi_Addr)); altgot = elfsh_create_section(ELFSH_SECTION_NAME_ALTGOT); hdr = elfsh_create_shdr(0, SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, 0, 0, sz, 0, 0, 0, sizeof(eresi_Addr)); XALLOC(__FILE__, __FUNCTION__, __LINE__, prologdata, sz, -1); memcpy(prologdata, elfsh_readmem(got), got->shdr->sh_size); if (elfsh_insert_mapped_section(file, altgot, hdr, prologdata, ELFSH_DATA_INJECTION, mod) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".alt.got insertion failed", -1); altgot = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_ALTGOT, NULL, NULL, NULL); if (altgot == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".alt.got insertion failed", -1); file->secthash[ELFSH_SECTION_ALTGOT] = altgot; altgot->curend = got->shdr->sh_size; memset(elfsh_readmem(altgot) + got->shdr->sh_size, 0x00, got->shdr->sh_size); altgot->shdr->sh_entsize = sizeof(eresi_Addr); } /* Insert EXTPLT in order to be able to resolve non present symbols */ if (FILE_IS_IA32(file)) { extplt_size = plt->shdr->sh_size * 2; extplt = elfsh_create_section(ELFSH_SECTION_NAME_EXTPLT); hdr = elfsh_create_shdr(0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, 0, extplt_size, 0, 0, 0, 0); XALLOC(__FILE__, __FUNCTION__, __LINE__, prologdata, plt->shdr->sh_size, -1); memcpy(prologdata, elfsh_readmem(plt), plt->shdr->sh_size); if (elfsh_insert_mapped_section(file, extplt, hdr, prologdata, mode, mod) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".ext.plt insertion failed", -1); extplt = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_EXTPLT, NULL, NULL, NULL); if (extplt == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, ".ext.plt insertion failed", -1); file->secthash[ELFSH_SECTION_EXTPLT] = extplt; extplt->curend = elfsh_get_first_pltentsz(file); } /* Loop on .plt and inject 'old_symnam' symbols */ for (off = 0; off < plt->shdr->sh_size; off += entsz) { /* SPARC does not have ALTGOT */ if (FILE_IS_MIPS(file) || FILE_IS_ALPHA64(file) || FILE_IS_IA32(file)) diff = (uint32_t) altgot->shdr->sh_addr - got->shdr->sh_addr; else diff = 0; /* Special case for the first plt entry */ if (off == 0 && elfsh_altplt_firstent(enew, &off, symtab, file, extplt, plt, diff) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "ALTPLT on first entry failed", -1); else if (off == 0) continue; /* Get the existing symbol name for this plt entry ... */ sym = elfsh_get_sym_by_value(elfsh_readmem(dynsym), dynsym->shdr->sh_size / sizeof(elfsh_Sym), plt->shdr->sh_addr + off, NULL, ELFSH_EXACTSYM); /* New versions of ld do not fill the vaddr of dynamic symbols, do it ourself. Do not insert old symbol in emergency cases */ if (sym == NULL) { if ((sym = elfsh_restore_dynsym(file, plt, off, dynsym)) == NULL) continue; name = elfsh_get_dynsymbol_name(file, sym); /* __gmon_start__ should not be resolved if it was not already done by gcc */ if (name && !strcmp(name, "__gmon_start__")) sym->st_value = 0x0; } /* ... and we inject the 'old' occurence symbol pointing in .alt.plt (.plt on MIPS) */ if (!FILE_IS_MIPS(file)) addr = enew->shdr->sh_addr + off; else addr = plt->shdr->sh_addr + off; #if __BYTE_ORDER == __BIG_ENDIAN if (file->hdr->e_ident[EI_DATA] == ELFDATA2LSB) #elif __BYTE_ORDER == __LITTLE_ENDIAN if (file->hdr->e_ident[EI_DATA] == ELFDATA2MSB) #else #error Unexpected __BYTE_ORDER ! #endif addr = swaplong(addr); /* Injection */ name = elfsh_get_dynsymbol_name(file, sym); newsym = elfsh_create_symbol(addr, entsz, STT_FUNC, 0, 0, 0); snprintf(buf, BUFSIZ, "old_%s", name); if (elfsh_insert_symbol(symtab, &newsym, buf) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "old_* symbol injection failed", -1); #if __DEBUG_COPYPLT__ printf("[DEBUG_COPYPLT] Symbol at .plt + %u injected" " succesfully (%s) \n", off, buf); #endif /* On ALPHA, shift the relocation offset from .got to .alt.got to avoid hooks removing when calling back the original function. */ if (FILE_IS_ALPHA64(file) && elfsh_shift_alpha_relocs(file, name, altgot, off) < 0) continue; /* Reencode the PLT entry to use the alternative GOT */ /* This condition is for compatibility with other archs where EXTPLT is not yet supported. For those we do not enter the hook */ if (FILE_IS_IA32(file)) { diff = (eresi_Addr) altgot->shdr->sh_addr - got->shdr->sh_addr; elfsh_encodeplt(file, plt, diff, off); if (file->hdr->e_type == ET_DYN) elfsh_encodeplt(file, file->secthash[ELFSH_SECTION_ALTPLT], diff, off); diff = (eresi_Addr) altgot->shdr->sh_addr - got->shdr->sh_addr + got->shdr->sh_size; elfsh_encodeplt(file, extplt, diff, off); } } /* Activate ALTGOT */ if (elfsh_redirect_pltgot(file, altgot, got, plt, enew) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "PLTGOT redirection failed", -1); /* Activate EXTPLT */ if (elfsh_extplt_mirror_sections(file) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Section mirroring failed", -1); #if __DEBUG_COPYPLT__ printf("[DEBUG_COPYPLT] Section Mirrored Successfully ! \n"); #endif /* Everything is 0k4y */ if (elfsh_sync_sorted_symtab(symtab) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "symtab synchronisation failed", -1); if (elfsh_sync_sorted_symtab(dynsym) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "dynsym synchronisation failed", -1); elfsh_sync_sectnames(file); 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); }