/* Chrysalide - Outil d'analyse de fichiers binaires
* program.c - gestion des en-têtes de programme d'un ELF
*
* Copyright (C) 2015-2017 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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.
*
* Chrysalide 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 "dynamic.h"
#include
#include "elf-int.h"
#include "program.h"
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* index = indice de la section recherchée. *
* dynamic = ensemble d'informations à faire remonter. [OUT] *
* *
* Description : Recherche un en-tête de programme DYNAMIC au sein de binaire.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool find_elf_dynamic_program_header(const GElfFormat *format, elf_phdr *dynamic)
{
bool result; /* Bilan d'opération à renvoyer*/
uint16_t max; /* Nombre d'en-têtes présents */
uint16_t i; /* Boucle de parcours */
result = false;
max = ELF_HDR(format, format->header, e_phnum);
for (i = 0; i < max && !result; i++)
{
if (!find_elf_program_by_index(format, i, dynamic))
break;
result = (ELF_PHDR(format, *dynamic, p_type) == PT_DYNAMIC);
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* dynamic = programme de type PT_DYNAMIC. *
* index = indice de l'élément recherché. *
* item = élément retrouvé dans la section. [OUT] *
* *
* Description : Retrouve un élément dans la section dynamique par son indice.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool _find_elf_dynamic_item_by_index(const GElfFormat *format, const elf_phdr *dynamic, size_t index, elf_dyn *item)
{
bool result; /* Bilan à retourner */
size_t max; /* Nombre d'entités présentes */
phys_t pos; /* Position de lecture */
max = ELF_PHDR(format, *dynamic, p_filesz) / ELF_SIZEOF_DYN(format);
assert(index < max);
if (index < max)
{
pos = ELF_PHDR(format, *dynamic, p_offset) + index * ELF_SIZEOF_DYN(format);
result = read_elf_dynamic_entry(format, pos, item);
}
else
result = false;
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* index = indice de l'élément recherché. *
* item = élément retrouvé dans la section. [OUT] *
* *
* Description : Retrouve un élément dans la section dynamique par son indice.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool find_elf_dynamic_item_by_index(const GElfFormat *format, size_t index, elf_dyn *item)
{
bool result; /* Bilan à retourner */
elf_phdr dynamic; /* En-tête de programme DYNAMIC*/
result = find_elf_dynamic_program_header(format, &dynamic);
if (result)
result = _find_elf_dynamic_item_by_index(format, &dynamic, index, item);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* dynamic = programme de type PT_DYNAMIC. *
* type = sorte d'élément recherché. *
* item = élément retrouvé dans la section. [OUT] *
* *
* Description : Retrouve un élément dans la section dynamique par son type. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool _find_elf_dynamic_item_by_type(const GElfFormat *format, const elf_phdr *dynamic, int64_t type, elf_dyn *item)
{
bool result; /* Bilan à retourner */
off_t max; /* Nombre d'entités présentes */
off_t i; /* Boucle de parcours */
phys_t pos; /* Position de lecture */
result = false;
max = ELF_PHDR(format, *dynamic, p_filesz) / ELF_SIZEOF_DYN(format);
for (i = 0; i < max && !result; i++)
{
pos = ELF_PHDR(format, *dynamic, p_offset) + i * ELF_SIZEOF_DYN(format);
if (!read_elf_dynamic_entry(format, pos, item))
break;
result = (ELF_DYN(format, *item, d_tag) == type);
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* type = sorte d'élément recherché. *
* item = élément retrouvé dans la section. [OUT] *
* *
* Description : Retrouve un élément dans la section dynamique par son type. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool find_elf_dynamic_item_by_type(const GElfFormat *format, int64_t type, elf_dyn *item)
{
bool result; /* Bilan à retourner */
elf_phdr dynamic; /* En-tête de programme DYNAMIC*/
result = find_elf_dynamic_program_header(format, &dynamic);
if (result)
result = _find_elf_dynamic_item_by_type(format, &dynamic, type, item);
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* count = nombre d'éléments dans la liste constituée. *
* *
* Description : Fournit la liste des objets partagés requis. *
* *
* Retour : Liste de noms d'objets ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
const char **list_elf_needed_objects(const GElfFormat *format, size_t *count)
{
const char **result; /* Liste à retourner */
elf_phdr dynamic; /* Programme à analyser */
virt_t strtab_virt; /* Adresse mémoire des chaînes */
uint64_t max; /* Nombre d'entités présentes */
uint64_t i; /* Boucle de parcours */
phys_t pos; /* Position de lecture */
elf_dyn item; /* Informations extraites */
vmpa2t strtab_pos; /* Emplacement des chaînes */
GBinContent *content; /* Contenu global analysé */
vmpa2t end; /* Limite finale de contenu */
vmpa2t str_pos; /* Emplacement d'une chaîne */
phys_t diff; /* Données encore disponibles */
const bin_t *string; /* Nouvelle chaîne trouvée */
result = NULL;
*count = 0;
if (!find_elf_program_by_type(format, PT_DYNAMIC, &dynamic))
goto leno_exit;
max = ELF_PHDR(format, dynamic, p_filesz) / ELF_SIZEOF_DYN(format);
/* Première passe : recherche des chaînes */
strtab_virt = VMPA_NO_VIRTUAL;
for (i = 0; i < max; i++)
{
pos = ELF_PHDR(format, dynamic, p_offset) + i * ELF_SIZEOF_DYN(format);
if (!read_elf_dynamic_entry(format, pos, &item))
break;
if (ELF_DYN(format, item, d_tag) == DT_STRTAB)
strtab_virt = ELF_DYN(format, item, d_un.d_val);
}
if (strtab_virt == VMPA_NO_VIRTUAL)
goto leno_exit;
if (!g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), strtab_virt, &strtab_pos))
goto leno_exit;
/* Seconde passe : recherche des objets requis */
content = G_BIN_FORMAT(format)->content;
g_binary_content_compute_end_pos(content, &end);
for (i = 0; i < max; i++)
{
pos = ELF_PHDR(format, dynamic, p_offset) + i * ELF_SIZEOF_DYN(format);
if (!read_elf_dynamic_entry(format, pos, &item))
break;
if (ELF_DYN(format, item, d_tag) == DT_NEEDED)
{
copy_vmpa(&str_pos, &strtab_pos);
advance_vmpa(&str_pos, ELF_DYN(format, item, d_un.d_val));
diff = compute_vmpa_diff(&str_pos, &end);
string = g_binary_content_get_raw_access(content, &str_pos, diff);
result = (const char **)realloc(result, ++(*count) * sizeof(const char *));
result[*count - 1] = (const char *)string;
}
}
leno_exit:
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à manipuler. *
* virt = position en mémoire de la PLT. [OUT] *
* *
* Description : Retrouve l'adresse de la PLT en se basant sur la GOT. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool resolve_plt_using_got(GElfFormat *format, virt_t *virt)
{
bool result; /* Bilan à retourner */
elf_phdr dynamic; /* Programme à analyser */
elf_dyn pltgot; /* Table de type DT_PLTGOT */
virt_t got_virt; /* Adresse mémoire de la GOT */
vmpa2t got_addr; /* Localisation complète */
GBinContent *content; /* Contenu binaire à parcourir */
uint32_t raw_32; /* Valeur brute de 32 bits lue */
uint64_t raw_64; /* Valeur brute de 64 bits lue */
result = false;
if (!find_elf_program_by_type(format, PT_DYNAMIC, &dynamic))
goto rpug_exit;
if (!_find_elf_dynamic_item_by_type(format, &dynamic, DT_PLTGOT, &pltgot))
goto rpug_exit;
got_virt = ELF_DYN(format, pltgot, d_un.d_ptr);
if (!g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), got_virt, &got_addr))
goto rpug_exit;
content = G_BIN_FORMAT(format)->content;
/**
* Quelques pistes pour la connaissance des premières cellules d'une GOT :
*
* "Lazy procedure linkage with the PLT" (mot clef : GOT+4).
* http://www.iecc.com/linker/linker10.html
*
* "How the ELF Ruined Christmas" (mot clef : GOT[1]).
* https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-di-frederico.pdf
*/
if (format->is_32b)
{
advance_vmpa(&got_addr, 3 * sizeof(uint32_t));
result = g_binary_content_read_u32(content, &got_addr, format->endian, &raw_32);
if (result)
*virt = raw_32;
}
else
{
advance_vmpa(&got_addr, 3 * sizeof(uint64_t));
result = g_binary_content_read_u64(content, &got_addr, format->endian, &raw_64);
if (result)
*virt = raw_64;
}
rpug_exit:
return result;
}