/** * 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))); }
elfshsect_t *insert_symtab_sec(elfshobj_t *f, eresi_Addr numsym, eresi_Addr strtab) { char *syms; elfsh_Shdr *hdrsym; elfshsect_t *newsym; syms = (char *) calloc(numsym, sizeof(Elf64_Sym)); hdrsym = (elfsh_Shdr *) calloc(1, sizeof(elfsh_Shdr)); newsym = elfsh_create_section(".sym.p"); if (!newsym) { elfsh_error(); exit(-1); } /* Create a section header for the mapped section for a string table*/ *hdrsym = elfsh_create_shdr(0, SHT_SYMTAB, SHF_WRITE | SHF_ALLOC, 0, 0, numsym*sizeof(Elf64_Sym), strtab, 2, 8, sizeof(Elf64_Sym)); if (elfsh_insert_data_section(f, newsym, *hdrsym, syms) < 0) { elfsh_error(); exit(-1); } /* Retreive it again since the file offset and the vaddr may have been updated during insertion */ newsym = elfsh_get_section_by_name(f, ".sym.p", NULL, NULL, NULL); elfsh_set_section_type(newsym->shdr, SHT_DYNSYM); if (!newsym) { elfsh_error(); exit(-1); } return newsym; }
eresi_Addr ret0_offset(char *lib) { //148dc: 31 c0 xor %eax,%eax //148de: c3 retq // look at text segment, search for byte value c3 elfshsect_t *text, *init, *plt; eresi_Addr offset; unsigned char *data; eresi_Addr sz, i; unsigned char seq[3] = {0x31,0xc0,0xc3}; elfshobj_t *f; f = elfutils_read_elf_file(lib); if (NULL == f){ return -1; } text = elfsh_get_section_by_name(f,".text", NULL, NULL, NULL); if (NULL == text){ return -1; } data = (char *) text->data; sz = elfsh_get_section_size(text->shdr); for (i = 0; i < (sz-2); i++) { if ((seq[0] == data[i]) && (seq[1] == data[i+1]) && (seq[2] == data[i+2])) { return i + text->shdr->sh_addr; } } return 0; }
eresi_Addr lookup_libc_offset(char *libc, char *function) { elfshobj_t *f; elfsh_Rela *r; elfsh_Sym *s; int i; char *cmpname; f = elfutils_read_elf_file(libc); elfshsect_t *dynsym, *strtab; dynsym = elfsh_get_section_by_name(f,".dynsym", NULL, NULL, NULL); // lookup strtab strtab = elfsh_get_section_by_index(f, elfsh_get_section_link(dynsym->shdr), NULL, NULL); // get symbol elfsh_Sym *table; table = (elfsh_Sym *) dynsym->data; eresi_Addr numsym = elfsh_get_section_size(dynsym->shdr)/sizeof(elfsh_Sym); char *strs = strtab->data; for (i = 0; i < numsym; i++) { //look at each entry s = &(table[i]); cmpname = strs + s->st_name; if(strcmp(function, cmpname) == 0){ return s->st_value; } } return 0; }
/** * Tell elfsh to strip all unmapped sections * @param file * @return */ int elfsh_strip(elfshobj_t *file) { elfshsect_t *bss; elfshsect_t *next; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); bss = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_BSS, NULL, NULL, NULL); if (file == NULL || file->sectlist == NULL || bss == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameter", -1); while (bss->shdr->sh_addr) bss = bss->next; while (bss) { next = bss->next; if ((bss->index == 0) || ((bss->index != file->hdr->e_shstrndx) && elfsh_remove_section(file, bss->name))) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to remove section", -1); bss = next; } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Get comment section * @param file * @return */ elfshsect_t *elfsh_get_comments(elfshobj_t *file) { elfshsect_t *enew; int size; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (NULL == file) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL paramater", NULL); enew = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_COMMENT, NULL, NULL, &size); if (NULL == enew) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get .comment by name", NULL); if (NULL == elfsh_readmem(enew)) { enew->data = elfsh_load_section(file, enew->shdr); if (NULL == elfsh_readmem(enew)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to load .comment", NULL); } file->secthash[ELFSH_SECTION_COMMENT] = enew; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (enew)); }
/** * Return a pointer on ".reginfo" ri_gp_value * @param file * @return */ elfsh_Sword *elfsh_get_gpvalue_addr(elfshobj_t* file) { elfshsect_t *reginfo; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (file == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", NULL); reginfo = elfsh_get_section_by_name(file,".reginfo", NULL, NULL, NULL); if (reginfo == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "No .reginfo section", NULL); if (reginfo->data == NULL) reginfo->data = elfsh_load_section(file, reginfo->shdr); if (reginfo->data == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Can't read .reginfo section", NULL); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (&((elfsh_RegInfo *) elfsh_readmem(reginfo))->ri_gp_value)); }
/** * Relocate the object * @param file * @param rel * @param stage * @return */ int elfsh_relocate_object(elfshobj_t *file, elfshobj_t *rel, u_char stage) { elfshsect_t *sect; elfshsect_t *reltab; char sctname[BUFSIZ]; u_int index; u_int found; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Last pass : relocate each inserted section */ for (found = index = 0; index < rel->hdr->e_shnum; index++) { sect = elfsh_get_section_by_index(rel, index, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant get section in ET_REL", -1); /* Check if the section is mapped */ if (elfsh_get_section_allocflag(sect->shdr) && sect->shdr->sh_size && sect->shdr->sh_type == SHT_PROGBITS) { /* Find the associate relocation section */ snprintf(sctname, sizeof(sctname), "%s%s", (IS_REL(sect) ? ".rel" : ".rela"), sect->name); reltab = elfsh_get_section_by_name(rel, sctname, NULL, NULL, NULL); if (reltab == NULL) continue; found++; /* Find the injected instance of this allocatable section in the ET_EXEC */ snprintf(sctname, sizeof(sctname), "%s%s", sect->parent->name, sect->name); sect = elfsh_get_section_by_name(file, sctname, NULL, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant get section in ET_EXEC", -1); if (elfsh_relocate_etrel_section(sect, reltab, stage) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to relocate section", -1); } } /* Note that we might have done no relocation if no table was available */ /* This can happen on very simple .o files */ PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Inject a section from ET_REL object into ET_EXEC * @param file * @param sect * @param mod * @return */ static int elfsh_inject_etrel_section(elfshobj_t *file, elfshsect_t *sect, u_int mod) { elfsh_Shdr hdr; elfshsect_t *enew; char *newname; char writable; int mode; char *data; u_int modulo; elfshsect_t *plt; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (elfsh_dynamic_file(file) && NULL == (plt = elfsh_get_plt(file, NULL))) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get PLT", -1); /* else create a new section */ hdr = elfsh_create_shdr(0, sect->shdr->sh_type, sect->shdr->sh_flags, 0, 0, sect->shdr->sh_size, 0, 0, 0, 0); XALLOC(__FILE__, __FUNCTION__, __LINE__,newname, strlen(sect->parent->name) + strlen(sect->name) + 2, -1); sprintf(newname, "%s%s", sect->parent->name, sect->name); enew = elfsh_create_section(newname); /* Copy the data */ XALLOC(__FILE__, __FUNCTION__, __LINE__,data, sect->shdr->sh_size, -1); memcpy(data, sect->data, sect->shdr->sh_size); /* Inject new section by top or after bss depending on its type */ writable = elfsh_get_section_writableflag(sect->shdr); /* FreeBSD is incompatible with pre-interp injection */ ELFSH_SELECT_INJECTION(file,writable,mode); if (mode == ELFSH_DATA_INJECTION) modulo = sizeof(eresi_Addr); else { /* modulo = mod; (to be uncommented one day) */ //modulo = elfsh_get_pagesize(file); modulo = sizeof(eresi_Addr); } #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Mapping new section %s with data = %p \n", enew->name, data); #endif if (elfsh_insert_mapped_section(file, enew, hdr, data, mode, modulo) < 0) goto bad; enew = elfsh_get_section_by_name(file, newname, NULL, NULL, NULL); if (enew == NULL) goto bad; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); bad: XFREE(__FILE__, __FUNCTION__, __LINE__,newname); PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to inject ET_REL section", -1); }
void insert_exec_secs(elf_bf_exec_t *env) { elfshsect_t *sec, *str; sec = elfsh_get_section_by_name(env->ee_lm.lm_f, ".dynsym", NULL, NULL, NULL); //str = elfsh_get_section_by_name(env->ee_lm.lm_f, ".dynstr", NULL, NULL, NULL); env->ee_num_orig_syms = elfsh_get_section_size(sec->shdr)/sizeof(Elf64_Sym); env->ee_num_used_syms = env->ee_num_orig_syms; env->ee_lm.lm_sym = insert_symtab_sec(env->ee_lm.lm_f, env->ee_num_new_syms + env->ee_num_used_syms + env->ee_tape_len, elfsh_get_section_link(sec->shdr)); elfsh_set_section_type(sec->shdr, SHT_NULL); copy_dynsym(env); sec = elfsh_get_section_by_name(env->ee_lm.lm_f, ".rela.dyn", NULL, NULL, NULL); env->ee_lm.lm_reloc = insert_reloc_sec(env->ee_lm.lm_f, env->ee_num_reloc + (elfsh_get_section_size(sec->shdr)/sizeof(Elf64_Rela)), env->ee_lm.lm_sym); copy_dynrel(env); env->ee_lm.lm_allocated = 1; }
/** * @brief Change the DT_PLTGOT entry in the .dynamic section to change * the relocation base address. * @param file The host file. * @param altgot Section descriptor for the .elfsh.altgot section. * @param got Section descriptor for the .got section. * @param plt Section descriptor for the .plt section. * @param altplt Section descriptor for the .elfsh.altplt section. * @return Success (0) or Error (-1). */ int elfsh_redirect_pltgot(elfshobj_t *file, elfshsect_t *altgot, elfshsect_t *got, elfshsect_t *plt, elfshsect_t *altplt) { elfsh_Sym *sym; elfsh_Dyn *dyn; elfshsect_t *relplt; char *name; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Get the DT_PLTGOT entry in .dynamic */ dyn = elfsh_get_dynamic_entry_by_type(file, DT_PLTGOT); if (!dyn) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find DT_PLTGOT", -1); /* Get the PLT related relocation table */ name = IS_REL(plt) ? ELFSH_SECTION_NAME_RELPLT : ELFSH_SECTION_NAME_RELAPLT; relplt = elfsh_get_section_by_name(plt->parent, name, 0, 0, 0); if (!relplt) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find RELPLT section by name", -1); /* On MIPS we change it from .got to .alt.got : ALTGOT technique */ if (FILE_IS_MIPS(file) || FILE_IS_IA32(file)) { elfsh_set_dynentry_val(dyn, altgot->shdr->sh_addr); if (FILE_IS_MIPS(file)) { elfsh_set_gpvalue(file, altgot->shdr->sh_addr + 0x8000 - 0x10); sym = elfsh_get_dynsymbol_by_name(file, "_gp_disp"); if (sym == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Could not find _gp_disp ", -1); sym->st_value = altgot->shdr->sh_addr + 0x8000 - 0x10; elfsh_shift_mips_relocs(file, altgot->shdr->sh_addr - got->shdr->sh_addr); } else elfsh_shift_ia32_relocs(file, altgot->shdr->sh_addr - got->shdr->sh_addr, relplt, ELFSH_NOLIMIT); } /* On SPARC we change it from .plt to .alt.plt : ALTPLT technique */ else if (FILE_IS_SPARC(file)) { elfsh_set_dynentry_val(dyn, altplt->shdr->sh_addr); elfsh_shift_sparc_relocs(file, altplt->shdr->sh_addr - plt->shdr->sh_addr, relplt); } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
void copy_dynsym(elf_bf_exec_t *ee) { //get original symbol table elfshsect_t *dynsym; dynsym = elfsh_get_section_by_name(ee->ee_lm.lm_f, ".dynsym", NULL, NULL, NULL); // copy in dynsym symbols ee->ee_lm.lm_sym->data = memcpy(ee->ee_lm.lm_sym->data, dynsym->data, elfsh_get_section_size(dynsym->shdr)); }
void copy_dynrel(elf_bf_exec_t *ee) { // get a copy of the current reladyn section so we can // copy data at end elfshsect_t *dynrel, *newrel; dynrel = elfsh_get_section_by_name(ee->ee_lm.lm_f, ".rela.dyn", NULL, NULL, NULL); newrel = ee->ee_lm.lm_reloc; // copy original relocation entries at end memcpy((void *) (newrel->data) + (ee->ee_num_reloc*sizeof(Elf64_Rela)), dynrel->data, elfsh_get_section_size(dynrel->shdr)); }
void elfutils_setup_env(char *src, char *execf_in, char *execf_out, char *libc, int tape_len, eresi_Addr ifuncoffset, eresi_Addr dl_auxv, // offset of _dl_auxv (in ld.so's data) eresi_Addr endoffset, // offset of &end on stack from where auxv lives on stack (value of _dl_auxv) int debug, elf_bf_env_t *env) { elfsh_Dyn *dyn; // open and load exec env->e_bf_source = read_source(src); env->e_bf_sourcepath = src; env->e_exec.ee_ifunc_offset = ifuncoffset; env->e_exec.ee_dl_auxv = dl_auxv; env->e_exec.ee_end_offset = endoffset; env->e_exec.ee_for_debug = debug; env->e_bf_libc = libc; env->e_exec.ee_libc = libc; env->e_exec.ee_exec_path = execf_in; init_elf_bf_linkmap(&(env->e_exec.ee_lm), execf_in, execf_out); elfshsect_t *bs; //we will pretend this section is our string table bs = elfsh_get_section_by_name(env->e_exec.ee_lm.lm_f, ".bss", NULL, NULL, NULL); env->e_exec.ee_lm.lm_bss_index = bs->index; env->e_exec.ee_tape_len = tape_len; //env->e_exec.ee_reloc_end = exec_reloc_end; env->e_exec.ee_dt_rela = get_dynent_addr(env->e_exec.ee_lm.lm_f, DT_RELA); env->e_exec.ee_dt_relasz = get_dynent_addr(env->e_exec.ee_lm.lm_f, DT_RELASZ); env->e_exec.ee_dt_sym = get_dynent_addr(env->e_exec.ee_lm.lm_f, DT_SYMTAB); env->e_exec.ee_dt_jmprel = get_dynent_addr(env->e_exec.ee_lm.lm_f, DT_JMPREL); env->e_exec.ee_dt_pltrelsz = get_dynent_addr(env->e_exec.ee_lm.lm_f, DT_PLTRELSZ); env->e_exec.ee_ptr_tape_ptr = NULL; env->e_exec.ee_tape_ptr = NULL; env->e_exec.ee_ptr_tape_copy = NULL; env->e_exec.ee_lm.lm_ifunc = NULL; env->e_exec.ee_exec_map = NULL; env->e_exec.ee_ld_base = NULL; env->e_exec.ee_libc_base = NULL; env->e_exec.ee_stack_addr = NULL; env->e_exec.ee_lm.lm_getchar = NULL; env->e_exec.ee_lm.lm_putchar = NULL; env->e_exec.ee_lm.lm_putcharextra = NULL; env->e_exec.ee_exec_map_value = NULL; env->e_exec.ee_num_reloc = bf_rela_count(env); init_tape_syms(&(env->e_exec)); //first count number of new syms insert_exec_secs(&(env->e_exec)); init_tape_syms(&(env->e_exec)); fix_dynamic_table(&(env->e_exec)); }
int main(int argc, char **argv) { elfshobj_t *host; elfshobj_t *rel; elfshsect_t *txtsect; elfsh_Sym *puts_troj; elfsh_Sym *hook_func; int idx; u_long addr; /* Map host file and relocatable file */ rel = elfsh_map_obj(RELOC_FILE); if (NULL == rel) goto err; host = elfsh_map_obj(TROJANED_FILE); if (NULL == host) goto err; /* Inject etrel */ idx = elfsh_inject_etrel(host, rel); if (idx < 0) goto err; /* Get injected's section info */ txtsect = elfsh_get_section_by_name(host, RELOC_FILE".text", NULL, NULL, NULL); if (txtsect == NULL) goto err; puts_troj = elfsh_get_symbol_by_name(host, "puts_troj"); idx = elfsh_hijack_function_by_name(host, ELFSH_HIJACK_TYPE_PLT, "puts", puts_troj->st_value, NULL); if (idx < 0) goto err; hook_func = elfsh_get_symbol_by_name(host, "hook_func"); idx = elfsh_hijack_function_by_name(host, ELFSH_HIJACK_TYPE_FLOW, "legit_func", hook_func->st_value, NULL); if (idx < 0) goto err; /* Save it */ idx = elfsh_save_obj(host, OUTPUT_FILE); if (idx < 0) goto err; puts("[*] ET_REL injected"); return (0); err: elfsh_error(); return (-1); }
/** * Retreive strtab * @param file * @param index * @return */ elfshsect_t *elfsh_get_strtab(elfshobj_t *file, int index) { elfshsect_t *s; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (file == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", NULL); s = file->secthash[ELFSH_SECTION_STRTAB]; if (s) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, s); /* Read the string table */ if (index > 0) s = elfsh_get_section_by_index(file, index, NULL, NULL); else s = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_STRTAB, NULL, NULL, NULL); /* Section is present */ if (s != NULL) { file->secthash[ELFSH_SECTION_STRTAB] = s; s->shdr->sh_link = file->secthash[ELFSH_SECTION_SYMTAB]->index; if (s->data == NULL) { s->data = elfsh_load_section(file, s->shdr); if (s->data == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to load STRTAB", NULL); } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (s)); } /* Section is not present */ s = elfsh_rebuild_strtab(file); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (s)); }
/** * Add a symbol name in .dynstr * @param file * @param name * @return */ int elfsh_insert_in_dynstr(elfshobj_t *file, char *name) { elfshsect_t *sect; int ret; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (file == NULL || name == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); sect = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_DYNSTR, NULL, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to find DYNSTR by name", -1); ret = elfsh_append_data_to_section(sect, name, strlen(name) + 1); if (ret < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Failed to append data to dynstr", -1); else PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, ret); }
/** * Shift DTORS on ET_DYN * @param file * @param size * @return */ int elfsh_shift_dtors(elfshobj_t *file, u_int size) { elfshsect_t *dtors; int nbr; u_int idx; eresi_Addr *addr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); dtors = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_DTORS, NULL, NULL, &nbr); if (!dtors) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot retreive DTORS in ET_DYN", -1); nbr = nbr / sizeof(eresi_Addr); for (idx = 0; idx < nbr; idx++) { addr = elfsh_get_dtors_entry_by_index(dtors->data, idx); if (*addr && *addr != ELFSH_END_DTORS) *addr += size; } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (0)); }
/** * Read the destructor array in .dtors * @param file * @param num * @return */ eresi_Addr *elfsh_get_dtors(elfshobj_t *file, int *num) { elfshsect_t *enew; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (file == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", NULL); /* Load dtors */ enew = file->secthash[ELFSH_SECTION_DTORS]; if (enew == NULL) { enew = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_DTORS, NULL, NULL, NULL); if (NULL == enew) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get DTORS by name", NULL); } /* Read dtors */ if (NULL == enew->data) { enew->data = elfsh_load_section(file, enew->shdr); if (NULL == enew->data) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to load DTORS", NULL); file->secthash[ELFSH_SECTION_DTORS] = enew; } /* Return data */ if (num != NULL) *num = enew->shdr->sh_size / sizeof(eresi_Addr); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (elfsh_readmem(enew))); }
/** * Display the dynamic symbol table */ int cmd_dynsym() { elfshsect_t *sct; elfsh_Sym *dynsym; regex_t *tmp; int num; char logbuf[BUFSIZ]; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); dynsym = elfsh_get_dynsymtab(world.curjob->curfile, &num); if (dynsym == NULL) RET(-1); else { sct = elfsh_get_section_by_name(world.curjob->curfile, ELFSH_SECTION_NAME_ALTDYNSYM, NULL, NULL, &num); if (!sct) sct = elfsh_get_section_by_type(world.curjob->curfile, SHT_DYNSYM, 0, NULL, NULL, &num); if (!sct) RET(-1); num = num / sizeof(elfsh_Sym); } snprintf(logbuf, BUFSIZ - 1, " [DYNAMIC SYMBOL TABLE]\n [Object %s]\n [Section %s]\n", world.curjob->curfile->name, sct->name); revm_output(logbuf); FIRSTREGX(tmp); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, ds(world.curjob->curfile, sct, num, tmp, elfsh_get_dynsymbol_name)); }
elfshsect_t *insert_reloc_sec(elfshobj_t *f, eresi_Addr numrel, elfshsect_t *newsym) { char *rels; elfsh_Shdr *hdrrel; elfshsect_t *newrel; //alloc space for new sections rels = (char *) calloc(numrel, sizeof(Elf64_Rela)); hdrrel = (elfsh_Shdr *) calloc(1, sizeof(elfsh_Shdr)); /* Create the section descriptor (ESD) */ newrel = elfsh_create_section(".rela.p"); if (!newrel) { elfsh_error(); exit(-1); } /* Create a section header for the mapped section */ *hdrrel = elfsh_create_shdr(0, SHT_RELA, SHF_WRITE | SHF_ALLOC, 0, 0, numrel*sizeof(Elf64_Rela), newsym->index, 0, 8, sizeof(Elf64_Rela)); if (elfsh_insert_data_section(f, newrel, *hdrrel, rels) < 0) { elfsh_error(); exit(-1); } /* Retreive it again since the file offset and the vaddr may have been updated during insertion */ newrel = elfsh_get_section_by_name(f, ".rela.p", NULL, NULL, NULL); if (!newrel) { elfsh_error(); exit(-1); } return newrel; }
/** * Inject a ET_REL object into a ET_EXEC object * @param file * @param rel * @return */ int elfsh_inject_etrel(elfshobj_t *file, elfshobj_t *rel) { u_int mod; u_int pgsize; u_int index; elfshsect_t *sect; elfshsect_t *hooks; int ret = 0; static int depth = 0; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (file == NULL || file->hdr == NULL || rel == NULL || rel->hdr == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); if (rel->hdr->e_type != ET_REL || (file->hdr->e_type != ET_EXEC && file->hdr->e_type != ET_DYN)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Bad parameter types", -1); #if __DEBUG_RELADD__ if (rel->pending) { printf("[DEBUG_RELADD] BUG BUG BUG \n"); exit(0); } printf("[DEBUG_RELADD] INJECTING %s in %s (depth %d)\n", rel->name, file->name, depth++); #endif /* Set pending injection flag */ rel->pending = 1; /* If not already done */ elfsh_setup_hooks(); /* First physically insert all BSS in the file and fuse ** the module's BSS with the last one */ if (elfsh_fuse_bss(file, rel) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant fuze BSS sections", -1); } /* First pass : find and inject all allocatable sections */ for (index = 0; index < rel->hdr->e_shnum; index++) { /* Get the current section */ sect = elfsh_get_section_by_index(rel, index, NULL, NULL); if (sect == NULL) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant read section in ET_REL", -1); } /* Check if the current section need to be mapped */ if (elfsh_get_section_allocflag(sect->shdr) && sect->shdr->sh_size && sect->shdr->sh_type == SHT_PROGBITS) { mod = 0; if (elfsh_inject_etrel_section(file, sect, mod) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to inject ET_REL section", -1); } } } /* compute the inject modulo */ mod = elfsh_get_pagesize(file); //mod = sizeof(eresi_Addr); /* Do a copy of the procedure linkage table for eventual redirection */ if (!elfsh_static_file(file) && elfsh_copy_plt(file, mod) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to copy PLT", -1); } /* Create an additional hook table for non-plt function redirection */ hooks = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_HOOKS, 0, 0, 0); if (!hooks) { int mode; /* get injection mode */ ELFSH_SELECT_INJECTION(file,NULL,mode); pgsize = elfsh_get_pagesize(file); pgsize *= 4; /* We need a lot more than a page to trace big binaries like ssh */ hooks = elfsh_insert_section(file, ELFSH_SECTION_NAME_HOOKS, NULL, mode, pgsize - 1, pgsize); if (!hooks) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot inject .hooks", -1); } hooks->curend = 0; } #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Entering intermediate symbol injection loop\n"); #endif /* Intermediate pass 2 : Inject ET_REL symbol table into host file */ if (elfsh_fuse_etrel_symtab(file, rel) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to fuze symbol tables", -1); } #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Entering final relocation loop\n"); elfsh_print_sectlist(file, "before relocation"); #endif /* Now call the relocation on the object's sections */ ret = elfsh_relocate_object(file, rel, ELFSH_RELOC_STAGE1); rel->pending = 0; depth--; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (ret)); }
/** * 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); }
elfshsect_t *reloc_get_reloc_shsect(elfshobj_t *f, char *name) { return elfsh_get_section_by_name(f,name, NULL, NULL, NULL); }
/** * @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); }
/** * Relocate the just injected section * @param enew * @param reltab * @param stage * @return */ static int elfsh_relocate_etrel_section(elfshsect_t *enew, elfshsect_t *reltab, u_char stage) { elfsh_Rel *cur; volatile u_int index; elfsh_Sym *sym; volatile u_int size; eresi_Addr *dword; eresi_Addr addr; char *name; char tmpname[BUFSIZ]; elfshsect_t *sect; u_int entsz; elfshsect_t *plt; void *data; elfsh_Half symtype; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* ET_REL object is not mapped we use unconditionaly the ondisk relocation tables for such operation */ data = reltab->data; #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Using reloc table from %s [%s] data at %p \n", reltab->parent->name, reltab->name, data); #endif /* Loop on the relocation table entries */ size = (reltab->shdr->sh_type == SHT_RELA ? sizeof(elfsh_Rela) : sizeof(elfsh_Rel)); size = reltab->shdr->sh_size / size; plt = elfsh_get_plt(enew->parent, NULL); if (NULL == plt && elfsh_dynamic_file(enew->parent)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get plt", -1); entsz = elfsh_get_pltentsz(enew->parent); if (entsz < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get pltentsz", -1); for (index = 0; index < size; index++) { #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] relocation loop stage %u for section %s index %u \n", stage, enew->name, index); #endif /* We try a enew relocation now that the ET_REL dependence is mapped */ retry: /* Get symbol value in ET_REL */ cur = (reltab->shdr->sh_type == SHT_RELA ? (void *) (((elfsh_Rela *) data) + index) : (void *) (((elfsh_Rel *) data) + index)); sym = elfsh_get_symbol_from_reloc(reltab->parent, cur); name = elfsh_get_symname_from_reloc(reltab->parent, cur); if (sym == NULL || name == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to find symbol in ET_REL", -1); /* Grab a pointer on the dword that need to be relocated */ dword = (eresi_Addr *) ((char *) elfsh_readmem(enew) + cur->r_offset); /* ** If symbol type is NOTYPE, we use ET_EXEC symtab, else if ** symbol link is COMMON, we use ET_REL symbol inserted in ET_EXEC ** during BSS sizescan in bss.c:elfsh_find_bsslen() */ symtype = elfsh_get_symbol_type(sym); if (elfsh_get_symbol_bind(sym) != STB_LOCAL && /* patch BEOS */ (symtype == STT_NOTYPE || elfsh_get_symbol_link(sym) == SHN_COMMON)) { if (stage == ELFSH_RELOC_STAGE2 && !strstr(name, "old_")) continue; /* If the symbol is not found and we are still in the first stage relocation, just pass it */ sym = elfsh_get_metasym_by_name(enew->parent, name); if (!sym) { switch (elfsh_find_relocsym(enew, reltab, &sym, name, stage, symtype)) { case 2: #if __DEBUG_STATIC__ fprintf(stderr, "[DEBUG_STATIC] RETRY\n"); #endif goto retry; break; case 0: continue; case 1: break; case -1: default: PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to satisfy symbol in ET_REL", -1); } } addr = sym->st_value; #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Relocate using existing symbol %-20s " AFMT "]\n", name, (eresi_Addr) addr); #endif } /* Compute addr giving the injected section's vaddr in ET_EXEC */ else { /* All the following relocs are computed in stage 1 */ if (stage == ELFSH_RELOC_STAGE2) continue; /* Find target section in ET_REL */ sect = elfsh_get_section_by_index(reltab->parent, sym->st_shndx, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find extracted section in ET_REL", -1); #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Found -%s- section (idx = %u), now looking at " "injected base address\n", sect->name, sect->index); #endif /* Find corresponding inserted section in ET_EXEC */ snprintf(tmpname, sizeof(tmpname), "%s%s", reltab->parent->name, sect->name); sect = elfsh_get_section_by_name(enew->parent, tmpname, NULL, NULL, NULL); if (sect == NULL) { #if __DEBUG_RELADD__ elfsh_print_sectlist(reltab->parent, "HEH"); fprintf(stderr, "[DEBUG_RELADD] Did not found %s section (sym = %s) \n", tmpname, name); #endif PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find inserted section in ET_EXEC", -1); } /* Compute pointer value */ addr = sect->shdr->sh_addr; addr += ((elfsh_get_symbol_type(sym) == STT_SECTION && !FILE_IS_SPARC(sect->parent) && !FILE_IS_ALPHA64(sect->parent) && !FILE_IS_MIPS(sect->parent)) ? *dword : sym->st_value); #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Relocate using section %-20s base [-> " AFMT "] \n", sect->name, (eresi_Addr) addr); #endif } /* Perform relocation */ if (elfsh_relocate_entry(enew, cur, dword, addr, reltab) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to relocate entry", -1); } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Match a list of function from symbol tables * @param funcreg Function regex (or not ?) * @param func_list the final function list */ int trace_match_funcname(elfshobj_t *curfile, char *funcname, char ***func_list) { regex_t preg; char **f_list; u_int count = 0; elfshsect_t *sect; int num; elfsh_Sym *symtab; elfsh_Sym *sym; char funcreg[256]; char addrname[256]; size_t len; eresi_Addr addr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (!funcname || !func_list) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid parameters", -1); len = strlen(funcname); /* We don't want to strip some part of the submited function but if you find a function/regex of this size (for this purpose) ... */ if (len > 255) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Function name is too long", -1); /* An address ? */ if (IS_VADDR(funcname)) { /* Retrieve the address */ sscanf(funcname + 2, AFMT, &addr); /* Prealloc the list */ XALLOC(__FILE__, __FUNCTION__, __LINE__, f_list, sizeof(char*)*2, -1); sym = elfsh_get_symbol_by_value(curfile, addr, 0, ELFSH_EXACTSYM); /* We have a symbol for this address */ if (sym) { f_list[0] = elfsh_get_symbol_name(curfile, sym); f_list[1] = NULL; } else { sym = elfsh_get_dynsymbol_by_value(curfile, addr, 0, ELFSH_EXACTSYM); /* We have a dynamic symbol for this address */ if (sym) { f_list[0] = elfsh_get_dynsymbol_name(curfile, sym); f_list[1] = NULL; } else { TRACE_GET_FUNC_NAME(addrname, 255, funcname); f_list[0] = strdup(addrname); f_list[1] = NULL; } } goto end; } /* Add ^ and $ if needed, else we will check too many things For example, someone wanna add "main" function, if we don't add those symbols, it will match __libc_start_main which is very special function and that can create problems and make the tracer useless */ snprintf(funcreg, 255, "%s%s%s", funcname[0] != '^' ? "^" : "", funcname, funcname[len-1] != '$' ? "$" : ""); /* Do we have a regex ? */ if (regcomp(&preg, funcreg, 0) != 0) { XALLOC(__FILE__, __FUNCTION__, __LINE__, f_list, sizeof(char*)*2, -1); f_list[0] = funcname; f_list[1] = NULL; goto end; } /* Preallocation */ XALLOC(__FILE__, __FUNCTION__, __LINE__, f_list, sizeof(char*) * TRACE_MATCH_ALLOCSTEP, -1); /* Total match case */ if (TRACE_MATCH_ALL(funcname)) { /* Match everything we can, symbol or not ! */ trace_match_addrtable(curfile, &f_list, &count); } /** * Match on symbol table */ symtab = elfsh_get_symtab(curfile, &num); if (symtab != NULL) { sect = elfsh_get_section_by_type(curfile, SHT_SYMTAB, 0, NULL, NULL, 0); /* Match function regex in the symbol table */ trace_match_symtab(sect, num, &preg, &f_list, &count, elfsh_get_symbol_name); } /** * Match on dynamic symbol table */ symtab = elfsh_get_dynsymtab(curfile, &num); if (symtab != NULL) { sect = elfsh_get_section_by_name(curfile, ELFSH_SECTION_NAME_ALTDYNSYM, NULL, NULL, &num); if (!sect) sect = elfsh_get_section_by_type(curfile, SHT_DYNSYM, 0, NULL, NULL, &num); num /= sizeof(elfsh_Sym); /* Match function regex in the dynamic symbol table */ trace_match_symtab(sect, num, &preg, &f_list, &count, elfsh_get_dynsymbol_name); } /* Do we get something ? */ if (count == 0) { XFREE(__FILE__, __FUNCTION__, __LINE__, f_list); PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Can't match a single function", -1); } end: /* Set final pointer */ *func_list = f_list; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Static hooking for Mips32 * Note : a new technique is beeing considered that * may invalidate this handler as it (at least * change the way it should be implemented) * * Make sure to ask anything before deciding to implement it * * @param file * @param name * @param symbol * @param addr */ int elfsh_cflow_mips32(elfshobj_t *file, char *name, elfsh_Sym *symbol, eresi_Addr addr) { elfshsect_t *hooks; elfshsect_t *source; uint32_t buff[3]; int ret, len; int off; char *hookbuf; char *hook; //elfsh_Sym sym; char bufname[BUFSIZ]; void *data; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* func+0: [addu t9, (hook-func)] func+4: [jump hook] func+8: [nop] func+c: [???] hook: ... old_entry+0: [addu t9, (func-old_entry)] old_entry+4: [instr1] old_entry+8: [instr2] old_entry+c: [instr3] old_entry+10: [jmp func+8] old_entry+14: [nop] */ /* Resolve parameters */ off = elfsh_get_foffset_from_vaddr(file, symbol->st_value); if (!off) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid address to hijack", -1); ret = elfsh_readmemf(file, off, (void *) buff, 3*4); if (ret != 3*4) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Function too small to be hijacked", -1); /* If the hook section does not exist, create it */ hooks = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_HOOKS, 0, 0, 0); if (!hooks) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot get .HOOKS", -1); hook = (char *) (hooks->shdr->sh_addr + hooks->curend); if (((uint32_t) symbol->st_value & 0xf0000000) != (addr & 0xf0000000)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "hook function too far from hijacked function", -1); if (((uint32_t) hook & 0xf0000000) != ((symbol->st_value + 0x8) & 0xf0000000)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "hook section too far from hijacked function", -1); if ((addr - (uint32_t) symbol->st_value) & (0xffffffff<<16)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "hook function too far from hijacked function", -1); if (((uint32_t) symbol->st_value - (uint32_t) hook) & (0xffffffff<<16)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "hook section too far from hijacked function", -1); /* Determine the minimal aligned length */ /* RISC's powa */ /* 3 instructions : 1 add t9..., 1 jmp, 1 nop for delay slot */ ret = 3 * 4; /* Create the hook for this function */ data = elfsh_readmem(hooks); memset(data + hooks->curend, 0x00, 40); // nop /* addi $t, $s, imm : 0010 00ss ssst tttt iiii iiii iiii iiii */ *((uint32_t *) ((char *) (data + hooks->curend) + 0x0)) = 0x23390000; *((uint32_t *) ((char *) (data + hooks->curend) + 0x0)) |= (((uint32_t) symbol->st_value - (uint32_t)hook) & 0x0000ffff); /* first three hijacked function's instructions */ *((uint32_t *) ((char *) (data + hooks->curend) + 0x4)) = buff[0]; *((uint32_t *) ((char *) (data + hooks->curend) + 0x8)) = buff[1]; *((uint32_t *) ((char *) (data + hooks->curend) + 0xc)) = buff[2]; /* non-linked jump to func + 8 (where should be a NOP) */ /* mips32 jump use the 4 MSB of PC reg and 26 bits from instruction left shited by 2 */ memcpy(data + hooks->curend + 0x10, "\x08\x00\x00\x00", 4); *((uint32_t *) ((char *) (data + hooks->curend) + 0x10)) |= ((symbol->st_value + 0x8) & (~ 0xe0000000 ))>>2; /* NOTE : there must be a NOP after this last jump */ /* Insert the old symbol on the original saved bytes */ //name = elfsh_get_symbol_name(file, symbo); snprintf(bufname, BUFSIZ, "old_%s", name); elfsh_insert_funcsym(file, bufname, (eresi_Addr) hook, ret + 0x10, hooks->index); /* snprintf(bufname, BUFSIZ, "hook_%s", name); elfsh_insert_funcsym(file, bufname, addr, ret + 8, hooks->index); */ /* We need to grab the parent section to compute the remaining offset */ source = elfsh_get_parent_section_by_foffset(file, off, NULL); if (!source) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot find parent section for hooked addr", -1); /* Install the hook */ hookbuf = alloca(ret); /* patch t9 reg */ *((uint32_t *) ((char *) (hookbuf) + 0x0)) = 0x23390000; *((uint32_t *) ((char *) (hookbuf) + 0x0)) |= ((addr - symbol->st_value) & 0x0000ffff); /* jump to hook func */ *((uint32_t *) ((char *) (hookbuf) + 0x4)) = 0x08000000; *((uint32_t *) ((char *) (hookbuf) + 0x4)) |= ((uint32_t) (addr ) & (~0xe0000000))>>2; /* delay slot's NOP */ *((uint32_t *) ((char *) (hookbuf) + 0x8)) = 0x00000000; len = elfsh_writememf(file, off, hookbuf, ret); if (len != ret) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Error during hook installation", -1); /* Everything OK, ret is always 3*4 on mips32 (RISC strike again) */ hooks->curend += ret + 6; // (6 = 1 add, 3 instr, 1 jump, 1 nop) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Load linkmap * @param name * @return */ int e2dbg_linkmap_load(char *name) { static int done = 0; elfshsect_t *got; eresi_Addr *linkmap_entry; void *data; #if defined(sun) Link_map *actual; #else elfshlinkmap_t *actual; #endif char *gotname; char *ename; elfsh_Ehdr *hdr; u_int elftypenum; elfsh_Sym *endsym; char buff[64]; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (done) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg] Starting Loading LINKMAP !! \n"); #endif e2dbg_user_hooks_install(); revm_config(E2DBG_CONFIG); /* Load debugged file */ if (name) { /* No need to fill ET_EXEC base addr */ if (!revm_is_loaded(name) && revm_file_load(name, 0, NULL) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot load file", -1); #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] file %s loaded\n", name); #endif world.curjob->curfile->linkmap = E2DBG_DYNAMIC_LINKMAP; world.curjob->curfile->iotype = ELFSH_IOTYPE_EMBEDDED; world.curjob->curfile->running = 0; } #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] Before switch\n"); #endif /* Switch to obj 1 */ if (revm_doswitch(1) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot switch on object 1", -1); #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] After switch \n"); #endif /* Base address for PIE binaries have to be imported */ if (world.curjob->curfile->hdr->e_type == ET_DYN && !world.curjob->curfile->rhdr.base) { #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] Inside ET_DYN condition\n"); #endif endsym = elfsh_get_symbol_by_name(world.curjob->curfile, "_end"); fprintf(stderr, "endsym = " AFMT " \n", (eresi_Addr) endsym); sleep(1); #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] Filling PIE base" " (_end ondisk = " AFMT " / _end in memory = " AFMT ") ! \n", endsym->st_value, e2dbgworld.syms.piebase); #endif world.curjob->curfile->rhdr.base = e2dbgworld.syms.piebase - endsym->st_value; } /* Get ALTGOT or GOT if we used LD_PRELOAD */ if (!e2dbgworld.preloaded) { gotname = ELFSH_SECTION_NAME_ALTGOT; got = elfsh_get_section_by_name(world.curjob->curfile, gotname, NULL, NULL, NULL); } else got = elfsh_get_gotsct(world.curjob->curfile); #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] %s section at " XFMT "\n", got->name, got->shdr->sh_addr); fprintf(stderr, "[e2dbg_linkmap_load] BASE = %08x\n", world.curjob->curfile->rhdr.base); #endif /* Fix first file linkmap entry */ if (world.curjob->curfile->linkmap == E2DBG_DYNAMIC_LINKMAP) { /* Fix first file linkmap entry */ hdr = elfsh_get_hdr(world.curjob->curfile); if (!hdr) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot get ELF header", -1); elftypenum = elfsh_get_objtype(hdr); //fprintf(stderr, "[e2dbg_linkmap_load] after ELF header \n"); /* Get ALTGOT entry */ data = elfsh_readmem(got); //fprintf(stderr, "[e2dbg_linkmap_load] after get_raw (data = %08X) \n", data); linkmap_entry = elfsh_get_got_entry_by_index(data, 1); //fprintf(stderr, "[e2dbg_linkmap_load] after entry_by_index (linkmap_entry = %08x)\n", // linkmap_entry); #if defined(__FreeBSD__) || defined(__NetBSD__) world.curjob->curfile->linkmap = (elfshlinkmap_t *) &((Obj_Entry *) elfsh_get_got_val(linkmap_entry))->linkmap; #elif defined(sun) world.curjob->curfile->linkmap = e2dbgworld.syms.map; #else world.curjob->curfile->linkmap = (elfshlinkmap_t *) elfsh_get_got_val(linkmap_entry); #endif } #if __DEBUG_LINKMAP__ else fprintf(stderr, "[e2dbg_linkmap_load] Linkmap was -NOT- dynamic\n"); fprintf(stderr, "[e2dbg_linkmap_load] LINKMAP Found at " XFMT "\n", world.curjob->curfile->linkmap); #endif revm_doswitch(1); /* now load all linkmap's files */ for (actual = elfsh_linkmap_get_lprev(world.curjob->curfile->linkmap); actual != NULL; actual = elfsh_linkmap_get_lprev(actual)) { #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] Running on LINKMAP PREV " XFMT "\n", actual); #endif ename = elfsh_linkmap_get_lname(actual); if (ename && *ename && !revm_is_loaded(ename)) { if (revm_file_load(ename, elfsh_linkmap_get_laddr(actual), world.curjob->curfile->linkmap) < 0) e2dbg_output(" [EE] Loading failed"); world.curjob->curfile->iotype = ELFSH_IOTYPE_EMBEDDED; } } #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] Running on LINKMAP NEXT\n"); #endif for (actual = elfsh_linkmap_get_lnext(world.curjob->curfile->linkmap); actual != NULL; actual = elfsh_linkmap_get_lnext(actual)) { ename = elfsh_linkmap_get_lname(actual); #if __DEBUG_LINKMAP__ fprintf(stderr, "[e2dbg_linkmap_load] Running on LINKMAP NEXT " XFMT " (%s baseaddr %08X) \n", actual, ename, actual->laddr); #endif if (ename && *ename && !revm_is_loaded(ename)) { if (revm_file_load(ename, elfsh_linkmap_get_laddr(actual), world.curjob->curfile->linkmap) < 0) e2dbg_output(" [EE] Loading failed"); world.curjob->curfile->iotype = ELFSH_IOTYPE_EMBEDDED; } } /* Everything was OK */ e2dbg_output("\n"); //elfsh_set_runtime_mode(); revm_doswitch(1); snprintf(buff, sizeof(buff), " [*] Target PID = %u \n", getpid()); e2dbg_output(buff); done = 1; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }