/* OpenIDA - Outil d'analyse de fichiers binaires * e_elf.c - support du format ELF * * Copyright (C) 2008 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * OpenIDA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "e_elf.h" #include #include #include "elf-int.h" #include "section.h" #include "strings.h" #include "symbol.h" #include "../../panel/log.h" #include "../../common/extstr.h" #define _(str) str /* Indique le type d'architecture visée par le format. */ FormatTargetMachine get_elf_target_machine(const elf_format *); /* Fournit l'adresse mémoire du point d'entrée du programme. */ uint64_t get_elf_entry_point(const elf_format *); /* Récupère tous les éléments identifiées dans le binaire. */ size_t get_elf_resolved_items(const elf_format *, char ***, ResolvedType **, uint64_t **); /****************************************************************************** * * * Paramètres : content = contenu binaire à parcourir. * * length = taille du contenu en question. * * * * Description : Indique si le format peut être pris en charge ici. * * * * Retour : true si la réponse est positive, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool elf_is_matching(const uint8_t *content, off_t length) { bool result; /* Bilan à faire connaître */ result = false; if (length >= 4) result = (strncmp((const char *)content, "\x7f\x45\x4c\x46" /* .ELF */, 4) == 0); return result; } /****************************************************************************** * * * Paramètres : content = contenu binaire à parcourir. * * length = taille du contenu en question. * * * * Description : Prend en charge un nouvel ELF. * * * * Retour : Adresse de la structure mise en place ou NULL en cas d'échec.* * * * Remarques : - * * * ******************************************************************************/ elf_format *load_elf(const uint8_t *content, off_t length) { elf_format *result; /* Structure à retourner */ bool test; /* Bilan d'une initialisation */ Elf32_Half i; Elf32_Phdr phdr; size_t count; result = (elf_format *)calloc(1, sizeof(elf_format)); EXE_FORMAT(result)->content = content; EXE_FORMAT(result)->length = length; EXE_FORMAT(result)->get_target_machine = (get_target_machine_fc)get_elf_target_machine; EXE_FORMAT(result)->get_entry_point = (get_entry_point_fc)get_elf_entry_point; EXE_FORMAT(result)->get_def_parts = (get_def_parts_fc)get_elf_default_code_parts; EXE_FORMAT(result)->find_section = (find_section_fc)find_elf_section_content_by_name; EXE_FORMAT(result)->get_symbols = (get_symbols_fc)get_elf_symbols; EXE_FORMAT(result)->get_resolved = (get_resolved_fc)get_elf_resolved_items; EXE_FORMAT(result)->resolve_symbol = (resolve_symbol_fc)resolve_elf_symbol; EXE_FORMAT(result)->get_all_routines = (get_all_routines_fc)get_all_elf_routines; memcpy(&result->header, content, sizeof(Elf32_Ehdr)); /* TODO : endian */ /* Vérification des tailles d'entrée de table */ switch (result->header.e_ident[EI_CLASS]) { case ELFCLASS32: if (result->header.e_phentsize != sizeof(Elf32_Phdr)) { log_variadic_message(LMT_BAD_BINARY, _("Corrupted program header size (%hu); fixed !"), result->header.e_phentsize); result->header.e_phentsize = sizeof(Elf32_Phdr); } if (result->header.e_shentsize != sizeof(Elf32_Shdr)) { log_variadic_message(LMT_BAD_BINARY, _("Corrupted section header size (%hu); fixed !"), result->header.e_shentsize); result->header.e_shentsize = sizeof(Elf32_Shdr); } break; case ELFCLASS64: if (result->header.e_phentsize != sizeof(Elf64_Phdr)) { log_variadic_message(LMT_BAD_BINARY, _("Corrupted program header size (%hu); fixed !"), result->header.e_phentsize); result->header.e_phentsize = sizeof(Elf64_Phdr); } if (result->header.e_shentsize != sizeof(Elf64_Shdr)) { log_variadic_message(LMT_BAD_BINARY, _("Corrupted section header size (%hu); fixed !"), result->header.e_shentsize); result->header.e_shentsize = sizeof(Elf64_Shdr); } break; default: log_variadic_message(LMT_BAD_BINARY, ("Invalid ELF class '%hhu'"), result->header.e_ident[EI_CLASS]); break; } /* FIXME : à améliorer */ if ((result->header.e_shnum * result->header.e_shentsize) >= length) { log_variadic_message(LMT_BAD_BINARY, ("Suspicious section table (bigger than the binary !) ; reset !")); result->header.e_shnum = 0; } result->is_32b = (result->header.e_ident[EI_CLASS] == ELFCLASS32); for (i = 0; i < result->header.e_phnum; i++) { memcpy(&phdr, &content[result->header.e_phoff + i * result->header.e_phentsize], result->header.e_phentsize); printf(" seg [0x%08x] :: %d -> %d\n", phdr.p_type, phdr.p_offset, phdr.p_filesz); } test = read_elf_section_names(result); printf("section names ok ? %d\n", test); test = find_all_elf_strings(result); printf("strings ok ? %d\n", test); test = load_elf_symbols(result); printf("symbols ok ? %d\n", test); return result; lelf: /* TODO */ return NULL; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * * * Description : Indique le type d'architecture visée par le format. * * * * Retour : Identifiant de l'architecture ciblée par le format. * * * * Remarques : - * * * ******************************************************************************/ FormatTargetMachine get_elf_target_machine(const elf_format *format) { FormatTargetMachine result; /* Identifiant à retourner */ switch (format->header.e_machine) { case EM_MIPS: result = FTM_MIPS; break; case EM_386: result = FTM_386; break; default: /* FIXME */ break; } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * * * Description : Fournit l'adresse mémoire du point d'entrée du programme. * * * * Retour : Adresse de mémoire. * * * * Remarques : - * * * ******************************************************************************/ uint64_t get_elf_entry_point(const elf_format *format) { return format->header.e_entry; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * count = quantité de zones listées. [OUT] * * * * Description : Fournit les références aux zones de code à analyser. * * * * Retour : Zones de code à analyser. * * * * Remarques : - * * * ******************************************************************************/ bin_part **get_elf_default_code_parts(const elf_format *format, size_t *count) { bin_part **result; /* Tableau à retourner */ bin_part *part; /* Partie à intégrer à la liste*/ off_t offset; /* Position physique */ off_t size; /* Taille de la partie */ uint64_t voffset; /* Adresse virtuelle éventuelle*/ int i; /* Boucle de parcours */ Elf_Shdr shdr; /* En-tête de section ELF */ Elf_Phdr phdr; /* En-tête de programme ELF */ result = NULL; *count = 0; if (format->sec_size > 0) { if (find_elf_section_content_by_name(format, ".plt", &offset, &size, &voffset)) { part = create_bin_part(); set_bin_part_name(part, ".plt"); set_bin_part_values(part, offset, size, voffset); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } if (find_elf_section_content_by_name(format, ".MIPS.stubs", &offset, &size, &voffset)) { part = create_bin_part(); set_bin_part_name(part, ".MIPS.stubs"); set_bin_part_values(part, offset, size, voffset); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } if (find_elf_section_content_by_name(format, ".init", &offset, &size, &voffset)) { part = create_bin_part(); set_bin_part_name(part, ".init"); set_bin_part_values(part, offset, size, voffset); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } if (find_elf_section_content_by_name(format, ".text", &offset, &size, &voffset)) { part = create_bin_part(); set_bin_part_name(part, ".text"); set_bin_part_values(part, offset, size, voffset); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } if (find_elf_section_content_by_name(format, ".fini", &offset, &size, &voffset)) { part = create_bin_part(); set_bin_part_name(part, ".fini"); set_bin_part_values(part, offset, size, voffset); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } } /* Si aucune section n'a été trouvée... */ if (*count == 0) for (i = 0; i < format->header.e_shnum; i++) { offset = format->header.e_shoff + format->header.e_shentsize * i; if ((offset + format->header.e_shentsize) >= EXE_FORMAT(format)->length) continue; memcpy(&shdr, &EXE_FORMAT(format)->content[offset], format->header.e_shentsize); if (ELF_SHDR(format, &shdr, sh_flags) & SHF_EXECINSTR) { part = create_bin_part(); /* TODO : nom */ set_bin_part_values(part, ELF_SHDR(format, &shdr, sh_offset), ELF_SHDR(format, &shdr, sh_size), ELF_SHDR(format, &shdr, sh_addr)); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } } /* En désespoir de cause, on se rabbat sur les parties de programme directement */ if (*count == 0) for (i = 0; i < format->header.e_phnum; i++) { offset = format->header.e_phoff + format->header.e_phentsize * i; if ((offset + format->header.e_phentsize) >= EXE_FORMAT(format)->length) continue; memcpy(&phdr, &EXE_FORMAT(format)->content[offset], format->header.e_phentsize); if (ELF_PHDR(format, &phdr, p_flags) & PF_X) { part = create_bin_part(); /* TODO : nom */ set_bin_part_values(part, ELF_PHDR(format, &phdr, p_offset), ELF_PHDR(format, &phdr, p_filesz), ELF_PHDR(format, &phdr, p_vaddr)); result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *)); result[*count - 1] = part; } } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * labels = liste des commentaires à insérer. [OUT] * * types = type des symboles listés. [OUT] * * offsets = liste des indices des commentaires. [OUT] * * * * Description : Récupère tous les symboles présents dans le contenu binaire. * * * * Retour : Nombre d'éléments mis en place. * * * * Remarques : - * * * ******************************************************************************/ size_t get_elf_symbols(const elf_format *format, char ***labels, SymbolType **types, uint64_t **offsets) { size_t result; /* Quantité à retourner */ size_t i; /* Boucle de parcours */ result = format->sym_count; *labels = (char **)calloc(result, sizeof(char *)); *types = (SymbolType *)calloc(result, sizeof(SymbolType)); *offsets = (uint64_t *)calloc(result, sizeof(uint64_t)); for (i = 0; i < format->sym_count; i++) { (*labels)[i] = strdup(format->symbols[i].name); (*types)[i] = STP_SECTION; (*offsets)[i] = format->symbols[i].address; } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * labels = liste des commentaires à insérer. [OUT] * * types = type des symboles listés. [OUT] * * offsets = liste des indices des commentaires. [OUT] * * * * Description : Récupère tous les éléments identifiées dans le binaire. * * * * Retour : Nombre d'éléments mis en place. * * * * Remarques : - * * * ******************************************************************************/ size_t get_elf_resolved_items(const elf_format *format, char ***labels, ResolvedType **types, uint64_t **offsets) { size_t result; /* Quantité à retourner */ size_t i; /* Boucle de parcours */ size_t start; /* Point de départ du tour */ result = format->sym_count + format->str_count; *labels = (char **)calloc(result, sizeof(char *)); *types = (SymbolType *)calloc(result, sizeof(SymbolType)); *offsets = (uint64_t *)calloc(result, sizeof(uint64_t)); for (i = 0; i < format->sym_count; i++) { (*labels)[i] = strdup(format->symbols[i].name); (*types)[i] = RTP_SECTION; (*offsets)[i] = format->symbols[i].address; } start = format->sym_count; for (i = 0; i < format->str_count; i++) { (*labels)[start + i] = strndup(format->strings[i].value, format->strings[i].len); (*types)[start + i] = RTP_STRING; (*offsets)[start + i] = format->strings[i].address; (*labels)[start + i] = escape_crlf((*labels)[start + i]); } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * label = étiquette du symbole si trouvé. [OUT] * * type = type du symbole trouvé. [OUT] * * address = adresse à cibler, puis décallage final. [OUT] * * * * Description : Recherche le symbole correspondant à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool resolve_elf_symbol(const elf_format *format, char **label, SymbolType *type, vmpa_t *address) { bool result; /* Bilan à retourner */ size_t best_index; /* Meilleur symbole trouvé */ vmpa_t best_addr; /* Meilleure adresse trouvée */ vmpa_t addr; /* Adresse de routine */ size_t i; /* Boucle de parcours */ if (resolve_elf_strings(format, label, address)) { *type = STP_STRING; return true; } best_index = format->routines_count; /* Pour GCC */ best_addr = UINT64_MAX; /* FIXME */ for (i = 0; i < format->routines_count; i++) { addr = g_binary_routine_get_address(format->routines[i]); if (addr <= *address && (*address - addr) < best_addr) { best_index = i; best_addr = *address - addr; } } result = (best_addr != UINT64_MAX); if (result) { *label = strdup(g_binary_routine_get_name(format->routines[best_index])); *type = STP_SECTION; *address -= g_binary_routine_get_address(format->routines[best_index]); } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * count = taille du tableau créé. [OUT] * * * * Description : Fournit le prototype de toutes les routines détectées. * * * * Retour : Tableau créé ou NULL si aucun symbole de routine trouvé. * * * * Remarques : - * * * ******************************************************************************/ GBinRoutine **get_all_elf_routines(const elf_format *format, size_t *count) { *count = format->routines_count; return format->routines; }