char * version_needed::lookup_version(Elf32_Half ver) { current_entry = reinterpret_cast<Elf32_Verneed *>(ver_needed); while(current_entry != NULL) { if (current_entry->vn_aux == 0) current_aux_entry = NULL; else current_aux_entry = reinterpret_cast<Elf32_Vernaux *>(reinterpret_cast<char *>(current_entry) + convert_endian(4, current_entry->vn_aux, match_endian)); while (current_aux_entry != NULL) { if (get_cur_other() == ver) /* found! */ return get_cur_vername(); if (current_aux_entry->vna_next == 0) current_aux_entry = NULL; else current_aux_entry = reinterpret_cast<Elf32_Vernaux *>(reinterpret_cast<char *>(current_aux_entry) + convert_endian(4, current_aux_entry->vna_next, match_endian)); } if (current_entry->vn_next == 0) current_entry = NULL; else current_entry = reinterpret_cast<Elf32_Verneed *>(reinterpret_cast<char *>(current_entry) + convert_endian(4, current_entry->vn_next, match_endian)); } return NULL; }
void write_ld_first(char *args, t_size *p, int fd, t_lab *lab) { short int ind; int direct; if (args[0] == '%' && args[1] != ':') { args++; direct = my_getnbr(args); convert_endian(&direct, my_endian()); p->len += write(fd, &direct, sizeof(int)); } else if (args[0] != '%') { ind = my_getnbr(args); convert_short_endian(&ind, my_endian()); p->len += write(fd, &ind, sizeof(short int)); } else { direct = find_good_lab(lab, args) - p->size; convert_endian(&direct, my_endian()); p->len += write(fd, &direct, sizeof(int)); } }
Elf32_Word dynamic_relocations::read_info (unsigned int ndx) { if (ndx >= size) return 0; if (use == AC_USE_REL) return convert_endian(4, rel[ndx].r_info, match_endian); else if (use == AC_USE_RELA) return convert_endian(4, rela[ndx].r_info, match_endian); return 0; }
bool ac_rtld::detect_static_glibc(int fd, bool match_endian) { Elf32_Ehdr ehdr; Elf32_Shdr shdr; unsigned i; lseek(fd, 0, SEEK_SET); //Test if it's an ELF file if ((read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) || // read header (strncmp((char *)ehdr.e_ident, ELFMAG, 4) != 0) || // test elf magic number 0) { return false; } // first load the section name string table char *string_table = NULL; int shoff = convert_endian(4,ehdr.e_shoff, match_endian); short shndx = convert_endian(2,ehdr.e_shstrndx, match_endian); short shsize = convert_endian(2,ehdr.e_shentsize, match_endian); lseek(fd, shoff+(shndx*shsize), SEEK_SET); if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) { return false; } string_table = (char *) malloc(convert_endian(4,shdr.sh_size, match_endian)); lseek(fd, convert_endian(4,shdr.sh_offset, match_endian), SEEK_SET); if (read(fd, string_table, convert_endian(4,shdr.sh_size, match_endian)) != (signed)convert_endian(4,shdr.sh_size, match_endian)) { free (string_table); return false; } for (i=0; i<convert_endian(2,ehdr.e_shnum, match_endian); i++) { lseek(fd, shoff + shsize*i, SEEK_SET); if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) { free (string_table); return false; } if (!strncmp(string_table+convert_endian(4,shdr.sh_name, match_endian), "__libc", 6)) { this->glibc = true; free(string_table); return true; } } free(string_table); return false; }
char * version_definitions::get_cur_name(){ Elf32_Verdaux *aux; if (current_entry == NULL) return 0; if (current_entry->vd_aux == 0) return 0; aux = reinterpret_cast<Elf32_Verdaux *>(reinterpret_cast<char *>(current_entry) + current_entry->vd_aux); return (char *) strtab + convert_endian(4, aux->vda_name, match_endian); }
bool version_needed::set_entry (char * filename) { current_entry = reinterpret_cast<Elf32_Verneed *>(ver_needed); while(current_entry != NULL) { if (!strcmp(filename, get_cur_filename())) { current_aux_entry = reinterpret_cast<Elf32_Vernaux *>(reinterpret_cast<char *>(current_entry) + convert_endian(4, current_entry->vn_aux, match_endian)); return true; } if (current_entry->vn_next == 0) current_entry = NULL; else current_entry = reinterpret_cast<Elf32_Verneed *>(reinterpret_cast<char *>(current_entry) + convert_endian(4, current_entry->vn_next, match_endian)); } return false; }
bool version_definitions::set_entry(Elf32_Half index) { current_entry = reinterpret_cast<Elf32_Verdef *>(ver_def); while (current_entry != NULL) { if (convert_endian(2, current_entry->vd_ndx, match_endian) == index) return true; if (current_entry->vd_next == 0) current_entry = NULL; else current_entry = reinterpret_cast<Elf32_Verdef *>(reinterpret_cast<char *>(current_entry) + convert_endian(4, current_entry->vd_next, match_endian)); } return false; }
/* * Write archive to file */ static int output_archive(const char *path) { FILE *fp; convert_endian(); fp = fopen(path, "wb"); if (!fp) { fprintf(stderr, "Error: failed to open %s\n", path); fclose(fp); return -1; } if (fwrite(archive, sizeof(char), archive->size, fp) != archive->size) { fprintf(stderr, "Error: failed to write to %s\n", path); fclose(fp); return -1; } fclose(fp); printf("Wrote archive to %s\n", path); return 0; }
bool version_definitions::set_entry(char *vername, Elf32_Word hash) { current_entry = reinterpret_cast<Elf32_Verdef *>(ver_def); while (current_entry != NULL) { if (get_cur_hash() == hash) { if (!strcmp(get_cur_name(), vername)) return true; } if (current_entry->vd_next == 0) current_entry = NULL; else current_entry = reinterpret_cast<Elf32_Verdef *>(reinterpret_cast<char *>(current_entry) + convert_endian(4, current_entry->vd_next, match_endian)); } return false; }
Elf32_Sword dynamic_relocations::read_addend (unsigned int ndx) { if (use != AC_USE_RELA || ndx >= size) return 0; return convert_endian(4, rela[ndx].r_addend, match_endian); }
/* Get current aux entry hash value */ Elf32_Word version_needed::get_cur_hash() { if (current_aux_entry == NULL) return 0; return convert_endian(4, current_aux_entry->vna_hash, match_endian); }
bool version_needed::go_next_aux_entry() { if (current_aux_entry == NULL || current_aux_entry->vna_next == 0) return false; current_aux_entry = reinterpret_cast<Elf32_Vernaux *> (reinterpret_cast<char *>(current_aux_entry) + convert_endian(4, current_aux_entry->vna_next, match_endian)); return true; }
/* Get current aux entry other field */ Elf32_Half version_needed::get_cur_other() { if (current_aux_entry == NULL) return 0; return convert_endian(2, current_aux_entry->vna_other, match_endian); }
int process_elf(char* filename, hash_node **hashtable) { Elf32_Ehdr ehdr; Elf32_Shdr shdr; Elf32_Phdr phdr; int fd; unsigned int i, j; unsigned int match_endian; int shoff; /* Section header offset */ short shsize; /* Section header size */ short type; /* ELF file type */ char sections_patched = 0; char dynamic_found = FALSE; union _endian_test { char bytes[4]; unsigned int word; } endian_test; /* Open ELF file */ if (!filename || ((fd = open(filename, O_RDWR)) == -1)) { perror("Fatal error - open file"); return -1; } /* Test if it is an ELF file */ if ((read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) || // read header (strncmp((char *)ehdr.e_ident, ELFMAG, 4) != 0) || // test elf magic number 0) { close(fd); fprintf(stderr, "Failure opening file: Not an ELF file or magic number mismatches.\n"); exit(EXIT_FAILURE); } /* Our program is running little or big endian machine? */ endian_test.word = 1; if (endian_test.bytes[0] == 1) match_endian = 0; /* little */ else match_endian = 1; /* big */ /* Big or little? */ if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB) /* Big endian */ { match_endian = !(match_endian ^ 1); } else if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) /* Little endian */ { match_endian = !(match_endian ^ 0); } else { fprintf(stderr, "Warning! Can't determine if ELF file is little or big endian. Machine default assumed.\n"); } /* Testing file type */ type = convert_endian(2, ehdr.e_type, match_endian); if (type != ET_EXEC && type != ET_REL && type != ET_DYN) { close(fd); fprintf(stderr, "Can't patch this file: this ELF is not a relocatable file, executable or DSO.\n"); return ACRELCONVERT_FUNC_ERROR; } /* Trying to locate section header */ shoff = convert_endian(4,ehdr.e_shoff, match_endian); shsize = convert_endian(2,ehdr.e_shentsize, match_endian); if (shoff == 0 || shsize == 0) { unsigned int segment_type; short phnum = convert_endian(2, ehdr.e_phnum, match_endian); short phentsize = convert_endian(2, ehdr.e_phentsize, match_endian); int phoff = convert_endian(4, ehdr.e_phoff, match_endian); fprintf(stderr, "Warning: File's section header has been striped out. Can't locate ordinary relocation sections. We will still try patching dynamic relocations...\n"); if (type != ET_DYN) { fprintf(stderr, "Error: File is not a dynamic shared object, can't continue without section header.\n"); close(fd); return ACRELCONVERT_FUNC_ERROR; } if (phnum == 0 || phoff == 0) { fprintf(stderr, "Fatal error: Program headers are missing.\n"); close(fd); return ACRELCONVERT_FUNC_ERROR; } /* Iterates through all program headers and locate the DYNAMIC segment type */ for (i=0; i < phnum; i++) { /* No need to continue looping if the DYNAMIC segment was already found.*/ if (dynamic_found) break; lseek(fd, phoff + phentsize * i, SEEK_SET); if (read(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) { close(fd); fprintf(stderr, "Fatal error: Couldn't read program header.\n"); return ACRELCONVERT_FUNC_ERROR; } segment_type = convert_endian(4, phdr.p_type, match_endian); /* Found! */ if (segment_type == PT_DYNAMIC) { Elf32_Word p_filesz = convert_endian(4,phdr.p_filesz, match_endian), rel_size; Elf32_Off p_offset = convert_endian(4,phdr.p_offset, match_endian); unsigned int rel_offset; Elf32_Dyn * buffer = NULL; Elf32_Addr rel_addr; int rel_type; buffer = (Elf32_Dyn *) malloc(sizeof(char)*p_filesz); if (buffer == NULL) { close(fd); fprintf(stderr, "Fatal error in buffer dynamic allocation.\n"); return ACRELCONVERT_FUNC_ERROR; } lseek(fd, p_offset, SEEK_SET); if (read(fd, buffer, p_filesz) != p_filesz) { close(fd); free(buffer); fprintf(stderr, "Fatal error: Couldn't read DYNAMIC segment.\n"); return ACRELCONVERT_FUNC_ERROR; } rel_type = 0; rel_addr = 0; rel_size = 0; /* Iterates though all dynamic entries and finds REL/RELA records */ for (j = 0; j < (p_filesz / sizeof(Elf32_Dyn)); j++) { switch (convert_endian(4,buffer[j].d_tag, match_endian)) { case DT_REL: case DT_RELA: rel_type = convert_endian(4, buffer[j].d_tag, match_endian); rel_addr = convert_endian(4, buffer[j].d_un.d_ptr, match_endian); break; case DT_RELSZ: case DT_RELASZ: rel_size = convert_endian(4, buffer[j].d_un.d_val, match_endian); break; default: break; } } free(buffer); /* If any info is missing, terminate */ if (rel_type == 0 || rel_addr == 0 || rel_size == 0) { close(fd); fprintf(stderr, "Could not locate a dynamic relocation section in this DSO.\n"); return ACRELCONVERT_FUNC_ERROR; } rel_offset = 0; /* Identify where, in the file, these relocations are (rel_offset) */ for (j=0; j < phnum; j++) { lseek(fd, phoff + phentsize * i, SEEK_SET); if (read(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) { close(fd); fprintf(stderr, "Fatal error: Couldn't read program header.\n"); return ACRELCONVERT_FUNC_ERROR; } segment_type = convert_endian(4, phdr.p_type, match_endian); if (segment_type == PT_LOAD) { Elf32_Addr p_vaddr = convert_endian(4,phdr.p_vaddr, match_endian); Elf32_Word p_filesz = convert_endian(4,phdr.p_filesz, match_endian); Elf32_Off p_offset = convert_endian(4,phdr.p_offset, match_endian); /* Does this segment contains our dynrelocs section? */ if (p_vaddr <= rel_addr && p_vaddr + p_filesz >= rel_addr + rel_size) { rel_offset = p_offset + (rel_addr - p_vaddr); } } } /* Not found */ if (rel_offset == 0) { close(fd); fprintf(stderr, "Could not locate the dynamic relocation section in this DSO.\n"); return ACRELCONVERT_FUNC_ERROR; } /* Now we have rel_offset, rel_type and rel_size. Start patching relocations */ dynamic_found = TRUE; lseek(fd, rel_offset, SEEK_SET); if (patch_relocation_section(fd, rel_size, rel_type, hashtable, match_endian) == ACRELCONVERT_FUNC_ERROR) { close(fd); fprintf(stderr, "Failed patching relocation codes at dynrelocs section.\n"); return ACRELCONVERT_FUNC_ERROR; } } } if (!dynamic_found) { close(fd); fprintf(stderr, "Fatal error: Couldn't find any DYNAMIC segment in this DSO, preventing us from locating dynamic relocations section without a section headers table.\n"); return ACRELCONVERT_FUNC_ERROR; } } else /* We do have section headers information. */ { int shoff = convert_endian(4,ehdr.e_shoff, match_endian); short shsize = convert_endian(2,ehdr.e_shentsize, match_endian); /* Iterates though all sections. Patches all REL or RELA section types. */ for (i=0; i<convert_endian(2,ehdr.e_shnum, match_endian); i++) { Elf32_Word shtype; int rel_type, rel_size; lseek(fd, shoff + shsize*i, SEEK_SET); if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) { fprintf(stderr, "Error while trying to read section header.\n"); close(fd); return ACRELCONVERT_FUNC_ERROR; } shtype = convert_endian(4, shdr.sh_type, match_endian); if (shtype == SHT_REL || shtype == SHT_RELA) { if (shtype == SHT_REL) rel_type = DT_REL; else rel_type = DT_RELA; rel_size = convert_endian(4, shdr.sh_size, match_endian); lseek(fd, convert_endian(4, shdr.sh_offset, match_endian), SEEK_SET); if (patch_relocation_section(fd, rel_size, rel_type, hashtable, match_endian) == ACRELCONVERT_FUNC_ERROR) { close(fd); fprintf(stderr, "Failed patching relocation codes at section number %d.\n", i); return ACRELCONVERT_FUNC_ERROR; } sections_patched++; } } } if (!sections_patched) { if (!dynamic_found) printf("\nNo relocation sections found. File has not been changed.\n"); } else { printf("\nDone patching %d sections.\n", sections_patched); } /* Close file */ close(fd); return ACRELCONVERT_FUNC_OK; }
int patch_relocation_section(unsigned int fd, unsigned int size, int rel_type, hash_node **hashtable, unsigned int match_endian) { unsigned int i; unsigned int entry_size = (rel_type == DT_REL) ? sizeof(Elf32_Rel) : sizeof(Elf32_Rela); unsigned int total = size / entry_size; if (rel_type == DT_REL) /* rel relocation type*/ { Elf32_Rel rel; unsigned int new_code, old_code, sym; for (i = 0; i < total; i++) { /* Read it. */ if (read(fd, &rel, sizeof(rel)) != sizeof(rel)) { fprintf(stderr, "Couldn't read REL relocation number %d.\n", i); return ACRELCONVERT_FUNC_ERROR; } /* Patch it. */ old_code = ELF32_R_TYPE(convert_endian(4, rel.r_info, match_endian)); sym = ELF32_R_SYM(convert_endian(4, rel.r_info, match_endian)); if (hash_get_value(hashtable, old_code, &new_code) == ACRELCONVERT_FUNC_ERROR) { fprintf(stderr, "Error: unrecognized relocation code 0x%X (%d), at relocation number %d\n", old_code, old_code, i); return ACRELCONVERT_FUNC_ERROR; } rel.r_info = convert_endian(4, ELF32_R_INFO(sym, new_code), match_endian); /* Write it back, patched. */ lseek(fd, - sizeof(rel), SEEK_CUR); if (write(fd, &rel, sizeof(rel)) != sizeof(rel)) { fprintf(stderr, "Error: couldn't write REL relocation number %d.\n", i); return ACRELCONVERT_FUNC_ERROR; } } } else /* rela relocation type */ { Elf32_Rela rela; unsigned int new_code, old_code, sym; for (i = 0; i < total; i++) { /* Read it. */ if (read(fd, &rela, sizeof(rela)) != sizeof(rela)) { fprintf(stderr, "Couldn't read RELA relocation number %d.\n", i); return ACRELCONVERT_FUNC_ERROR; } /* Patch it. */ old_code = ELF32_R_TYPE(convert_endian(4, rela.r_info, match_endian)); sym = ELF32_R_SYM(convert_endian(4, rela.r_info, match_endian)); if (hash_get_value(hashtable, old_code, &new_code) == ACRELCONVERT_FUNC_ERROR) { fprintf(stderr, "Error: unrecognized relocation code 0x%X (%d), at relocation number %d\n", old_code, old_code, i); return ACRELCONVERT_FUNC_ERROR; } rela.r_info = convert_endian(4, ELF32_R_INFO(sym, new_code), match_endian); /* Write it back, patched. */ lseek(fd, - sizeof(rela), SEEK_CUR); if (write(fd, &rela, sizeof(rela)) != sizeof(rela)) { fprintf(stderr, "Error: couldn't write RELA relocation number %d.\n", i); return ACRELCONVERT_FUNC_ERROR; } } } return ACRELCONVERT_FUNC_OK; }
Elf32_Word version_definitions::get_cur_hash() { if (current_entry == NULL) return 0; return convert_endian(4, current_entry->vd_hash, match_endian); }
Elf32_Half version_definitions::get_cur_flags() { if (current_entry == NULL) return 0; return convert_endian(4, current_entry->vd_flags, match_endian); }
/* Return the filename (of current entry) */ char * version_needed::get_cur_filename() { if (current_entry == NULL) return 0; return (char *) strtab + convert_endian(4, current_entry->vn_file, match_endian); }
bool link_node::link_node_setup(Elf32_Addr dynaddr, unsigned char *mem, Elf32_Addr l_addr, unsigned int t, unsigned char *name, version_needed *verneed, bool match_endian) { Elf32_Addr hashaddr = 0, symaddr= 0, straddr = 0, reladdr = 0, verneed_addr = 0, verdef_addr = 0, versym_addr = 0, init_addr = 0, init_addr_array = 0, init_addr_arraysz = 0, fini_addr = 0, fini_addr_array = 0, fini_addr_arraysz = 0; Elf32_Word pltrel = 0, relsize = 0; unsigned int reltype = 0; bool is_linux_runtime_linker = false; this->match_endian = match_endian; load_addr = l_addr; type = t; soname = name; this->mem = mem; dyn_info.load_dynamic_info(dynaddr, mem, match_endian); hashaddr = dyn_info.get_value(DT_HASH); symaddr = dyn_info.get_value(DT_SYMTAB); straddr = dyn_info.get_value(DT_STRTAB); verneed_addr = dyn_info.get_value(DT_VERNEED); verdef_addr = dyn_info.get_value(DT_VERDEF); versym_addr = dyn_info.get_value(DT_VERSYM); init_addr = dyn_info.get_value(DT_INIT); init_addr_array = dyn_info.get_value(DT_INIT_ARRAY); init_addr_arraysz = dyn_info.get_value(DT_INIT_ARRAYSZ); fini_addr = dyn_info.get_value(DT_FINI); fini_addr_array = dyn_info.get_value(DT_FINI_ARRAY); fini_addr_arraysz = dyn_info.get_value(DT_FINI_ARRAYSZ); if ( dyn_info.compare_library_soname ((char *)soname, get_program_interpreter()) == 0) { is_linux_runtime_linker = true; } if (hashaddr) hashaddr += l_addr; if (symaddr) symaddr += l_addr; if (straddr) straddr += l_addr; if (verneed_addr) verneed_addr += l_addr; if (verdef_addr) verdef_addr += l_addr; if (versym_addr) versym_addr += l_addr; /* Configure init/fini addresses (avoid including the program interpreter) */ if (!is_linux_runtime_linker && init_addr) { init_addr += l_addr; add_to_start_vector(init_addr); } if (!is_linux_runtime_linker && init_addr_arraysz && init_addr_array) { init_addr_array += l_addr; for (unsigned i = 0; i < init_addr_arraysz; i+= sizeof(Elf32_Addr)) { Elf32_Addr tmp = *(reinterpret_cast<Elf32_Addr *>(mem + init_addr_array + i)); tmp = convert_endian(sizeof(Elf32_Addr), tmp, match_endian); add_to_start_vector((unsigned) (tmp + l_addr)); } } if (!is_linux_runtime_linker && fini_addr) { fini_addr += l_addr; add_to_fini_vector(fini_addr); } if (!is_linux_runtime_linker && fini_addr_arraysz && fini_addr_array) { fini_addr_array += l_addr; for (unsigned i = 0; i < fini_addr_arraysz; i+= sizeof(Elf32_Addr)) { Elf32_Addr tmp = *(reinterpret_cast<Elf32_Addr *>(mem + fini_addr_array + i)); tmp = convert_endian(sizeof(Elf32_Addr), tmp, match_endian); add_to_fini_vector((unsigned) (tmp + l_addr)); } } /* Set back the adjusted value because dyn_info uses it when extracting needed libraries names. */ dyn_info.set_value(DT_STRTAB, straddr); dyn_table.setup_hash(mem, hashaddr, symaddr, straddr, verdef_addr, verneed_addr, versym_addr, match_endian); pltrel = dyn_info.get_value(DT_PLTREL); if (pltrel == 0) /* file has no relocations */ { has_relocations = 0; } else { has_relocations = 1; reltype = (pltrel == DT_REL)? AC_USE_REL : AC_USE_RELA; reladdr = l_addr + dyn_info.get_value(pltrel); /* DT_REL or DT_RELA */ if (reladdr == 0) /* No data relocations */ reladdr = l_addr = dyn_info.get_value(DT_JMPREL); relsize = dyn_info.get_value(pltrel + 1); /* DT_RELSZ or DT_RELASZ*/ relsize += dyn_info.get_value(DT_PLTRELSZ); relsize /= (pltrel == DT_REL)? 8:12; dyn_relocs.setup(reladdr, relsize, mem, reltype, match_endian); } if (verneed != NULL) { /* we have special needs for this library*/ if (verneed->set_entry((char *)name) == true) { /* does not have version information, this is not the desired library */ if (!verdef_addr) return false; do /* For each verneeded aux entry, see if this library has the desired version tag */ { if (dyn_table.get_verdefs()->set_entry(verneed->get_cur_vername(), verneed->get_cur_hash()) == false) { /* We don't have this version definition, see if it is really necessary (not a weak def) */ if (!(verneed->get_cur_flags() & VER_FLG_WEAK)) return false; /* Library is old, reject it */ } } while (verneed->go_next_aux_entry()); } } /* Library accepted */ return true; }
/* Return the number of associated aux entries (of current entry) */ Elf32_Half version_needed::get_cur_cnt() { if (current_entry == NULL) return 0; return convert_endian(2, current_entry->vn_cnt, match_endian); }