/* OpenIDA - Outil d'analyse de fichiers binaires
* symbol.c - gestion des symboles d'un 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 "symbol.h"
#include
#include
#include
#include
#include "../../arch/processor.h" /* FIXME : remove me ! */
#include "../../arch/instruction.h" /* FIXME : remove me ! */
#include "../../arch/instruction-int.h" /* FIXME : remove me ! */
#include "elf-int.h"
#include "section.h"
/* -------------------------- DETAIL DES SYMBOLES EXTERNES -------------------------- */
/* Charge en mémoire la liste des symboles dynamiques. */
bool load_elf_relocation_table(elf_format *, const off_t *, const off_t *, const off_t *, const off_t *, const off_t *, const off_t *);
/* Récupère les informations d'un symbole dynamique donné. */
char *get_elf_dynamic_symbol_info(elf_format *, const off_t *, const off_t *, const off_t *, const off_t *, const off_t *);
/* Décode les instructions liées à la relocalisation. */
asm_instr **decode_elf_relocations(elf_format *, size_t *);
/* Déduit les adresses effectives des relocalisations. */
void translate_elf_relocations(elf_format *, asm_instr **, size_t);
/* Charge en mémoire la liste humaine des symboles (32 bits). */
bool load_elf_symbol_table_32(elf_format *, const off_t *, const off_t *, const off_t *, const off_t *);
/* Charge en mémoire la liste humaine des symboles (64 bits). */
bool load_elf_symbol_table_64(elf_format *, const off_t *, const off_t *, const off_t *, const off_t *);
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* *
* Description : Charge en mémoire la liste humaine des symboles. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_elf_symbols(elf_format *format)
{
bool result; /* Bilan à retourner */
Elf_Shdr *sections; /* Groupe de sections trouvées */
size_t count; /* Quantité de données */
size_t i; /* Boucle de parcours */
Elf_Shdr section; /* Section trouvée ou non */
off_t sym_start; /* Début de section */
off_t sym_size; /* Taille de section */
off_t str_start; /* Début de section */
off_t str_size; /* Taille de section */
off_t rel_start; /* Début de section */
off_t rel_size; /* Taille de section */
off_t dyn_start; /* Début de section */
off_t dyn_size; /* Taille de section */
asm_instr **instructions; /* Instructions décodées */
result = true;
/* Table des symboles */
find_elf_section_by_type(format, SHT_SYMTAB, §ions, &count);
for (i = 0; i < count; i++)
{
/* Section ".symtab" */
get_elf_section_content(format, §ions[i], &sym_start, &sym_size, NULL);
/* Section ".strtab" */
result &= find_elf_section_by_index(format, ELF_SHDR(format, §ions[i], sh_link), §ion);
get_elf_section_content(format, §ion, &str_start, &str_size, NULL);
if (result)
{
result = load_elf_symbol_table_32(format, &sym_start, &sym_size, &str_start, &str_size);
}
}
/* Relocalisations dynamiques */
find_elf_section_by_type(format, SHT_REL, §ions, &count);
for (i = 0; i < count; i++)
{
/* Section ".rel.xxx" */
get_elf_section_content(format, §ions[i], &rel_start, &rel_size, NULL);
/* Section ".dynsym" */
result &= find_elf_section_by_index(format, ELF_SHDR(format, §ions[i], sh_link), §ion);
get_elf_section_content(format, §ion, &dyn_start, &dyn_size, NULL);
/* Section ".dynstr" */
result &= find_elf_section_by_index(format, ELF_SHDR(format, §ion, sh_link), §ion);
get_elf_section_content(format, §ion, &str_start, &str_size, NULL);
/* Récupération (première partie) */
if (result)
{
result = load_elf_relocation_table(format, &rel_start, &rel_size, &dyn_start, &dyn_size, &str_start, &str_size);
}
}
free(sections);
/* Récupération (seconde partie) */
if (result)
{
instructions = decode_elf_relocations(format, &count);
translate_elf_relocations(format, instructions, count);
/* TODO : free instructions */
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* sym_start = début de la zone à traiter. *
* sym_size = taille de la zone à traiter. *
* str_start = début de la zone de chaîne de caractères. *
* str_size = taille de la zone de chaînes de caractères. *
* *
* Description : Charge en mémoire la liste humaine des symboles (32 bits). *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_elf_symbol_table_32(elf_format *format, const off_t *sym_start, const off_t *sym_size, const off_t *str_start, const off_t *str_size)
{
off_t iter; /* Boucle de parcours */
Elf32_Sym symbol; /* Symbole ELF lu */
if (*sym_size % sizeof(Elf32_Sym) != 0) return false;
for (iter = *sym_start; iter < (*sym_start + *sym_size); iter += sizeof(Elf32_Sym))
{
memcpy(&symbol, &EXE_FORMAT(format)->content[iter], sizeof(Elf32_Sym));
if (!(ELF32_ST_TYPE(symbol.st_info) == STT_FUNC
|| (ELF32_ST_TYPE(symbol.st_info) == STT_NOTYPE
&& ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL))) continue;
if (symbol.st_value == 0) continue;
/* Sécurité anti-débordements */
if (symbol.st_name >= *str_size) continue;
/* Si le symbole possède un nom... */
if (strlen(&EXE_FORMAT(format)->content[*str_start + symbol.st_name]) > 0)
{
format->symbols = (elf_symbol *)realloc(format->symbols, ++format->sym_count * sizeof(elf_symbol));
format->symbols[format->sym_count - 1].name = &EXE_FORMAT(format)->content[*str_start + symbol.st_name];
format->symbols[format->sym_count - 1].address = symbol.st_value;
}
}
return true;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* sym_start = début de la zone à traiter. *
* sym_size = taille de la zone à traiter. *
* str_start = début de la zone de chaîne de caractères. *
* str_size = taille de la zone de chaînes de caractères. *
* *
* Description : Charge en mémoire la liste humaine des symboles (64 bits). *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_elf_symbol_table_64(elf_format *format, const off_t *sym_start, const off_t *sym_size, const off_t *str_start, const off_t *str_size)
{
off_t iter; /* Boucle de parcours */
Elf64_Sym symbol; /* Symbole ELF lu */
if (*sym_size % sizeof(Elf64_Sym) != 0) return false;
for (iter = *sym_start; iter < (*sym_start + *sym_size); iter += sizeof(Elf64_Sym))
{
memcpy(&symbol, &EXE_FORMAT(format)->content[iter], sizeof(Elf64_Sym));
if (!(ELF64_ST_TYPE(symbol.st_info) == STT_FUNC
|| (ELF64_ST_TYPE(symbol.st_info) == STT_NOTYPE
&& ELF64_ST_BIND(symbol.st_info) == STB_GLOBAL))) continue;
if (symbol.st_value == 0) continue;
/* Sécurité anti-débordements */
if (symbol.st_name >= *str_size) continue;
/* Si le symbole possède un nom... */
if (strlen(&EXE_FORMAT(format)->content[*str_start + symbol.st_name]) > 0)
{
format->symbols = (elf_symbol *)realloc(format->symbols, ++format->sym_count * sizeof(elf_symbol));
format->symbols[format->sym_count - 1].name = &EXE_FORMAT(format)->content[*str_start + symbol.st_name];
format->symbols[format->sym_count - 1].address = symbol.st_value;
}
}
return true;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* comments = liste des commentaires à insérer. [OUT] *
* offsets = liste des indices des commentaires. [OUT] *
* count = taille des listes construites. [OUT] *
* *
* Description : Récupère tous les commentaires à insérer dans le code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void get_elf_symbol_comments(const elf_format *format, char ***comments, uint64_t **offsets, size_t *count)
{
size_t i; /* Boucle de parcours */
size_t len; /* Longueur d'une désignation */
if (format->sym_count > 0)
{
*comments = (char **)calloc(*count + format->sym_count, sizeof(char *));
*offsets = (uint64_t *)calloc(*count + format->sym_count, sizeof(uint64_t));
for (i = 0; i < format->sym_count; i++)
{
len = strlen(format->symbols[i].name);
(*comments)[i] = (char *)calloc(len + 9, sizeof(char));
snprintf((*comments)[i], len + 9, "<%s>", format->symbols[i].name);
(*offsets)[i] = format->symbols[i].address;
}
*count += format->sym_count;
}
}
/* ---------------------------------------------------------------------------------- */
/* DETAIL DES SYMBOLES EXTERNES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* plt_start = début de la zone à traiter. *
* plt_size = taille de la zone à traiter. *
* dyn_start = début des informations dynamiques associées. *
* dyn_size = taille de la zone associée. *
* str_start = début de la zone de chaîne de caractères. *
* str_size = taille de la zone de chaînes de caractères. *
* *
* Description : Charge en mémoire la liste des symboles dynamiques. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_elf_relocation_table(elf_format *format, const off_t *plt_start, const off_t *plt_size, const off_t *dyn_start, const off_t *dyn_size, const off_t *str_start, const off_t *str_size)
{
off_t iter; /* Boucle de parcours */
Elf_Rel reloc; /* Infos de relocalisation */
off_t index; /* Indice de la portion visée */
char *name; /* Nom du symbole trouvé */
for (iter = *plt_start; iter < (*plt_start + *plt_size); iter += ELF_SIZEOF_REL(format))
{
memcpy(&reloc, &EXE_FORMAT(format)->content[iter], ELF_SIZEOF_REL(format));
switch (format->header.e_machine)
{
case EM_386:
switch (ELF32_R_TYPE(ELF_REL_TYPE(format, reloc)))
{
case R_386_JMP_SLOT:
index = ELF_REL_SYM(format, reloc);
name = get_elf_dynamic_symbol_info(format, dyn_start, dyn_size, &index, str_start, str_size);
if (name != NULL)
{
format->symbols = (elf_symbol *)realloc(format->symbols, ++format->sym_count * sizeof(elf_symbol));
format->symbols[format->sym_count - 1].name = name;
format->symbols[format->sym_count - 1].address = ELF_REL(format, reloc, r_offset);
}
break;
default:
printf("Relocation not supported (%lld) !\n", ELF_REL_TYPE(format, reloc));
break;
}
break;
default:
printf("Machine not recognized !\n");
return false;
break;
}
}
return true;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* dyn_start = début des informations dynamiques associées. *
* dyn_size = taille de la zone associée. *
* index = indice de l'entrée à venir lire. *
* str_start = début de la zone de chaîne de caractères. *
* str_size = taille de la zone de chaînes de caractères. *
* *
* Description : Récupère les informations d'un symbole dynamique donné. *
* *
* Retour : Nom du symbole trouvé, ou NULL si erreur ou non adapté. *
* *
* Remarques : - *
* *
******************************************************************************/
char *get_elf_dynamic_symbol_info(elf_format *format, const off_t *dyn_start, const off_t *dyn_size, const off_t *index, const off_t *str_start, const off_t *str_size)
{
off_t offset; /* Emplacement à venir lire */
Elf_Sym symbol; /* Symbole aux infos visées */
offset = *dyn_start + *index * ELF_SIZEOF_SYM(format);
if ((offset + ELF_SIZEOF_SYM(format)) > (*dyn_start + *dyn_size)) return NULL;
memcpy(&symbol, &EXE_FORMAT(format)->content[offset], ELF_SIZEOF_SYM(format));
if (ELF_SYM(format, symbol, st_name) >= *str_size) return NULL;
return (char *)&EXE_FORMAT(format)->content[*str_start + ELF_SYM(format, symbol, st_name)];
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* count = nombre d'instructions lues. [OUT] *
* *
* Description : Décode les instructions liées à la relocalisation. *
* *
* Retour : Liste des instructions décodées ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
asm_instr **decode_elf_relocations(elf_format *format, size_t *count)
{
asm_instr **result; /* Liste à renvoyer */
off_t rel_start; /* Début de section */
off_t rel_size; /* Taille de section */
uint64_t rel_vaddress; /* Adresse virtuelle associée */
Elf_Shdr *sections; /* Groupe de sections trouvées */
size_t sec_count; /* Quantité de données */
size_t i; /* Boucle de parcours */
off_t pos; /* Tête de lecture */
uint64_t offset; /* Adresse virtuelle courante */
asm_instr *instr; /* Instruction décodée */
asm_processor *proc; /* TODO : remove me ! */
proc = create_x86_processor();
result = NULL;
*count = 0;
if (!find_elf_section_content_by_name(format, ".plt", &rel_start, &rel_size, &rel_vaddress))
{
printf("No .plt section found ! Trying to guess it...\n");
find_elf_section_by_type(format, SHT_PROGBITS, §ions, &sec_count);
for (i = 0; i < sec_count; i++)
if (ELF_SHDR(format, §ions[i], sh_entsize) > 0)
{
get_elf_section_content(format, §ions[i], &rel_start, &rel_size, &rel_vaddress);
break;
}
free(sections);
if (i == sec_count) return NULL;
}
for (pos = 0; pos < rel_size; )
{
offset = rel_vaddress + pos;
instr = decode_instruction(proc, &EXE_FORMAT(format)->content[rel_start], &pos, rel_size, 0/* FIXME*/, offset);
result = (asm_instr **)realloc(result, ++(*count) * sizeof(asm_instr *));
result[*count - 1] = instr;
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* instructions = listes des instructions à interpréter. *
* count = nombre d'instructions lues. *
* *
* Description : Déduit les adresses effectives des relocalisations. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void translate_elf_relocations(elf_format *format, asm_instr **instructions, size_t count)
{
size_t i; /* Boucle de parcours #1 */
uint64_t address; /* Adresse virtuelle finale */
size_t j; /* Boucle de parcours #2 */
size_t new_len; /* Taille du nouveau nom */
char *new_name; /* Nom avec suffixe @plt */
for (i = 0; (i + 2) < count; )
{
if ((instructions[i]->type == AIT_JUMP || instructions[i]->type == AIT_CALL)
&& instructions[i + 1]->type == AIT_PUSH
&& instructions[i + 2]->type == AIT_JUMP)
{
if (get_imm_operand_value(instructions[i]->operands[0], AOS_64_BITS_UNSIGNED, &address))
for (j = 0; j < format->sym_count; j++)
if (format->symbols[j].address == address)
{
new_len = strlen(format->symbols[j].name) + 4 + 1;
new_name = calloc(new_len, sizeof(char));
snprintf(new_name, new_len, "%s@plt", format->symbols[j].name);
format->symbols = (elf_symbol *)realloc(format->symbols, ++format->sym_count * sizeof(elf_symbol));
format->symbols[format->sym_count - 1].name = new_name;
format->symbols[format->sym_count - 1].address = instructions[i]->vaddress;
}
i += 3;
}
else i++;
}
}