/* OpenIDA - Outil d'analyse de fichiers binaires * elf.c - support du format ELF * * Copyright (C) 2009-2012 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 "elf.h" #include #include #include #include #include #include "elf-int.h" #include "program.h" #include "section.h" #include "strings.h" #include "symbols.h" #include "../../gui/panels/log.h" /* Taille maximale d'une description */ #define MAX_PORTION_DESC 256 /* Initialise la classe des formats d'exécutables ELF. */ static void g_elf_format_class_init(GElfFormatClass *); /* Initialise une instance de format d'exécutable ELF. */ static void g_elf_format_init(GElfFormat *); /* Indique le type d'architecture visée par le format. */ static FormatTargetMachine g_elf_format_get_target_machine(const GElfFormat *); /* Fournit l'adresse mémoire du point d'entrée du programme. */ static vmpa_t g_elf_format_get_entry_point(const GElfFormat *); /* Etend la définition des portions au sein d'un binaire. */ static void g_elf_format_refine_portions(const GElfFormat *, GBinPortion *); /* Fournit les références aux zones binaires à analyser. */ static GBinPart **g_elf_format_get_parts(const GElfFormat *, size_t *); /* Fournit la position correspondant à une adresse virtuelle. */ static bool g_elf_format_translate_address_into_offset(const GElfFormat *, vmpa_t, off_t *); /* Fournit l'adresse virtuelle correspondant à une position. */ static bool g_elf_format_translate_offset_into_address(const GElfFormat *, off_t, vmpa_t *); /****************************************************************************** * * * Paramètres : type = type de format recherché. * * 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(FormatType type, const bin_t *content, off_t length) { bool result; /* Bilan à faire connaître */ result = false; if (length >= 4) result = (memcmp(content, "\x7f\x45\x4c\x46" /* .ELF */, 4) == 0); return result; } /* Indique le type défini pour un format d'exécutable ELF. */ G_DEFINE_TYPE(GElfFormat, g_elf_format, G_TYPE_EXE_FORMAT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des formats d'exécutables ELF. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_format_class_init(GElfFormatClass *klass) { } /****************************************************************************** * * * Paramètres : format = instance à initialiser. * * * * Description : Initialise une instance de format d'exécutable ELF. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_format_init(GElfFormat *format) { GExeFormat *exe_format; /* Format parent à constituer */ exe_format = G_EXE_FORMAT(format); exe_format->get_machine = (get_target_machine_fc)g_elf_format_get_target_machine; exe_format->get_entry_point = (get_entry_point_fc)g_elf_format_get_entry_point; exe_format->refine_portions = (refine_portions_fc)g_elf_format_refine_portions; exe_format->get_parts = (get_parts_fc)g_elf_format_get_parts; exe_format->translate_addr = (translate_addr_fc)g_elf_format_translate_address_into_offset; exe_format->translate_off = (translate_off_fc)g_elf_format_translate_offset_into_address; } /****************************************************************************** * * * Paramètres : content = contenu binaire à parcourir. * * length = taille du contenu en question. * * * * Description : Prend en charge un nouveau format ELF. * * * * Retour : Adresse de la structure mise en place ou NULL en cas d'échec.* * * * Remarques : - * * * ******************************************************************************/ GBinFormat *g_elf_format_new(const bin_t *content, off_t length) { GElfFormat *result; /* Structure à retourner */ result = g_object_new(G_TYPE_ELF_FORMAT, NULL); g_binary_format_set_content(G_BIN_FORMAT(result), content, length); if (!read_elf_header(content, length, &result->header, &result->is_32b, &result->endian)) { /* TODO */ return NULL; } /* Vérification des tailles d'entrée de table */ if (ELF_HDR(result, result->header, e_phentsize) != ELF_SIZEOF_PHDR(result)) { log_variadic_message(LMT_BAD_BINARY, _("Corrupted program header size (%hu); fixed ! -- replacing 0x%04hx by 0x%04hx at offset 0x%x"), ELF_HDR(result, result->header, e_phentsize), ELF_HDR(result, result->header, e_phentsize), ELF_SIZEOF_PHDR(result), ELF_HDR_OFFSET_OF(result, e_phentsize)); ELF_HDR_SET(result, result->header, e_phentsize, ELF_SIZEOF_PHDR(result)); } if (ELF_HDR(result, result->header, e_shentsize) != ELF_SIZEOF_SHDR(result)) { log_variadic_message(LMT_BAD_BINARY, _("Corrupted section header size (%hu); fixed ! -- replacing 0x%04hx by 0x%04hx at offset 0x%x"), ELF_HDR(result, result->header, e_shentsize), ELF_HDR(result, result->header, e_shentsize), ELF_SIZEOF_SHDR(result), ELF_HDR_OFFSET_OF(result, e_shentsize)); ELF_HDR_SET(result, result->header, e_shentsize, ELF_SIZEOF_SHDR(result)); } /* FIXME : à améliorer */ if ((ELF_HDR(result, result->header, e_shnum) * ELF_HDR(result, result->header, e_shentsize)) >= length) { log_variadic_message(LMT_BAD_BINARY, ("Suspicious section table (bigger than the binary !) ; reset ! -- replacing 0x%04hx by 0x%04hx at offset 0x%x"), ELF_HDR(result, result->header, e_shnum), 0, ELF_HDR_OFFSET_OF(result, e_shnum)); ELF_HDR_SET(result, result->header, e_shnum, 0); } if (!load_elf_symbols(result)) { /* TODO */ return NULL; } if (!find_all_elf_strings(result)) { /* TODO */ return NULL; } return G_BIN_FORMAT(result); } /****************************************************************************** * * * 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 : - * * * ******************************************************************************/ static FormatTargetMachine g_elf_format_get_target_machine(const GElfFormat *format) { FormatTargetMachine result; /* Identifiant à retourner */ switch (ELF_HDR(format, format->header, e_machine)) { case EM_386: result = FTM_386; break; case EM_MIPS: result = FTM_MIPS; break; case EM_ARM: result = FTM_ARM; break; case EM_NONE: default: result = FTM_NONE; 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 : - * * * ******************************************************************************/ static vmpa_t g_elf_format_get_entry_point(const GElfFormat *format) { return ELF_HDR(format, format->header, e_entry); } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * raw = portion de binaire brut à raffiner. * * * * Description : Etend la définition des portions au sein d'un binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_format_refine_portions(const GElfFormat *format, GBinPortion *raw) { uint16_t i; /* Boucle de parcours */ off_t offset; /* Début de part de programme */ elf_phdr phdr; /* En-tête de programme ELF */ uint32_t p_flags; /* Droits associés à une partie*/ const char *background; /* Fond signigicatif */ GBinPortion *new; /* Nouvelle portion définie */ char desc[MAX_PORTION_DESC]; /* Description d'une portion */ PortionAccessRights rights; /* Droits d'une portion */ elf_shdr strings; /* Section des descriptions */ bool has_strings; /* Section trouvée ? */ elf_shdr section; /* En-tête de section ELF */ uint64_t sh_flags; /* Droits associés à une partie*/ const char *name; /* Nom trouvé ou NULL */ /* Côté segments basiques */ #if 0 for (i = 0; i < ELF_HDR(format, format->header, e_phnum); i++) { offset = ELF_HDR(format, format->header, e_phoff) + ELF_HDR(format, format->header, e_phentsize) * i; if (!read_elf_program_header(format, &offset, &phdr)) continue; p_flags = ELF_PHDR(format, phdr, p_flags); if (p_flags & PF_X) background = BPC_CODE; else if (p_flags & PF_W) background = BPC_DATA; else background = BPC_DATA_RO; new = g_binary_portion_new(background); sprintf(desc, "%s %s", _("Segment"), get_elf_program_type_desc(ELF_PHDR(format, phdr, p_type))); g_binary_portion_set_desc(new, desc); g_binary_portion_set_values(new, ELF_PHDR(format, phdr, p_offset), ELF_PHDR(format, phdr, p_filesz), ELF_PHDR(format, phdr, p_vaddr)); rights = PAC_NONE; if (p_flags & PF_R) rights |= PAC_READ; if (p_flags & PF_W) rights |= PAC_WRITE; if (p_flags & PF_X) rights |= PAC_EXEC; g_binary_portion_set_rights(new, rights); g_binary_portion_include(raw, new); } #endif /* Inclusion des sections, si possible... */ has_strings = find_elf_section_by_index(format, ELF_HDR(format, format->header, e_shstrndx), &strings); for (i = 0; i < ELF_HDR(format, format->header, e_shnum); i++) { if (!find_elf_section_by_index(format, i, §ion)) continue; sh_flags = ELF_SHDR(format, section, sh_flags); if (sh_flags & SHF_EXECINSTR) background = BPC_CODE; else if (sh_flags & SHF_WRITE) background = BPC_DATA; else background = BPC_DATA_RO; new = g_binary_portion_new(background); if (has_strings) name = extract_name_from_elf_string_section(format, &strings, ELF_SHDR(format, section, sh_name)); else name = NULL; if (name != NULL) sprintf(desc, "%s %s", _("Section"), name); else sprintf(desc, "%s ???", _("Section")); g_binary_portion_set_desc(new, desc); rights = PAC_NONE; if (sh_flags & SHF_ALLOC) rights |= PAC_READ; if (sh_flags & SHF_WRITE) rights |= PAC_WRITE; if (sh_flags & SHF_EXECINSTR) rights |= PAC_EXEC; g_binary_portion_set_rights(new, rights); g_binary_portion_set_values(new, ELF_SHDR(format, section, sh_offset), ELF_SHDR(format, section, sh_size), ELF_SHDR(format, section, sh_addr)); g_binary_portion_include(raw, new); } } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * count = quantité de zones listées. [OUT] * * * * Description : Fournit les références aux zones binaires à analyser. * * * * Retour : Zones binaires à analyser. * * * * Remarques : - * * * ******************************************************************************/ static GBinPart **g_elf_format_get_parts(const GElfFormat *format, size_t *count) { GBinPart **result; /* Tableau à retourner */ uint16_t i; /* Boucle de parcours */ elf_shdr strings; /* Section des descriptions */ bool has_strings; /* Section trouvée ? */ elf_shdr section; /* En-tête de section ELF */ GBinPart *part; /* Partie à intégrer à la liste*/ const char *name; /* Nom trouvé ou NULL */ off_t offset; /* Début de part de programme */ elf_phdr phdr; /* En-tête de programme ELF */ result = NULL; *count = 0; has_strings = find_elf_section_by_index(format, ELF_HDR(format, format->header, e_shstrndx), &strings); /* Première tentative : les sections */ for (i = 0; i < ELF_HDR(format, format->header, e_shnum); i++) { if (!find_elf_section_by_index(format, i, §ion)) continue; if (ELF_SHDR(format, section, sh_flags) & SHF_EXECINSTR) { part = g_binary_part_new(); if (has_strings) { name = extract_name_from_elf_string_section(format, &strings, ELF_SHDR(format, section, sh_name)); if (name != NULL) g_binary_part_set_name(part, name); } g_binary_part_set_values(part, ELF_SHDR(format, section, sh_offset), ELF_SHDR(format, section, sh_size), ELF_SHDR(format, section, sh_addr)); result = (GBinPart **)realloc(result, ++(*count) * sizeof(GBinPart *)); 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 < ELF_HDR(format, format->header, e_phnum); i++) { offset = ELF_HDR(format, format->header, e_phoff) + ELF_HDR(format, format->header, e_phentsize) * i; if (!read_elf_program_header(format, &offset, &phdr)) continue; if (ELF_PHDR(format, phdr, p_flags) & PF_X) { part = g_binary_part_new(); /* TODO : nom */ g_binary_part_set_values(part, ELF_PHDR(format, phdr, p_offset), ELF_PHDR(format, phdr, p_filesz), ELF_PHDR(format, phdr, p_vaddr)); result = (GBinPart **)realloc(result, ++(*count) * sizeof(GBinPart *)); result[*count - 1] = part; } } return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * addr = adresse virtuelle à retrouver. * * pos = position correspondante. [OUT] * * * * Description : Fournit la position correspondant à une adresse virtuelle. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_elf_format_translate_address_into_offset(const GElfFormat *format, vmpa_t addr, off_t *pos) { bool result; /* Bilan à retourner */ result = translate_address_into_offset_using_elf_sections(format, addr, pos); if (!result) result = translate_address_into_offset_using_elf_programs(format, addr, pos); return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * pos = position dans le flux binaire à retrouver. * * addr = adresse virtuelle correspondante. [OUT] * * * * Description : Fournit l'adresse virtuelle correspondant à une position. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_elf_format_translate_offset_into_address(const GElfFormat *format, off_t pos, vmpa_t *addr) { bool result; /* Bilan à retourner */ result = translate_offset_into_address_using_elf_sections(format, pos, addr); if (!result) /* TODO : prgm... */; return result; }