/* Chrysalide - Outil d'analyse de fichiers binaires
* disassembler.c - encadrement des phases de désassemblage
*
* Copyright (C) 2010-2014 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 "disassembler.h"
#include
#include
#include
#include
#include "fetch.h"
#include "output.h"
#include "instructions.h"
#include "routines.h"
#include "../../decomp/lang/asm.h"
#include "../../format/format.h"
#include "../../glibext/delayed-int.h"
#include "../../gui/panels/log.h"
#include "../../plugins/pglist.h"
/* ------------------------ DESASSEMBLAGE DE BINAIRE DIFFERE ------------------------ */
/* Ensembles binaires à désassembler (instance) */
struct _GDelayedDisassembly
{
GDelayedWork parent; /* A laisser en premier */
GLoadedBinary *binary; /* Destinataire final */
GExeFormat *format; /* Format du binaire représenté*/
GArchInstruction **instrs; /* Instructions résultantes */
GCodeBuffer *buffer; /* Tampon pour le rendu */
};
/* Ensembles binaires à désassembler (classe) */
struct _GDelayedDisassemblyClass
{
GDelayedWorkClass parent; /* A laisser en premier */
};
/* Initialise la classe des tâches de désassemblage différé. */
static void g_delayed_disassembly_class_init(GDelayedDisassemblyClass *);
/* Initialise une tâche de désassemblage différé. */
static void g_delayed_disassembly_init(GDelayedDisassembly *);
/* Crée une tâche de désassemblage différé. */
static GDelayedDisassembly *g_delayed_disassembly_new(GLoadedBinary *, GArchInstruction **, GCodeBuffer *);
/* Opère sur toutes les instructions. */
static void process_all_instructions(wgroup_id_t, GtkStatusStack *, const char *, ins_fallback_cb, GArchProcessor *, GProcContext *, GExeFormat *);
/* Opère sur toutes les routines. */
static void process_all_routines(wgroup_id_t, GtkStatusStack *, const char *, rtn_fallback_cb, GArchProcessor *, GExeFormat *);
/* Assure le désassemblage en différé. */
static void g_delayed_disassembly_process(GDelayedDisassembly *, GtkStatusStack *);
/* -------------------------- GESTION GLOBALE DE PROCEDURE -------------------------- */
/* Construit la description d'introduction du désassemblage. */
static void build_disass_prologue(GCodeBuffer *, const char *, const char *);
/* ---------------------------------------------------------------------------------- */
/* DESASSEMBLAGE DE BINAIRE DIFFERE */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les tâches de désassemblage différé. */
G_DEFINE_TYPE(GDelayedDisassembly, g_delayed_disassembly, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches de désassemblage différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_disassembly_class_init(GDelayedDisassemblyClass *klass)
{
GDelayedWorkClass *work; /* Version en classe parente */
work = G_DELAYED_WORK_CLASS(klass);
work->run = (run_task_fc)g_delayed_disassembly_process;
}
/******************************************************************************
* *
* Paramètres : disass = instance à initialiser. *
* *
* Description : Initialise une tâche de désassemblage différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_disassembly_init(GDelayedDisassembly *disass)
{
}
/******************************************************************************
* *
* Paramètres : binary = binaire chargé en attente des résultats. *
* format = format du binaire représenté. *
* instrs = emplacement pour la liste d'instructions. *
* buffer = tampon de sortie pour les instructions. *
* *
* Description : Crée une tâche de désassemblage différé. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
static GDelayedDisassembly *g_delayed_disassembly_new(GLoadedBinary *binary, GArchInstruction **instrs, GCodeBuffer *buffer)
{
GDelayedDisassembly *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_DELAYED_DISASSEMBLY, NULL);
result->binary = binary;
result->format = g_loaded_binary_get_format(binary);
result->instrs = instrs;
result->buffer = buffer;
return result;
}
/******************************************************************************
* *
* Paramètres : gid = groupe de travail impliqué. *
* status = barre de statut à tenir informée. *
* msg = message à faire paraître pour la patience. *
* fallback = routine de traitements particuliers. *
* proc = ensemble d'instructions désassemblées. *
* ctx = contexte fourni pour suivre le désassemblage. *
* format = accès aux données du binaire d'origine. *
* *
* Description : Opère sur toutes les instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void process_all_instructions(wgroup_id_t gid, GtkStatusStack *status, const char *msg, ins_fallback_cb fallback, GArchProcessor *proc, GProcContext *ctx, GExeFormat *format)
{
guint runs_count; /* Qté d'exécutions parallèles */
size_t ins_count; /* Quantité d'instructions */
size_t run_size; /* Volume réparti par exécution*/
GWorkQueue *queue; /* Gestionnaire de différés */
activity_id_t id; /* Identifiant de progression */
guint i; /* Boucle de parcours */
size_t begin; /* Début de bloc de traitement */
size_t end; /* Fin d'un bloc de traitement */
GInstructionsStudy *study; /* Tâche d'étude à programmer */
runs_count = g_get_num_processors();
ins_count = g_arch_processor_count_disassembled_instructions(proc);
run_size = ins_count / runs_count;
queue = get_work_queue();
id = gtk_status_stack_add_activity(status, msg, ins_count);
for (i = 0; i < runs_count; i++)
{
begin = i * run_size;
if ((i + 1) == runs_count)
end = ins_count;
else
end = begin + run_size;
study = g_instructions_study_new(proc, ctx, G_BIN_FORMAT(format), begin, end, id, fallback);
g_work_queue_schedule_work(queue, G_DELAYED_WORK(study), gid);
}
g_work_queue_wait_for_completion(queue, gid);
gtk_status_stack_remove_activity(status, id);
}
/******************************************************************************
* *
* Paramètres : gid = groupe de travail impliqué. *
status = barre de statut à tenir informée. *
* msg = message à faire paraître pour la patience. *
* fallback = routine de traitements particuliers. *
* proc = ensemble d'instructions désassemblées. *
* format = accès aux données du binaire d'origine. *
* *
* Description : Opère sur toutes les routines. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void process_all_routines(wgroup_id_t gid, GtkStatusStack *status, const char *msg, rtn_fallback_cb fallback, GArchProcessor *proc, GExeFormat *format)
{
mrange_t *exe_ranges; /* Liste de zones exécutables */
size_t exe_count; /* Nombre de ces zones */
GBinRoutine **routines; /* Liste des routines trouvées */
size_t routines_count; /* Nombre de ces routines */
guint runs_count; /* Qté d'exécutions parallèles */
size_t run_size; /* Volume réparti par exécution*/
GWorkQueue *queue; /* Gestionnaire de différés */
activity_id_t id; /* Identifiant de progression */
guint i; /* Boucle de parcours */
size_t begin; /* Début de bloc de traitement */
size_t end; /* Fin d'un bloc de traitement */
GRoutinesStudy *study; /* Tâche d'étude à programmer */
exe_ranges = g_exe_format_get_x_ranges(format, &exe_count);
routines = g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count);
runs_count = g_get_num_processors();
run_size = routines_count / runs_count;
queue = get_work_queue();
id = gtk_status_stack_add_activity(status, msg, routines_count);
for (i = 0; i < runs_count; i++)
{
begin = i * run_size;
if ((i + 1) == runs_count)
end = routines_count;
else
end = begin + run_size;
study = g_routines_study_new(proc, exe_ranges, exe_count, routines, routines_count,
begin, end, id, fallback);
g_work_queue_schedule_work(queue, G_DELAYED_WORK(study), gid);
}
g_work_queue_wait_for_completion(queue, gid);
gtk_status_stack_remove_activity(status, id);
if (exe_ranges != NULL)
free(exe_ranges);
}
/******************************************************************************
* *
* Paramètres : disass = analyse à mener. *
* status = barre de statut à tenir informée. *
* *
* Description : Assure le désassemblage en différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_disassembly_process(GDelayedDisassembly *disass, GtkStatusStack *status)
{
wgroup_id_t gid; /* Identifiant pour les tâches */
//GBinFormat *format; /* Format du fichier binaire */
GArchProcessor *proc; /* Architecture du binaire */
GProcContext *ctx; /* Contexte de suivi dédié */
//size_t i; /* Boucle de parcours */
//format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
proc = g_loaded_binary_get_processor(disass->binary);
ctx = g_arch_processor_get_context(proc);
gid = g_work_queue_define_work_group(get_work_queue());
/* Première étape */
//id = gtk_extended_status_bar_push(statusbar, _("Disassembling..."), true);
process_disassembly_event(PGA_DISASSEMBLY_STARTED, disass->binary);
*disass->instrs = disassemble_binary_content(disass->binary, ctx, gid, status);
g_arch_processor_set_disassembled_instructions(proc, *disass->instrs);
// plugins //////////////////////////
process_disassembly_event(PGA_DISASSEMBLY_RAW, disass->binary);
process_all_instructions(gid, status, _("Calling 'link' hook on all instructions..."),
g_instructions_study_do_link_operation,
proc, ctx, disass->format);
// plugins //////////////////////////
process_disassembly_event(PGA_DISASSEMBLY_HOOKED_LINK, disass->binary);
//gtk_extended_status_bar_remove(statusbar, id);
//run_plugins_on_binary(disass->binary, PGA_BINARY_DISASSEMBLED, true);
process_all_instructions(gid, status, _("Calling 'post' hook on all instructions..."),
g_instructions_study_do_post_operation,
proc, ctx, disass->format);
// plugins //////////////////////////
process_disassembly_event(PGA_DISASSEMBLY_HOOKED_POST, disass->binary);
/**
* TODO : établir les couvertures de fonctions,
* pour être en mesure de disposer de résolution de type XXX+yyy lors
* de l'établissement des liens.
*/
/* Seconde étape */
process_all_routines(gid, status,
_("Finding remaining limits..."),
g_routines_study_compute_limits, proc, disass->format);
// plugins //////////////////////////
process_disassembly_event(PGA_DISASSEMBLY_LIMITED, disass->binary);
/* Troisième étape */
process_all_instructions(gid, status, _("Establishing links betweek all instructions..."),
g_instructions_study_establish_links,
proc, ctx, disass->format);
// plugins //////////////////////////
process_disassembly_event(PGA_DISASSEMBLY_LINKED, disass->binary);
/* Quatrième étape */
// -- old -- id = gtk_extended_status_bar_push(statusbar, _("Detecting loops..."), true);
// -- old -- detect_loops_in_code(proc, routines, routines_count, statusbar, 0/*id*/);
// -- old -- gtk_extended_status_bar_remove(statusbar, 0/*id*/);
///
// plugins //////////////////////////
// -- old -- process_disassembly_event(PGA_DISASSEMBLY_LOOPS, disass->binary);
//////////////////////////////////////
// Control-flow analysis...
#if 1
process_all_routines(gid, status,
_("Control-flow analysis for routines..."),
g_routines_study_handle_blocks, proc, disass->format);
#endif
/* Cinquième étape */
// -- old -- id = gtk_extended_status_bar_push(statusbar, _("Grouping routines instructions..."), true);
//qsort(routines, routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_rcompare);
// -- old -- group_routines_instructions(proc, routines, routines_count, statusbar, 0/*id*/);
// -- old -- gtk_extended_status_bar_remove(statusbar, 0/*id*/);
//run_plugins_on_binary(disass->binary, PGA_BINARY_GROUPED, true);
// -- old -- process_disassembly_event(PGA_DISASSEMBLY_GROUPED, disass->binary);
/* Sixième étape */
// -- old -- id = gtk_extended_status_bar_push(statusbar, _("Ranking each instructions block..."), true);
//qsort(routines, routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_rcompare);
// -- old -- rank_routines_blocks(routines, routines_count, statusbar, 0/*id*/);
// -- old -- gtk_extended_status_bar_remove(statusbar, 0/*id*/);
//run_plugins_on_binary(disass->binary, PGA_BINARY_GROUPED, true);
// -- old -- process_disassembly_event(PGA_DISASSEMBLY_RANKED, disass->binary);
/* Septième étape */
//id = gtk_extended_status_bar_push(statusbar, _("Printing disassembled code..."), true);
print_disassembled_instructions(disass->buffer, disass->format, proc, status);
/* Rajout de tous les éléments mis en place automatiquement */
void add_to_collection(GDbItem *item, GLoadedBinary *binary)
{
g_object_ref(G_OBJECT(item));
g_loaded_binary_add_to_collection(binary, item);
}
g_proc_context_foreach_db_item(ctx, (GFunc)add_to_collection, disass->binary);
/* Nettoyage final et sortie ! */
g_object_unref(G_OBJECT(ctx));
g_object_unref(G_OBJECT(proc));
process_disassembly_event(PGA_DISASSEMBLY_ENDED, disass->binary);
//gtk_extended_status_bar_remove(statusbar, 0/*id*/);
//run_plugins_on_binary(disass->binary, PGA_BINARY_PRINTED, true);
}
/* ---------------------------------------------------------------------------------- */
/* GESTION GLOBALE DE PROCEDURE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : buffer = tampon de destination pour le texte. *
* filename = nom du fichier ciblé à décompiler. *
* data = données en mémoire pour l'empreinte. *
* length = quantité de données à prendre en compte. *
* *
* Description : Construit la description d'introduction du désassemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void build_disass_prologue(GCodeBuffer *buffer, const char *filename, const char *checksum)
{
GLangOutput *output; /* Modèle de sortie adéquat */
GBufferLine *line; /* Ligne de destination */
bool managed; /* Groupe déjà défini ? */
size_t len; /* Taille du texte */
char *content; /* Contenu textuel d'une ligne */
output = g_asm_output_new();
line = g_lang_output_start_comments(output, buffer);
if (line != NULL)
{
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_buffer_line_add_flag(line, BLF_WIDTH_MANAGER);
g_code_buffer_append_new_line(buffer, line);
}
managed = (line != NULL);
/* Introduction */
line = g_lang_output_continue_comments(output, buffer,
SL(_("Disassembly generated by Chrysalide")));
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
if (!managed)
g_buffer_line_add_flag(line, BLF_WIDTH_MANAGER);
g_code_buffer_append_new_line(buffer, line);
line = g_lang_output_continue_comments(output, buffer,
SL(_("Chrysalide is free software - © 2008-2015 Cyrille Bagard")));
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_code_buffer_append_new_line(buffer, line);
line = g_lang_output_continue_comments(output, buffer, NULL, 0);
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_code_buffer_append_new_line(buffer, line);
/* Fichier */
len = strlen(_("File: ")) + strlen(filename) + 1;
content = (char *)calloc(len, sizeof(char));
snprintf(content, len, "%s%s", _("File: "), filename);
line = g_lang_output_continue_comments(output, buffer, content, len - 1);
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_code_buffer_append_new_line(buffer, line);
free(content);
/* Checksum SHA256 */
len = strlen(_("Sha256: ")) + strlen(checksum);
content = (char *)calloc(len + 1, sizeof(char));
snprintf(content, len + 1, "%s%s", _("Sha256: "), checksum);
line = g_lang_output_continue_comments(output, buffer, content, len - 1);
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_code_buffer_append_new_line(buffer, line);
free(content);
/* Ligne de séparation */
line = g_lang_output_continue_comments(output, buffer, NULL, 0);
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_code_buffer_append_new_line(buffer, line);
/* Conclusion */
line = g_lang_output_end_comments(output, buffer);
if (line != NULL)
{
g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
g_code_buffer_append_new_line(buffer, line);
}
g_object_unref(G_OBJECT(output));
}
/******************************************************************************
* *
* Paramètres : binary = représentation de binaire chargé. *
* parts = parties binaires à désassembler. *
* count = nombre de parties à traiter. *
* instrs = liste des instructions chargées. [OUT] *
* buffer = tampon de code mis en place. [OUT] *
* ack = fonction à appeler une fois l'opération terminée. *
* *
* Description : Procède au désassemblage d'un contenu binaire donné. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void disassemble_binary(GLoadedBinary *binary, GArchInstruction **instrs, GCodeBuffer **buffer, disassembly_ack_fc ack)
{
GBinFormat *format; /* Format associé au binaire */
GBinContent *content; /* Contenu bianire manipulé */
const gchar *checksum; /* Identifiant de binaire */
GDelayedDisassembly *disass; /* Désassemblage à mener */
GWorkQueue *queue; /* Gestionnaire de différés */
*buffer = g_code_buffer_new(BLC_ASSEMBLY);
format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
content = g_binary_format_get_content(format);
checksum = g_binary_content_get_checksum(content);
g_object_unref(G_OBJECT(content));
build_disass_prologue(*buffer, g_binary_content_describe(content, true), checksum);
disass = g_delayed_disassembly_new(binary, instrs, *buffer);
g_signal_connect(disass, "work-completed", G_CALLBACK(ack), binary);
queue = get_work_queue();
g_work_queue_schedule_work(queue, G_DELAYED_WORK(disass), DEFAULT_WORK_GROUP);
}