/* Chrysalide - Outil d'analyse de fichiers binaires
* symbols.c - gestion des symboles d'un ELF
*
* Copyright (C) 2008-2013 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* 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 "symbols.h"
#include
#include
#include
#include
#include "dynamic.h"
#include "elf-int.h"
#include "helper_arm.h"
#include "helper_x86.h"
#include "program.h"
#include "section.h"
#include "../mangling/demangler.h"
#include "../../arch/raw.h"
#include "../../common/extstr.h"
#include "../../core/params.h"
#include "../../gui/panels/log.h"
/* Enregistre un point d'entrée au sein d'un binaire ELF. */
static void register_elf_entry_point(GElfFormat *, virt_t, phys_t, GBinRoutine *);
/* Enumère tous les points d'entrée principaux d'un binaire ELF. */
static bool load_all_elf_basic_entry_points(GElfFormat *);
/* -------------------------- DETAIL DES SYMBOLES INTERNES -------------------------- */
/* Charge tous les symboles internes possibles. */
static bool load_elf_internal_symbols(GElfFormat *);
/* -------------------------- DETAIL DES SYMBOLES EXTERNES -------------------------- */
/* Retrouve un élément donné dans la section dynamique. */
static bool find_elf_dynamic_item(const GElfFormat *, const elf_shdr *, int32_t, elf_dyn *);
/* Charge tous les éléments dynamiques externes possibles. */
static bool load_elf_external_symbols(GElfFormat *, const elf_shdr *);
/******************************************************************************
* *
* 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(GElfFormat *format)
{
bool result; /* Bilan à retourner */
elf_shdr *sections; /* Groupe de sections trouvées */
size_t count; /* Quantité de données */
result = true;
/* Symboles internes */
result &= load_elf_internal_symbols(format);
/* Symboles externes */
#if 1
if (find_elf_sections_by_type(format, SHT_DYNAMIC, §ions, &count))
{
log_variadic_message(LMT_INFO, _("Binary is dynamically linked"));
result &= load_elf_external_symbols(format, §ions[0]);
free(sections);
}
else log_variadic_message(LMT_INFO, _("Binary is statically linked"));
#endif
/* Symboles internes */
//result &= load_elf_internal_symbols(format);
/* Symboles d'entrée, si encore besoin */
result &= load_all_elf_basic_entry_points(format);
g_binary_format_sort_symbols(G_BIN_FORMAT(format));
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* vaddr = adresse virtuelle du symbole à insérer. *
* len = taille de la routine à ajouter. *
* routine = représentation de la fonction repérée. *
* *
* Description : Enregistre un point d'entrée au sein d'un binaire ELF. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void register_elf_entry_point(GElfFormat *format, virt_t vaddr, phys_t len, GBinRoutine *routine)
{
GBinFormat *base; /* Version basique de l'instance */
vmpa2t addr; /* Localisation d'une routine */
mrange_t range; /* Couverture mémoire associée */
GBinSymbol *symbol; /* Nouveau symbole construit */
base = G_BIN_FORMAT(format);
/* Comptabilisation pour le désassemblage brut */
base->entry_points = (virt_t *)realloc(base->entry_points, ++base->ep_count * sizeof(virt_t));
base->entry_points[base->ep_count - 1] = vaddr;
/* Comptabilisation en tant que symbole */
if (ELF_HDR(format, format->header, e_machine) == EM_ARM)
vaddr &= ~0x1;
init_vmpa(&addr, VMPA_NO_PHYSICAL, vaddr);
if (g_binary_format_find_symbol_at(G_BIN_FORMAT(format), &addr, &symbol))
{
g_object_unref(G_OBJECT(routine));
routine = g_binary_symbol_get_routine(symbol);
g_object_ref(G_OBJECT(routine));
printf(" -- SYM CHANGE @ 0x%08x\n", vaddr);
_g_binary_symbol_attach_routine(symbol, routine, STP_ENTRY_POINT);
}
else
{
printf(" -- SYM ENTRY @ 0x%08x\n", vaddr);
init_mrange(&range, &addr, len);
g_binary_routine_set_range(routine, &range);
symbol = g_binary_symbol_new(STP_ENTRY_POINT, "XXX", ~0);
g_binary_symbol_attach_routine(symbol, routine);
_g_binary_format_add_symbol(base, symbol, false);
}
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Enumère tous les points d'entrée principaux d'un binaire ELF.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool load_all_elf_basic_entry_points(GElfFormat *format)
{
virt_t ep; /* Point d'entrée détecté */
GBinRoutine *routine; /* Routine à associer à un pt. */
elf_phdr dynamic; /* En-tête de programme DYNAMIC*/
elf_dyn item_a; /* Premier élément DYNAMIC */
elf_dyn item_b; /* Second élément DYNAMIC */
const bin_t *content; /* Contenu binaire à lire */
off_t length; /* Taille totale du contenu */
bool status; /* Bilan d'une opération */
off_t pos; /* Tête de lecture courante */
uint32_t virt_32; /* Adresse virtuelle sur 32b */
uint64_t virt_64; /* Adresse virtuelle sur 64b */
/* Point d'entrée principal éventuel */
ep = ELF_HDR(format, format->header, e_entry);
if (ep != 0x0)
{
routine = try_to_demangle_routine("entry_point");
register_elf_entry_point(format, ep, 0, routine);
}
/* Chargemet de l'en-tête de programme DYNAMIC */
if (!find_elf_dynamic_program_header(format, &dynamic))
goto laebep_exit;
/* Détection des constructeurs & destructeurs */
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_INIT, &item_a))
{
ep = ELF_DYN(format, item_a, d_un.d_ptr);
if (ep != 0x0)
{
routine = try_to_demangle_routine("init_function");
register_elf_entry_point(format, ep, 0, routine);
}
}
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_FINI, &item_a))
{
ep = ELF_DYN(format, item_a, d_un.d_ptr);
if (ep != 0x0)
{
routine = try_to_demangle_routine("termination_function");
register_elf_entry_point(format, ep, 0, routine);
}
}
void load_entry_points_from_array(GElfFormat *fmt, const elf_dyn *ar, const elf_dyn *sz, const char *prefix)
{
unsigned int i; /* Boucle de parcours */
char fullname[64]; /* Désignation humaine */
assert(sizeof(fullname) >= (strlen(prefix) + sizeof(XSTR(UINT64_MAX) + 1)));
content = G_BIN_FORMAT(fmt)->content;
length = G_BIN_FORMAT(fmt)->length;
status = g_exe_format_translate_address_into_offset(G_EXE_FORMAT(format),
ELF_DYN(fmt, *ar, d_un.d_val),
&pos);
if (!status) return;
if ((pos + ELF_DYN(fmt, *sz, d_un.d_val)) < length)
length = pos + ELF_DYN(fmt, *sz, d_un.d_val);
for (i = 0; pos < length; i++)
{
if (fmt->is_32b)
{
status = read_u32(&virt_32, content, &pos, length, fmt->endian);
ep = virt_32;
}
else
{
status = read_u64(&virt_64, content, &pos, length, fmt->endian);
ep = virt_64;
}
if (!status) break;
if (ep != 0x0)
{
snprintf(fullname, sizeof(fullname), "%s%u", prefix, i);
routine = try_to_demangle_routine(fullname);
register_elf_entry_point(fmt, ep, 0, routine);
}
}
}
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_INIT_ARRAY, &item_a))
{
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_INIT_ARRAYSZ, &item_b))
{
load_entry_points_from_array(format, &item_a, &item_b, "init_array_function_");
}
}
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_FINI_ARRAY, &item_a))
{
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_FINI_ARRAYSZ, &item_b))
{
load_entry_points_from_array(format, &item_a, &item_b, "fini_array_function_");
}
}
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_PREINIT_ARRAY, &item_a))
{
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_PREINIT_ARRAYSZ, &item_b))
{
load_entry_points_from_array(format, &item_a, &item_b, "preinit_array_function_");
}
}
/* Identification de l'entrée de la PLT */
if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_PLTGOT, &item_a))
{
status = g_exe_format_translate_address_into_offset(G_EXE_FORMAT(format),
ELF_DYN(format, item_a, d_un.d_val),
&pos);
if (status)
{
content = G_BIN_FORMAT(format)->content;
length = G_BIN_FORMAT(format)->length;
/* On saute le premier élément... */
if (format->is_32b)
status = read_u32(&virt_32, content, &pos, length, format->endian);
else
status = read_u64(&virt_64, content, &pos, length, format->endian);
while (1)
{
if (format->is_32b)
{
status = read_u32(&virt_32, content, &pos, length, format->endian);
ep = virt_32;
}
else
{
status = read_u64(&virt_64, content, &pos, length, format->endian);
ep = virt_64;
}
if (!status) break;
if (ep != 0x0)
{
routine = try_to_demangle_routine("plt_entry");
register_elf_entry_point(format, ep, 0, routine);
break;
}
}
}
}
laebep_exit:
return true;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* sym = section comprenant les symboles à venir lire. *
* index = indice de l'entrée à venir lire. *
* symbol = ensemble d'informations lues. [OUT] *
* *
* Description : Récupère la définition complète d'un symbole donné. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool get_elf_symbol_by_index(GElfFormat *format, const elf_shdr *sym, off_t index, elf_sym *symbol)
{
off_t sym_start; /* Début de section */
off_t sym_size; /* Taille de section */
off_t offset; /* Emplacement à venir lire */
get_elf_section_content(format, sym, &sym_start, &sym_size, NULL);
offset = sym_start + index * ELF_SIZEOF_SYM(format);
if ((offset + ELF_SIZEOF_SYM(format)) > (sym_start + sym_size)) return NULL;
return read_elf_symbol(format, &offset, symbol);
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* sym = section comprenant les symboles à venir lire. *
* str = section de chaînes de caractères pour les noms. *
* index = indice de l'entrée à venir lire. *
* *
* Description : Récupère la désignation d'un symbole donné. *
* *
* Retour : Nom du symbole trouvé, ou NULL si erreur ou non adapté. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *get_elf_symbol_name(GElfFormat *format, const elf_shdr *sym, const elf_shdr *str, off_t index)
{
const char *result; /* Résultat à retourner */
elf_sym symbol; /* Symbole aux infos visées */
result = NULL;
if (get_elf_symbol_by_index(format, sym, index, &symbol))
result = extract_name_from_elf_string_section(format, str, ELF_SYM(format, symbol, st_name));
return result;
}
/* ---------------------------------------------------------------------------------- */
/* DETAIL DES SYMBOLES INTERNES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* *
* Description : Charge tous les symboles internes possibles. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool load_elf_internal_symbols(GElfFormat *format)
{
bool result; /* Bilan à retourner */
bool no_name; /* Choix de construction de nom*/
elf_shdr *sections; /* Groupe de sections trouvées */
size_t count; /* Quantité de données */
size_t i; /* Boucle de parcours */
result = true;
/* Charge tous les symboles définis dans une section */
bool add_all_symbols_from_section(GElfFormat *format, const elf_shdr *section, bool use_virt)
{
elf_shdr strtab; /* Section .strtab trouvée */
bool has_strtab; /* Présence de cette section */
off_t start; /* Début de la zone à traiter */
off_t size; /* Taille de cette même zone */
off_t iter; /* Boucle de parcours */
elf_sym sym; /* Symbole aux infos visées */
virt_t virt; /* Adresse virtuelle */
vmpa2t addr; /* Localisation d'une routine */
mrange_t range; /* Couverture mémoire associée */
const char *name; /* Nom du symbole trouvé */
char alt_name[5 + VMPA_MAX_LEN]; /* Nom abstrait de substitution*/
GBinRoutine *routine; /* Nouvelle routine trouvée */
GBinSymbol *symbol; /* Nouveau symbole construit */
has_strtab = find_elf_section_by_index(format, ELF_SHDR(format, *section, sh_link), &strtab);
get_elf_section_content(format, section, &start, &size, NULL);
for (iter = start; iter < (start + size); )
{
result = read_elf_symbol(format, &iter, &sym);
if (!result) break;
/* On rejette les symboles qui ne sont pas définis au sein du binaire */
if (ELF_SYM(format, sym, st_shndx) == 0) continue;
#if 0
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
#endif
if (ELF_SYM(format, sym, st_value) == 0) continue;
/* Résolution précise d'adresse */
/* TODO */
//init_vmpa(&addr, VMPA_NO_PHYSICAL, ELF_SYM(format, sym, st_value));
virt = ELF_SYM(format, sym, st_value);
if (ELF_HDR(format, format->header, e_machine) == EM_ARM)
virt &= ~0x1;
if (!g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), virt, &addr))
continue;
//init_mrange(&range, &addr, ELF_SYM(format, sym, st_size));
init_mrange(&range, &addr, 0);
/* Première ébauche de nom */
if (!has_strtab) name = NULL;
else name = get_elf_symbol_name(format, section, &strtab,
((iter - start) / ELF_SIZEOF_SYM(format)) - 1);
/* Traitements particuliers */
switch (ELF_ST_TYPE(format, sym))
{
case STT_OBJECT:
/* Création d'un nom unique ? */
if (name != NULL)
{
strcpy(alt_name, "obj_");
if (use_virt)
vmpa2_virt_to_string(&addr, MDS_UNDEFINED, alt_name + 4, NULL);
else
vmpa2_phys_to_string(&addr, MDS_UNDEFINED, alt_name + 4, NULL);
}
/* TODO */
symbol = NULL;
break;
case STT_FUNC:
/* Création d'un nom unique ? */
if (name != NULL)
{
strcpy(alt_name, "func_");
if (use_virt)
vmpa2_virt_to_string(&addr, MDS_UNDEFINED, alt_name + 5, NULL);
else
vmpa2_phys_to_string(&addr, MDS_UNDEFINED, alt_name + 5, NULL);
}
/* Routine */
printf("SYM ADDING>> '%s'\n", name);
routine = try_to_demangle_routine(name);
g_binary_routine_set_range(routine, &range);
/* Symbole uniquement */
symbol = g_binary_symbol_new(STP_ROUTINE, name, ~0);
g_binary_symbol_attach_routine(symbol, routine);
break;
default:
symbol = NULL;
break;
}
if (symbol != NULL)
_g_binary_format_add_symbol(G_BIN_FORMAT(format), symbol, false);
}
return true;
}
if (!g_generic_config_get_value(get_main_configuration(), MPK_FORMAT_NO_NAME, &no_name))
return false;
if (find_elf_sections_by_type(format, SHT_DYNSYM, §ions, &count))
for (i = 0; i < count && result; i++)
result = add_all_symbols_from_section(format, §ions[i], no_name);
if (find_elf_sections_by_type(format, SHT_SYMTAB, §ions, &count))
for (i = 0; i < count && result; i++)
result = add_all_symbols_from_section(format, §ions[i], no_name);
return result;
}
/* ---------------------------------------------------------------------------------- */
/* DETAIL DES SYMBOLES EXTERNES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* dynamic = section de type SHT_DYNAMIC. *
* type = sorte d'élément recherché. *
* item = élément retrouvé dans la section. [OUT] *
* *
* Description : Retrouve un élément donné dans la section dynamique. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool find_elf_dynamic_item(const GElfFormat *format, const elf_shdr *section, int32_t type, elf_dyn *item)
{
bool result; /* Bilan à retourner */
const bin_t *content; /* Contenu binaire à lire */
off_t length; /* Taille totale du contenu */
off_t pos; /* Position de lecture */
off_t tmp; /* Position écrasable */
int32_t tag32; /* Type de l'entrée (32 bits) */
int64_t tag64; /* Type de l'entrée (64 bits) */
result = true;
content = G_BIN_FORMAT(format)->content;
length = G_BIN_FORMAT(format)->length;
for (pos = ELF_SHDR(format, *section, sh_offset);
pos < length/* FIXME !! + xploit */ && result;
pos += ELF_SIZEOF_DYN(format))
{
tmp = pos;
if (format->is_32b)
{
result = read_s32(&tag32, content, &tmp, length, format->endian);
if (tag32 == type) break;
}
else
{
result = read_s64(&tag64, content, &tmp, length, format->endian);
if (tag64 == type) break;
}
}
result &= (pos < length);
if (result)
{
if (format->is_32b)
{
result = read_s32(&item->dyn32.d_tag, content, &pos, length, format->endian);
result &= read_s32(&item->dyn32.d_un.d_val, content, &pos, length, format->endian);
}
else
{
result = read_s64(&item->dyn64.d_tag, content, &pos, length, format->endian);
result &= read_s64(&item->dyn64.d_un.d_val, content, &pos, length, format->endian);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* dynamic = section de type SHT_DYNAMIC. *
* *
* Description : Charge tous les éléments dynamiques externes possibles. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool load_elf_external_symbols(GElfFormat *format, const elf_shdr *section)
{
bool result; /* Bilan à retourner */
elf_dyn item; /* Elément dynamique */
elf_shdr relxxx; /* Section .rel.xxx trouvée */
elf_shdr dynsym; /* Section .dynsym trouvée */
elf_shdr dynstr; /* Section .dynstr trouvée */
elf_shdr plt; /* Section .plt trouvée */
result = true;
/* Section .rel.plt */
if (find_elf_dynamic_item(format, section, DT_JMPREL, &item))
{
result &= find_elf_section_by_virtual_address(format, ELF_DYN(format, item, d_un.d_ptr), &relxxx);
if (result)
result = find_elf_section_by_index(format, ELF_SHDR(format, relxxx, sh_link), &dynsym);
if (result)
result = find_elf_section_by_index(format, ELF_SHDR(format, dynsym, sh_link), &dynstr);
if (result)
switch (ELF_HDR(format, format->header, e_machine))
{
case EM_ARM:
result = load_elf_arm_relocated_symbols(format, &relxxx, &dynsym, &dynstr);
break;
case EM_386:
result = load_elf_x86_relocated_symbols(format, &relxxx, &dynsym, &dynstr);
break;
default:
break;
}
}
#if 0
/* Entrées équivalentes dans le binaire */
if (find_elf_dynamic_item(format, section, DT_SYMTAB, &item))
{
result &= find_elf_section_by_virtual_address(format, ELF_DYN(format, item, d_un.d_ptr), &dynsym);
if (result)
result = find_elf_section_by_index(format, ELF_SHDR(format, dynsym, sh_link), &dynstr);
if (result)
switch (g_exe_format_get_target_machine(G_EXE_FORMAT(format)))
{
case FTM_MIPS:
//result = find_elf_mips_dynamic_symbols(format, &dynsym, &dynstr);
break;
case FTM_386:
if (find_elf_dynamic_item(format, section, DT_JMPREL, &item))
{
result &= find_elf_section_by_virtual_address(format, ELF_DYN(format, item, d_un.d_ptr), &relxxx);
printf("VMA :: 0x%08llx\n", ELF_DYN(format, item, d_un.d_ptr));
if (result)
result = find_elf_section_by_index(format, ELF_SHDR(format, relxxx, sh_info), &plt);
if (result)
result = find_elf_x86_dynamic_symbols(format, &plt, &relxxx, &dynsym, &dynstr);
}
break;
default:
break;
}
}
#endif
return result;
}