/* OpenIDA - Outil d'analyse de fichiers binaires
* helper_x86.c - gestion auxiliaire de l'architecture x86
*
* Copyright (C) 2009-2011 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 "helper_x86.h"
#include
#include
#include
#include "elf-int.h"
#include "../symbol.h"
#include "../mangling/demangler.h"
#include "../../arch/immediate.h"
#include "../../arch/processor.h"
#include "../../arch/x86/instruction.h"
/* symbols.c : Récupère la désignation d'un symbole donné. */
extern const char *get_elf_symbol_name(GElfFormat *, const elf_shdr *, const elf_shdr *, off_t);
/* Décode les instructions liées à la relocalisation. */
GArchInstruction **decode_elf_relocations(GElfFormat *, const elf_shdr *, size_t *);
/* Déduit les adresses effectives des relocalisations. */
void translate_exe_elf_relocations(GElfFormat *, GArchInstruction **, size_t);
/* Déduit les adresses effectives des relocalisations. */
void translate_dyn_elf_relocations(GElfFormat *, GArchInstruction **, size_t, const elf_shdr *, const elf_shdr *, const elf_shdr *);
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* relxxx = section .rel.xxx trouvée (zone à traiter). *
* dynsym = section .dynsym trouvée (info. dynamiques). *
* dynstr = section .dynstr trouvée (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_x86_relocated_symbols(GElfFormat *format, const elf_shdr *relxxx, const elf_shdr *dynsym, const elf_shdr *dynstr)
{
bool result; /* Bilan à retourner */
off_t rel_start; /* Début de la zone à traiter */
off_t rel_size; /* Taille de cette même zone */
off_t iter; /* Boucle de parcours */
elf_rel reloc; /* Infos de relocalisation */
off_t index; /* Indice de la portion visée */
const char *name; /* Nom du symbole trouvé */
GBinSymbol *symbol; /* Nouveau symbole construit */
result = true;
get_elf_section_content(format, relxxx, &rel_start, &rel_size, NULL);
printf("rel :: %d -> %d\n", rel_start, rel_start + rel_size);
for (iter = rel_start; iter < (rel_start + rel_size); )
{
result = read_elf_relocation(format, &iter, &reloc);
if (!result) break;
switch (ELF_REL_TYPE(format, reloc))
{
case R_386_NONE:
break;
case R_386_JMP_SLOT:
index = ELF_REL_SYM(format, reloc);
name = get_elf_symbol_name(format, dynsym, dynstr, index);
//printf("got a jump ! >> %d - %s\n", index, name);
if (name == NULL)
{
/* FIXME */
name = "unknown";
}
symbol = g_binary_symbol_new(STP_FUNCTION, name, ELF_REL(format, reloc, r_offset));
g_binary_format_add_symbol(G_BIN_FORMAT(format), symbol);
break;
default:
printf("Relocation not supported (%lld) !\n", ELF_REL_TYPE(format, reloc));
break;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* plt = section .plt trouvée (points d'entrées dynamiques). *
* rel = section .rel.plt présentant la table des symboles. *
* dynsym = section listant tous les symboles. *
* dynstr = section contenant le nom de ces symboles. *
* *
* Description : Déduit les adresses effectives des appels externes. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool find_elf_x86_dynamic_symbols(GElfFormat *format, const elf_shdr *plt, const elf_shdr *rel, const elf_shdr *dynsym, const elf_shdr *dynstr)
{
GArchInstruction **instructions; /* Instructions décodées */
size_t count; /* Quantité d'instructions */
size_t i; /* Boucle de parcours */
instructions = decode_elf_relocations(format, plt, &count);
switch (ELF_HDR(format, format->header, e_type))
{
case ET_EXEC:
translate_exe_elf_relocations(format, instructions, count);
break;
case ET_DYN:
translate_dyn_elf_relocations(format, instructions, count, rel, dynsym, dynstr);
break;
}
for (i = 0; i < count; i++)
/* TODO : free instructions[i] */;
if (instructions != NULL)
free(instructions);
return true;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* plt = section .plt trouvée (points d'entrées dynamiques). *
* 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 : - *
* *
******************************************************************************/
GArchInstruction **decode_elf_relocations(GElfFormat *format, const elf_shdr *plt, size_t *count)
{
GArchInstruction **result; /* Liste à renvoyer */
off_t plt_start; /* Début de section */
off_t plt_size; /* Taille de section */
vmpa_t plt_address; /* Adresse virtuelle associée */
GArchProcessor *proc; /* Processeur pour le décodage */
off_t pos; /* Tête de lecture */
vmpa_t address; /* Adresse virtuelle courante */
GArchInstruction *instr; /* Instruction décodée */
result = NULL;
*count = 0;
get_elf_section_content(format, plt, &plt_start, &plt_size, &plt_address);
proc = get_arch_processor_for_type(APT_386);
for (pos = 0; pos < plt_size; )
{
address = plt_address + pos;
instr = g_arch_processor_decode_instruction(proc, NULL /*FIXME*/, &G_BIN_FORMAT(format)->content[plt_start],
&pos, plt_size, 0/* FIXME*/, address);
result = (GArchInstruction **)realloc(result, ++(*count) * sizeof(GArchInstruction *));
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_exe_elf_relocations(GElfFormat *format, GArchInstruction **instructions, size_t count)
{
size_t i; /* Boucle de parcours #1 */
X86Opcodes opcode_n0; /* Opcode de l'instruction n */
X86Opcodes opcode_n1; /* Opcode de l'instruction n+1 */
X86Opcodes opcode_n2; /* Opcode de l'instruction n+2 */
const GArchOperand *operand; /* Valeur du saut */
vmpa_t address; /* Adresse virtuelle finale */
GBinSymbol **symbols; /* Liste des symboles existants*/
size_t symbols_count; /* Taille de cette liste */
size_t j; /* Boucle de parcours #2 */
size_t new_len; /* Taille du nouveau nom */
char *new_name; /* Nom avec suffixe @plt */
GBinRoutine *routine; /* Nouvelle routine déduite */
GBinSymbol *symbol; /* Nouveau symbole construit */
for (i = 0; (i + 2) < count; )
{
opcode_n0 = g_x86_instruction_get_opcode(G_X86_INSTRUCTION(instructions[i]));
opcode_n1 = g_x86_instruction_get_opcode(G_X86_INSTRUCTION(instructions[i + 1]));
opcode_n2 = g_x86_instruction_get_opcode(G_X86_INSTRUCTION(instructions[i + 2]));
if (opcode_n0 == XOP_JMP_RM1632
&& opcode_n1 == XOP_PUSH_IMM1632
&& opcode_n2 == XOP_JMP_REL1632)
{
operand = g_arch_instruction_get_operand(instructions[i], 0);
if (g_imm_operand_to_vmpa_t(G_IMM_OPERAND(operand), &address))
{
symbols = g_binary_format_get_symbols(G_BIN_FORMAT(format), &symbols_count);
for (j = 0; j < symbols_count; j++)
if (g_binary_symbol_get_address(symbols[j]) == address)
{
/* Nom final */
new_len = strlen(g_binary_symbol_to_string(symbols[j])) + 4 + 1;
new_name = calloc(new_len, sizeof(char));
snprintf(new_name, new_len, "%s@plt", g_binary_symbol_to_string(symbols[j]));
g_arch_instruction_get_location(instructions[i], NULL, NULL, &address);
/* Routine */
routine = try_to_demangle_routine(g_binary_symbol_to_string(symbols[j]));
g_binary_routine_set_address(routine, address);
g_binary_format_add_routine(G_BIN_FORMAT(format), routine);
/* Symbole uniquement */
symbol = g_binary_symbol_new(STP_FUNCTION, new_name, address);
g_binary_symbol_attach_routine(symbol, routine);
g_binary_format_add_symbol(G_BIN_FORMAT(format), symbol);
break;
}
}
i += 3;
}
else i++;
}
}
/******************************************************************************
* *
* 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_dyn_elf_relocations(GElfFormat *format, GArchInstruction **instructions, size_t count, const elf_shdr *rel, const elf_shdr *dynsym, const elf_shdr *dynstr)
{
off_t rel_start; /* Début de la zone à traiter */
off_t rel_size; /* Taille de cette même zone */
size_t i; /* Boucle de parcours #1 */
X86Opcodes opcode_n0; /* Opcode de l'instruction n */
X86Opcodes opcode_n1; /* Opcode de l'instruction n+1 */
X86Opcodes opcode_n2; /* Opcode de l'instruction n+2 */
const GArchOperand *operand; /* Valeur du saut */
off_t pos; /* Tête de lecture */
bool negative; /* Tête de lecture invalide ? */
elf_rel reloc; /* Infos de relocalisation */
off_t index; /* Indice de la portion visée */
const char *name; /* Nom du symbole trouvé */
size_t new_len; /* Taille du nouveau nom */
char *new_name; /* Nom avec suffixe @plt */
vmpa_t address; /* Adresse virtuelle finale */
GBinRoutine *routine; /* Nouvelle routine déduite */
GBinSymbol *symbol; /* Nouveau symbole construit */
get_elf_section_content(format, rel, &rel_start, &rel_size, NULL);
for (i = 0; (i + 2) < count; )
{
opcode_n0 = g_x86_instruction_get_opcode(G_X86_INSTRUCTION(instructions[i]));
opcode_n1 = g_x86_instruction_get_opcode(G_X86_INSTRUCTION(instructions[i + 1]));
opcode_n2 = g_x86_instruction_get_opcode(G_X86_INSTRUCTION(instructions[i + 2]));
if (opcode_n0 == XOP_JMP_RM1632
&& opcode_n1 == XOP_PUSH_IMM1632
&& opcode_n2 == XOP_JMP_REL1632)
{
operand = g_arch_instruction_get_operand(instructions[i + 1], 0);
if (!g_imm_operand_to_off_t(G_IMM_OPERAND(operand), &pos, &negative))
goto next_op;
if ((pos + ELF_SIZEOF_REL(format)) > rel_size)
goto next_op;
pos += rel_start;
if (!read_elf_relocation(format, &pos, &reloc))
goto next_op;
index = ELF_REL_SYM(format, reloc);
name = get_elf_symbol_name(format, dynsym, dynstr, index);
if (name == NULL)
{
/* FIXME */
name = "unknown";
}
/* Nom final */
new_len = strlen(name) + 4 + 1;
new_name = calloc(new_len, sizeof(char));
snprintf(new_name, new_len, "%s@plt", name);
g_arch_instruction_get_location(instructions[i], NULL, NULL, &address);
/* Routine */
routine = try_to_demangle_routine(name);
g_binary_routine_set_address(routine, address);
g_binary_format_add_routine(G_BIN_FORMAT(format), routine);
/* Symbole uniquement */
symbol = g_binary_symbol_new(STP_FUNCTION, new_name, address);
g_binary_symbol_attach_routine(symbol, routine);
g_binary_format_add_symbol(G_BIN_FORMAT(format), symbol);
next_op:
i += 3;
}
else i++;
}
}