/* OpenIDA - Outil d'analyse de fichiers binaires
* theseus.c - décompilation en fonction du flot d'exécution
*
* Copyright (C) 2010-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 "theseus.h"
#include
#include
#include
/* Traite en premier lieu les adresses marquées "impératives". */
static bool register_white_list(GOpenidaBinary *);
/* Indique si une ligne peut faire l'objet d'un nouveau suivi. */
static bool can_line_be_followed(const GRenderingLine *);
/* Indique si une ligne a déjà été traitée lors d'un suivi. */
static bool can_line_be_processed(const GRenderingLine *);
/* Marque une ligne comme ayant été traitée. */
static void mark_line_as_done(GRenderingLine *, bool);
/* Suit le flot d'exécution à partir d'un point donné. */
static bool follow_flow_from_line(GOpenidaBinary *, vmpa_t);
/* Désassemble une nouvelle instruction à partir d'une adresse. */
static GRenderingLine *disassemble_target_address(GOpenidaBinary *, GRenderingLine *, vmpa_t);
/* Insère dans le flux existant les nouvelles lignes crées. */
static bool insert_new_lines_into_flow(GOpenidaBinary *, GRenderingLine *, GRenderingLine *);
/*
mov edx, 0x80480fb
call [edx]
mov edx, 0x80480fb
add edx, 0x8
call [edx]
mov edx, 0x80480fb
add edx, 0x10
call [edx]
mov edx, 0x80480fb
add edx, 0xc
call [edx]
mov edx, 0x80480fb
add edx, 0x14
call [edx]
mov edx, 0x80480fb
add edx, 0x4
call [edx]
*/
static const vmpa_t _white_list[] = {
0x80480fbull,
0x8048103ull,
0x804810bull,
0x8048107ull,
0x804810full,
0x80480ffull
};
static const size_t _white_list_count = 6;
/******************************************************************************
* *
* Paramètres : ref = espace de référencement global. *
* *
* Description : Initialise le greffon pour les bornes de routine. *
* *
* Retour : true. *
* *
* Remarques : - *
* *
******************************************************************************/
G_MODULE_EXPORT bool init_plugin(GObject *ref)
{
return true;
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Fournit une indication sur le type d'opération(s) menée(s). *
* *
* Retour : Description d'une action. *
* *
* Remarques : - *
* *
******************************************************************************/
G_MODULE_EXPORT PluginAction get_plugin_action(void)
{
return PGA_CODE_PROCESS;
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* action = action attendue. *
* *
* Description : Exécute une action définie sur un binaire chargé. *
* *
* Retour : true si une action a été menée, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
G_MODULE_EXPORT bool execute_action_on_binary(GOpenidaBinary *binary, PluginAction action)
{
bool result; /* Bilan à retourner */
GRenderingLine *lines; /* Lignes désassemblées */
GExeFormat *format; /* Format du binaire traité */
vmpa_t start; /* Point de départ de l'action */
GRenderingOptions *options;
GRenderingLine *iter;
lines = g_openida_binary_get_lines(binary);
format = g_openida_binary_get_format(binary);
start = g_exe_format_get_entry_point(format);
result = register_white_list(binary);
result &= follow_flow_from_line(binary, start);
//follow_flow_from_line(binary, 0x0804810bull);
options = g_openida_binary_get_options(binary);
for (iter = lines; iter != NULL; iter = g_rendering_line_get_next_iter(lines, iter, NULL))
{
printf(" >>>>>> %c%c ",
g_object_get_data(G_OBJECT(iter), "theseus_white") != NULL ? '!' : ' ',
g_object_get_data(G_OBJECT(iter), "theseus_done") != NULL ? '*' : ' ');
g_content_exporter_add_text(G_CONTENT_EXPORTER(iter), options, 0/*MRD_BLOCK*/, stdout);
fflush(stdout);
printf("\n");
}
//exit(-1);
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* *
* Description : Traite en premier lieu les adresses marquées "impératives". *
* *
* Retour : true si le déroulement s'est bien effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool register_white_list(GOpenidaBinary *binary)
{
bool result; /* Bilan à retourner */
GRenderingLine *lines; /* Lignes désassemblées */
size_t i; /* Boucle de parcours */
GRenderingLine *target; /* Ligne à transformer ? */
GRenderingLine *last; /* Nouvelle ligne principale */
GRenderingLine *new; /* Nouvelles lignes de rendu */
result = true;
lines = g_openida_binary_get_lines(binary);
for (i = 0; i < _white_list_count && result; i++)
{
target = g_rendering_line_find_by_address(lines, NULL, _white_list[i]);
target = g_rendering_line_loop_for_code(target, NULL);
new = disassemble_target_address(binary, target, _white_list[i]);
last = g_rendering_line_get_last_iter(new, NULL);
mark_line_as_done(last, true);
result = insert_new_lines_into_flow(binary, target, new);
result &= follow_flow_from_line(binary, _white_list[i]);
}
return result;
}
/******************************************************************************
* *
* Paramètres : line = ligne de rendu à analyser. *
* *
* Description : Indique si une ligne peut faire l'objet d'un nouveau suivi. *
* *
* Retour : true si le suivi peut continuer, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool can_line_be_followed(const GRenderingLine *line)
{
bool result; /* Bilan d'analyse à renvoyer */
result = true;
if (g_object_get_data(G_OBJECT(line), "theseus_done") != NULL)
result = (g_object_get_data(G_OBJECT(line), "theseus_white") != NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : line = ligne de rendu à analyser. *
* *
* Description : Indique si une ligne a déjà été traitée lors d'un suivi. *
* *
* Retour : true si le suivi peut continuer, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool can_line_be_processed(const GRenderingLine *line)
{
return (g_object_get_data(G_OBJECT(line), "theseus_done") == NULL);
}
/******************************************************************************
* *
* Paramètres : line = ligne de rendu à traiter. *
* white = type de marquage à apposer (liste blanche ou normal).*
* *
* Description : Marque une ligne comme ayant été traitée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void mark_line_as_done(GRenderingLine *line, bool white)
{
if (white)
g_object_set_data(G_OBJECT(line), "theseus_white", line);
else
g_object_set_data(G_OBJECT(line), "theseus_done", line);
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* start = point de départ de la procédure. *
* *
* Description : Suit le flot d'exécution à partir d'un point donné. *
* *
* Retour : true si le déroulement s'est bien effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool follow_flow_from_line(GOpenidaBinary *binary, vmpa_t start)
{
bool result; /* Bilan de opérations */
GRenderingLine *lines; /* Lignes désassemblées */
GRenderingLine *first; /* Première ligne à traiter */
vmpa_t addr; /* Adresse référencée */
GRenderingLine *new; /* Nouvelles lignes de rendu */
GRenderingLine *iter; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction à ausculter */
InstructionLinkType type; /* Type de référence */
GRenderingLine *target; /* Ligne visée par la référence*/
lines = g_openida_binary_get_lines(binary);
first = g_rendering_line_find_by_address(lines, NULL, start);
first = g_rendering_line_loop_for_code(first, NULL);
if (!can_line_be_followed(first))
return true;
//if (start != 0x0804810bull) return true;
printf("###################################### Passage\n");
/* Alignement sur le point d'entrée */
result = true;
addr = get_rendering_line_address(first);
if (addr < start)
{
new = disassemble_target_address(binary, first, start);
result = insert_new_lines_into_flow(binary, target, new);
first = g_rendering_line_find_by_address(lines, NULL, start);
//return true;
}
else g_object_set_data(G_OBJECT(first), "theseus_done", binary);
printf("Analyse :: 0x%016llx\n", get_rendering_line_address(first));
/* Poursuite du suivi du flot... */
for (iter = g_rendering_line_get_next_iter(lines, first, NULL);
iter != NULL/* && result*/;
iter = g_rendering_line_get_next_iter(lines, iter, NULL))
{
/* On ne traite que du code ici ! */
if (!G_IS_CODE_LINE(iter)) continue;
/*
printf("testing... 0x%08llx => %d\n",
get_rendering_line_address(iter),
can_line_be_processed(iter));
*/
if (!can_line_be_processed(iter))
break;
instr = g_code_line_get_instruction(G_CODE_LINE(iter));
/* Si le code n'es pas désassemblé ! */
if (G_IS_DB_INSTRUCTION(instr))
{
new = disassemble_target_address(binary, iter, get_rendering_line_address(iter));
result = insert_new_lines_into_flow(binary, iter, new);
if (!result)
{
mark_line_as_done(iter, false);
break;
}
else
{
iter = new;
mark_line_as_done(iter, false);
instr = g_code_line_get_instruction(G_CODE_LINE(iter));
if (!G_IS_DB_INSTRUCTION(instr)) break;
}
}
else mark_line_as_done(iter, false);
type = g_arch_instruction_get_link(instr, &addr);
if (get_rendering_line_address(iter) == 0x0804811aull)
printf(" == got it !! ==\n");
switch (type)
{
case ILT_JUMP:
case ILT_JUMP_IF_FALSE:
case ILT_JUMP_IF_TRUE:
result = follow_flow_from_line(binary, addr);
break;
default:
break;
}
if (get_rendering_line_address(iter) == 0x0804811aull)
printf(" == continue ? %d\n", result);
}
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* old = ligne contenant l'adresse visée à remplacer. *
* target = adresse de la nouvelle instruction à voir. *
* *
* Description : Désassemble une nouvelle instruction à partir d'une adresse. *
* *
* Retour : Nouvelle(s) ligne(s) en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static GRenderingLine *disassemble_target_address(GOpenidaBinary *binary, GRenderingLine *old, vmpa_t target)
{
GRenderingLine *result; /* Lignes en place à renvoyer */
GExeFormat *format; /* Format du binaire fourni */
GArchProcessor *proc; /* Architecture du binaire */
GRenderingOptions *options; /* Options de désassemblage */
bin_t *data; /* Données binaires chargées */
off_t length; /* Taille du code binaire */
vmpa_t old_addr; /* Adresse de ligne à casser */
off_t offset; /* Position dans le contenu */
vmpa_t i; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
GRenderingLine *line; /* Nouvelle ligne de rendu */
result = NULL;
format = g_openida_binary_get_format(binary);
proc = get_arch_processor_from_format(format);
options = g_openida_binary_get_options(binary);
data = g_openida_binary_get_data(binary, &length);
old_addr = get_rendering_line_address(old);
if (!g_exe_format_translate_address_into_offset(format, old_addr, &offset))
return NULL;
/* Si on doit laisser sur le carreau quelques octets perdus... */
for (i = 0; i < (target - old_addr); i++)
{
instr = g_db_instruction_new_from_data(data, &offset, length, 0/*base*/, proc);
g_arch_instruction_set_location(instr, 0/*base*/ + offset - 1, 1, old_addr + i);
line = g_code_line_new(old_addr + i, instr, options);
g_rendering_line_add_to_lines(&result, line);
}
/* Décodage des instructions */
instr = g_arch_processor_decode_instruction(proc, NULL /*FIXME*/, data,
&offset, length, 0/*offset*/, target);
line = g_code_line_new(target, instr, options);
g_rendering_line_add_to_lines(&result, line);
mark_line_as_done(line, false);
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* old = ancienne ligne, point de départ des remplacements. *
* new = nouvelle(s) ligne(s) de représentation. *
* *
* Description : Insère dans le flux existant les nouvelles lignes crées. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool insert_new_lines_into_flow(GOpenidaBinary *binary, GRenderingLine *old, GRenderingLine *new)
{
bool result; /* Bilan de opérations */
GExeFormat *format; /* Format du binaire fourni */
GArchProcessor *proc; /* Architecture du binaire */
GRenderingOptions *options; /* Options de désassemblage */
bin_t *data; /* Données binaires chargées */
off_t length; /* Taille du code binaire */
GRenderingLine **root; /* Racine des lignes de rendu */
GRenderingLine *iter; /* Boucle de parcours */
vmpa_t start; /* Adresse de début du bloc */
vmpa_t end; /* Adresse de fin du bloc */
vmpa_t max; /* Adresse de fin de ligne */
vmpa_t i; /* Boucle de parcours */
off_t offset; /* Position dans le contenu */
GArchInstruction *instr; /* Instruction décodée */
GRenderingLine *line; /* Nouvelle ligne de rendu */
GRenderingLine *last; /* Dernière ligne à vérifier */
format = g_openida_binary_get_format(binary);
proc = get_arch_processor_from_format(format);
options = g_openida_binary_get_options(binary);
data = g_openida_binary_get_data(binary, &length);
/* Etendue du bloc de remplacement */
iter = g_rendering_line_get_last_iter(new, NULL);
start = get_rendering_line_address(new);
end = get_rendering_line_address(iter);
end += get_rendering_line_length(iter) - 1;
/* Bourrage nécessaire ? */
root = g_openida_binary_get_lines_root(binary);
iter = g_rendering_line_find_by_address(*root, NULL, end);
max = get_rendering_line_address(iter);
max += get_rendering_line_length(iter);
for (i = end + 1; i < max; i++)
{
if (!g_exe_format_translate_address_into_offset(format, i, &offset))
/**
* Comme les adresses proviennent à l'origine de partie valide,
* on considère que l'appel est toujours un succès.
* Cela permet d'avoir deux bilan possible : procédure bien déroulée,
* ou bien remplacement impossible pour cause de place déjà prise.
*/
/*return false*/;
instr = g_db_instruction_new_from_data(data, &offset, length, 0/*base*/, proc);
g_arch_instruction_set_location(instr, 0/*base*/ + offset - 1, 1, i);
line = g_code_line_new(i, instr, options);
g_rendering_line_add_to_lines(&new, line);
}
/* S'assure qu'on ne touche à rien de sacré */
result = true;
last = g_rendering_line_find_by_address(*root, NULL, end);
for (iter = g_rendering_line_find_by_address(*root, NULL, start);
iter != NULL && result;
iter = g_rendering_line_get_next_iter(*root, iter, last))
{
result = can_line_be_processed(iter);
}
/* Remplacement en bonne et due forme */
if (result)
{
g_rendering_line_remove_range(root, start, end);
g_rendering_line_insert_lines(root, &new);
}
return result;
}