/* Chrysalide - Outil d'analyse de fichiers binaires
* debugger.c - gestion des différents débogueurs
*
* Copyright (C) 2010-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 "debugger.h"
#include
#include
#include
#include
#include
#include "debugger-int.h"
#include "../common/sort.h"
#include "../glibext/chrysamarshal.h"
#include "../gui/panels/log.h"
#include "../plugins/pglist.h"
/* ---------------------------- TRONC COMMUN DE DEBOGAGE ---------------------------- */
/* Initialise la classe de base des débogueurs. */
static void g_binary_debugger_class_init(GBinaryDebuggerClass *);
/* Initialise une instance de base d'un débogueur. */
static void g_binary_debugger_init(GBinaryDebugger *);
/* Supprime toutes les références externes. */
static void g_binary_debugger_dispose(GBinaryDebugger *);
/* Procède à la libération totale de la mémoire. */
static void g_binary_debugger_finalize(GBinaryDebugger *);
/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */
/* Active un point d'arrêt à un emplacement de mémoire donné. */
static bool g_binary_debugger_insert_memory_breakpoint(GBinaryDebugger *, virt_t, RawBpOrigin, dbg_thread_id_t, virt_t);
/* Désactive un point d'arrêt à un emplacement de mémoire donné. */
static bool g_binary_debugger_remove_memory_breakpoint(GBinaryDebugger *, virt_t);
/* Sème des points d'arrêt sur les instructions suivantes. */
static bool g_binary_debugger_spread_breakpoints(GBinaryDebugger *, dbg_thread_id_t, virt_t, RawBpOrigin, bool);
/* Retire tous les points d'arrêt issus d'un adresse. */
static void g_binary_debugger_remove_same_breakpoints(GBinaryDebugger *, dbg_thread_id_t, virt_t, RawBpOrigin);
/* Met à jour les points d'arrêt suite à un arrêt. */
static bool g_binary_debugger_update_breakpoints_on_stop(GBinaryDebugger *, dbg_thread_id_t, virt_t);
/* ---------------------------------------------------------------------------------- */
/* TRONC COMMUN DE DEBOGAGE */
/* ---------------------------------------------------------------------------------- */
/* Indique le type définit pour une ligne de représentation. */
G_DEFINE_TYPE(GBinaryDebugger, g_binary_debugger, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe de base des débogueurs. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_debugger_dispose;
object->finalize = (GObjectFinalizeFunc)g_binary_debugger_finalize;
g_signal_new("signaled",
G_TYPE_BINARY_DEBUGGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinaryDebuggerClass, signaled),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
g_signal_new("exited",
G_TYPE_BINARY_DEBUGGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinaryDebuggerClass, exited),
NULL, NULL,
g_cclosure_user_marshal_VOID__INT_INT,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
g_signal_new("terminated",
G_TYPE_BINARY_DEBUGGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinaryDebuggerClass, terminated),
NULL, NULL,
g_cclosure_user_marshal_VOID__INT_INT,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
g_signal_new("stopped",
G_TYPE_BINARY_DEBUGGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinaryDebuggerClass, stopped),
NULL, NULL,
g_cclosure_user_marshal_VOID__UINT64,
G_TYPE_NONE, 1, G_TYPE_UINT64);
g_signal_new("halted",
G_TYPE_BINARY_DEBUGGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinaryDebuggerClass, debugger_halted),
NULL, NULL,
g_cclosure_user_marshal_VOID__INT_UINT64_INT,
G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT64, G_TYPE_INT);
g_signal_new("mem-bp-handled",
G_TYPE_BINARY_DEBUGGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinaryDebuggerClass, mem_bp_handled),
NULL, NULL,
g_cclosure_user_marshal_VOID__INT_UINT64_INT,
G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT64);
}
/******************************************************************************
* *
* Paramètres : debugger = instance à initialiser. *
* *
* Description : Initialise une instance de base d'un débogueur. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_debugger_init(GBinaryDebugger *debugger)
{
g_rw_lock_init(&debugger->bp_lock);
}
/******************************************************************************
* *
* Paramètres : debugger = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_debugger_dispose(GBinaryDebugger *debugger)
{
g_object_unref(G_OBJECT(debugger->binary));
g_rw_lock_clear(&debugger->bp_lock);
G_OBJECT_CLASS(g_binary_debugger_parent_class)->dispose(G_OBJECT(debugger));
}
/******************************************************************************
* *
* Paramètres : debugger = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_debugger_finalize(GBinaryDebugger *debugger)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < debugger->bp_count; i++)
fini_raw_breakpoint(debugger->bpoints[i]);
if (debugger->bpoints != NULL)
free(debugger->bpoints);
G_OBJECT_CLASS(g_binary_debugger_parent_class)->finalize(G_OBJECT(debugger));
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* *
* Description : Démarre une procédure de débogage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_attach(GBinaryDebugger *debugger)
{
bool result; /* Bilan à retourner */
GPluginModule **pglist; /* Liste de greffons */
size_t pgcount; /* Taille de cette liste */
size_t i; /* Boucle de parcours */
if (debugger->attach == NULL) result = true;
else result = debugger->attach(debugger);
pgcount = 0;
pglist = NULL;//get_all_plugins_for_action(PGA_DEBUGGER_ATTACH, &pgcount);
if (pgcount > 0)
{
/*
for (i = 0; i < pgcount; i++)
g_plugin_module_handle_debugger(pglist[i], debugger, PGA_DEBUGGER_ATTACH);
*/
free(pglist);
}
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* *
* Description : Démarre une procédure de débogage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_debugger_run(GBinaryDebugger *debugger)
{
debugger->run(debugger);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* *
* Description : Tue une procédure de débogage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_debugger_kill(GBinaryDebugger *debugger)
{
debugger->kill(debugger);
}
/* ------------------- MANIPULATION DES DIFFERENTS THREADS ACTIFS ------------------- */
/* ---------------------------------------------------------------------------------- */
/* MANIPULATION DES DIFFERENTS THREADS ACTIFS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : list = descriptions à supprimer de la mémoire. *
* count = taille de cette liste. *
* *
* Description : Libère la mémoire d'une liste de threads actifs. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void delete_dbg_thread_desc(dbg_thread_desc *list, size_t count)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < count; i++)
free(list[i].name);
if (list != NULL)
free(list);
}
/******************************************************************************
* *
* Paramètres : debugger = instance du module de débogage chargé. *
* names = désignations de tous les threads ou NULL. [OUT] *
* count = nombre de threads actifs. [OUT] *
* *
* Description : Fournit les identifiants de tous les threads actifs. *
* *
* Retour : Liste des threads décomptés, à libérer de la mémoire ensuite.*
* *
* Remarques : - *
* *
******************************************************************************/
dbg_thread_desc *g_binary_debugger_list_all_threads(GBinaryDebugger *debugger, size_t *count)
{
dbg_thread_desc *result; /* Liste à retourner */
*count = 0;
result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->all_threads(debugger, count);
if (result != NULL)
{
int cmp_dbg_thread_desc(const dbg_thread_desc *a, const dbg_thread_desc *b)
{
int status; /* Bilan à retourner */
if (a->id < b->id)
status = -1;
if (a->id > b->id)
status = 1;
else
status = -1;
return status;
}
qsort(result, *count, sizeof(dbg_thread_desc), (__compar_fn_t)cmp_dbg_thread_desc);
}
return result;
}
/* ---------------------------------------------------------------------------------- */
/* ENTREES / SORTIES BASIQUES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 8 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_memory_u8(GBinaryDebugger *debugger, virt_t addr, uint8_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 8, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 16 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_memory_u16(GBinaryDebugger *debugger, virt_t addr, uint16_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 16, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 32 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_memory_u32(GBinaryDebugger *debugger, virt_t addr, uint32_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 32, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 64 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_memory_u64(GBinaryDebugger *debugger, virt_t addr, uint64_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 64, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* len = taille attendue de la valeur en octets. *
* *
* Description : Lit une valeur de taille quelconque à une adresse arbitraire.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_memory_data(GBinaryDebugger *debugger, virt_t addr, uint8_t *value, size_t len)
{
bool result; /* Bilan à retourner */
size_t iter; /* Tête de lecture / écriture */
size_t remaining; /* Quantité restant à replacer */
result = true;
iter = 0;
remaining = len;
while (result && remaining > 0)
{
if (remaining >= 8)
{
result = g_binary_debugger_read_memory_u64(debugger, addr + iter, (uint64_t *)&value[iter]);
iter += 8;
remaining -= 8;
}
else if (remaining >= 4)
{
result = g_binary_debugger_read_memory_u32(debugger, addr + iter, (uint32_t *)(&value[iter]));
iter += 4;
remaining -= 4;
}
else if (remaining >= 2)
{
result = g_binary_debugger_read_memory_u16(debugger, addr + iter, (uint16_t *)&value[iter]);
iter += 2;
remaining -= 2;
}
else if (remaining >= 1)
{
result = g_binary_debugger_read_memory_u8(debugger, addr + iter, (uint8_t *)&value[iter]);
iter += 1;
remaining -= 1;
}
else
assert(false);
}
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir modifier. *
* value = emplacement de la valeur à inscrire. *
* *
* Description : Ecrit une valeur de 8 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_memory_u8(GBinaryDebugger *debugger, virt_t addr, const uint8_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 8, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir modifier. *
* value = emplacement de la valeur à inscrire. *
* *
* Description : Ecrit une valeur de 16 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_memory_u16(GBinaryDebugger *debugger, virt_t addr, const uint16_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 16, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir modifier. *
* value = emplacement de la valeur à inscrire. *
* *
* Description : Ecrit une valeur de 32 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_memory_u32(GBinaryDebugger *debugger, virt_t addr, const uint32_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 32, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir modifier. *
* value = emplacement de la valeur à inscrire. *
* *
* Description : Ecrit une valeur de 64 bits à une adresse arbitraire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_memory_u64(GBinaryDebugger *debugger, virt_t addr, const uint64_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 64, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* addr = emplacement en mémoire à venir modifier. *
* value = emplacement de la valeur à incrire. *
* len = taille de la valeur fournie en octets. *
* *
* Description : Ecrit une valeur de taille quelconque à une adresse donnée. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_memory_data(GBinaryDebugger *debugger, virt_t addr, const uint8_t *value, size_t len)
{
bool result; /* Bilan à retourner */
size_t iter; /* Tête de lecture / écriture */
size_t remaining; /* Quantité restant à replacer */
result = true;
iter = 0;
remaining = len;
while (result && remaining > 0)
{
if (remaining >= 8)
{
result = g_binary_debugger_write_memory_u64(debugger, addr + iter, (uint64_t *)&value[iter]);
iter += 8;
remaining -= 8;
}
else if (remaining >= 4)
{
result = g_binary_debugger_write_memory_u32(debugger, addr + iter, (uint32_t *)&value[iter]);
iter += 4;
remaining -= 4;
}
else if (remaining >= 2)
{
result = g_binary_debugger_write_memory_u16(debugger, addr + iter, (uint16_t *)&value[iter]);
iter += 2;
remaining -= 2;
}
else if (remaining >= 1)
{
result = g_binary_debugger_write_memory_u8(debugger, addr + iter, (uint8_t *)&value[iter]);
iter += 1;
remaining -= 1;
}
else
assert(false);
}
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* group = éventuel groupe de registres ciblé ou NULL. *
* count = nombre d'éléments dans la liste de noms. [OUT] *
* *
* Description : Liste l'ensemble des registres appartenant à un groupe. *
* *
* Retour : Liste de noms à libérer de la mémoire après utilisation. *
* *
* Remarques : - *
* *
******************************************************************************/
char **g_binary_debugger_get_register_names(const GBinaryDebugger *debugger, const char *group, size_t *count)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_reg_names(debugger, group, count);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* name = désignation du registre visé. *
* *
* Description : Indique la taille associée à un registre donné. *
* *
* Retour : Taille en bits, ou 0 si le registre n'a pas été trouvé. *
* *
* Remarques : - *
* *
******************************************************************************/
unsigned int g_binary_debugger_get_register_size(const GBinaryDebugger *debugger, const char *name)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_reg_size(debugger, name);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 8 bits à partir d'un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_register_u8(GBinaryDebugger *debugger, const char *reg, uint8_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 8, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 16 bits à partir d'un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_register_u16(GBinaryDebugger *debugger, const char *reg, uint16_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 16, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 32 bits à partir d'un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_register_u32(GBinaryDebugger *debugger, const char *reg, uint32_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 32, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur lue à conserver. [OUT] *
* *
* Description : Lit une valeur de 64 bits à partir d'un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_read_register_u64(GBinaryDebugger *debugger, const char *reg, uint64_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 64, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur à écrire. *
* *
* Description : Ecrit une valeur de 8 bits dans un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_register_u8(GBinaryDebugger *debugger, const char *reg, const uint8_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 8, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur à écrire. *
* *
* Description : Ecrit une valeur de 16 bits dans un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_register_u16(GBinaryDebugger *debugger, const char *reg, const uint16_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 16, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur à écrire. *
* *
* Description : Ecrit une valeur de 32 bits dans un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_register_u32(GBinaryDebugger *debugger, const char *reg, const uint32_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 32, value);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler. *
* reg = désignation humaine du register à consulter. *
* value = emplacement de la valeur à écrire. *
* *
* Description : Ecrit une valeur de 64 bits dans un registre. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_write_register_u64(GBinaryDebugger *debugger, const char *reg, const uint64_t *value)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 64, value);
}
/* ---------------------------------------------------------------------------------- */
/* MANIPULATION DE L'ETAT COURANT */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* pc = adresse de l'instruction courante. [OUT] *
* *
* Description : Détermine le point d'exécution courant. *
* *
* Retour : Bilan de la récupération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_get_current_pc(GBinaryDebugger *debugger, virt_t *pc)
{
bool result; /* Bilan à retourner */
result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_current_pc(debugger, pc);
if (!result)
log_variadic_message(LMT_WARNING, "Unable to get the current PC!");
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* pc = adresse de l'instruction de retour d'appel. [OUT] *
* *
* Description : Détermine l'adresse du premier retour d'appel. *
* *
* Retour : Bilan de la récupération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_get_return_pc(GBinaryDebugger *debugger, virt_t *pc)
{
bool result; /* Bilan à retourner */
virt_t *callstack; /* Pile d'appels courante */
size_t size; /* Hauteur de cette pile */
result = g_binary_debugger_get_call_stack(debugger, &callstack, &size);
if (result && size > 0)
*pc = callstack[size - 1];
if (callstack != NULL)
free(callstack);
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* over = indique si les appels doivent être sautés ou non. *
* count = nombre d'adresses identifiées. *
* *
* Description : Détermine les prochaines probables instructions exécutées. *
* *
* Retour : Liste d'adresses à libérer de la mémoire après usage. *
* *
* Remarques : - *
* *
******************************************************************************/
virt_t *g_binary_debugger_get_next_pcs(GBinaryDebugger *debugger, virt_t pc, bool over, size_t *count)
{
virt_t *result; /* Liste à retourner */
GArchProcessor *proc; /* Processeur lié au binaire */
vmpa2t addr; /* Localisation à cibler */
instr_iter_t *iter; /* Parcours local d'adresses */
GArchInstruction *instr; /* Instruction correspondante */
virt_t ret; /* Adresse de retour d'appel */
size_t dcount; /* Nombre de liens de dest. */
size_t i; /* Boucle de parcours */
instr_link_t *dest; /* Instr. visée par une autre */
const mrange_t *range; /* Emplacement d'instruction */
result = NULL;
*count = 0;
proc = g_loaded_binary_get_processor(debugger->binary);
init_vmpa(&addr, VMPA_NO_PHYSICAL, pc);
iter = g_arch_processor_get_iter_from_address(proc, &addr);
if (iter != NULL)
{
instr = get_instruction_iterator_current(iter);
/* Si l'instruction est un retour à l'appelant */
if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT)
{
if (g_binary_debugger_get_return_pc(debugger, &ret))
{
*count = 1;
result = (virt_t *)malloc(sizeof(virt_t));
result[0] = ret;
}
}
/* Sinon on se penche sur ses destinations */
else
{
g_arch_instruction_lock_dest(instr);
dcount = g_arch_instruction_count_destinations(instr);
for (i = 0; i < dcount; i++)
{
dest = g_arch_instruction_get_destination(instr, i);
switch (dest->type)
{
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
case ILT_LOOP:
(*count)++;
result = (virt_t *)realloc(result, *count * sizeof(virt_t));
range = g_arch_instruction_get_range(dest->linked);
result[*count - 1] = get_virt_addr(get_mrange_addr(range));
break;
case ILT_CALL:
if (!over)
{
(*count)++;
result = (virt_t *)realloc(result, *count * sizeof(virt_t));
range = g_arch_instruction_get_range(dest->linked);
result[*count - 1] = get_virt_addr(get_mrange_addr(range));
}
break;
default:
break;
}
}
g_arch_instruction_unlock_dest(instr);
/* Si tout ça ne donne rien, on se rabat sur l'instruction suivante par défaut */
if (*count == 0)
{
g_object_unref(G_OBJECT(instr));
instr = get_instruction_iterator_next(iter);
if (instr != NULL)
{
*count = 1;
result = (virt_t *)malloc(sizeof(virt_t));
range = g_arch_instruction_get_range(instr);
result[0] = get_virt_addr(get_mrange_addr(range));
}
}
}
if (instr != NULL)
g_object_unref(G_OBJECT(instr));
delete_instruction_iterator(iter);
}
g_object_unref(G_OBJECT(proc));
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à consulter. *
* callstack = pile d'appels reconstituée. [OUT] *
* size = taille de cette pile. [OUT] *
* *
* Description : Remonte la pile d'appels jusqu'au point courant. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_get_call_stack(GBinaryDebugger *debugger, virt_t **callstack, size_t *size)
{
bool result; /* Bilan à retourner */
*callstack = NULL;
*size = 0;
result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_call_stack(debugger, callstack, size);
if (!result)
{
if (*callstack != NULL)
free(*callstack);
*callstack = NULL;
*size = 0;
}
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* pc = adresse de l'instruction courante. *
* *
* Description : Réagit à un arrêt du flot d'exécution. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void on_binary_debugger_stopped(GBinaryDebugger *debugger, virt_t pc)
{
pid_t tid; /* Identifiant du thread */
bool auto_resume; /* Poursuite automatique ? */
tid = 1; // FIXME
auto_resume = g_binary_debugger_update_breakpoints_on_stop(debugger, tid, pc);
if (!auto_resume)
g_signal_emit_by_name(debugger, "stopped", pc);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* pid = éventuel identifiant de processus concerné ou -1. *
* *
* Description : Réagit à la fin de l'opération de débogage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void on_binary_debugger_finished(GBinaryDebugger *debugger, pid_t pid)
{
/* TODO : libérer de la mémoire tous les BP */
}
/* ---------------------------------------------------------------------------------- */
/* GESTION DES POINTS D'ARRET */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* addr = emplacement du point mémoire à traiter. *
* origin = origine de la création du point d'arrêt. *
* tid = identifiant du thread concerné. *
* previous = éventuelle adresse précédent celle du point. *
* *
* Description : Active un point d'arrêt à un emplacement de mémoire donné. *
* *
* Retour : true si la demande a été prise en compte, false sinon. *
* *
* Remarques : L'accès à la liste doit être placée sous la protection de *
* l'appelant. *
* *
******************************************************************************/
static bool g_binary_debugger_insert_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr, RawBpOrigin origin, dbg_thread_id_t tid, virt_t previous)
{
bool result; /* Bilan à retourner */
size_t index; /* Indice de ce point d'arrêt */
bool found; /* Présence d'un point d'arrêt */
raw_breakpoint *bp; /* Point d'arrêt à constituer */
result = false;
found = bsearch_index(&addr, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *),
(__compar_fn_t)compare_raw_breakpoint_with_addr, &index);
if (found)
{
bp = debugger->bpoints[index];
result = true;
}
else
{
bp = G_BINARY_DEBUGGER_GET_CLASS(debugger)->enable_bp(debugger, addr);
if (bp != NULL)
{
debugger->bpoints = (raw_breakpoint **)qinsert(debugger->bpoints, &debugger->bp_count,
sizeof(raw_breakpoint *),
(__compar_fn_t)compare_raw_breakpoints, &bp);
result = true;
}
}
if (result)
set_raw_breakpoint_origin(bp, origin, tid, previous);
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* index = indice du point à supprimer. *
* *
* Description : Désactive un point d'arrêt à un emplacement de mémoire donné.*
* *
* Retour : true si la demande a été prise en compte, false sinon. *
* *
* Remarques : L'accès à la liste doit être placée sous la protection de *
* l'appelant. *
* *
******************************************************************************/
static bool g_binary_debugger_remove_memory_breakpoint(GBinaryDebugger *debugger, size_t index)
{
bool result; /* Bilan à retourner */
raw_breakpoint *bp; /* Point d'arrêt à manipuler */
result = false;
assert(index < debugger->bp_count);
bp = debugger->bpoints[index];
result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->disable_bp(debugger, bp);
if (result)
{
debugger->bpoints = (raw_breakpoint **)_qdelete(debugger->bpoints, &debugger->bp_count,
sizeof(raw_breakpoint *), index);
fini_raw_breakpoint(bp);
}
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* tid = identifiant du thread concerné. *
* pc = adresse de l'instruction courante. *
* type = type de point d'arrêt à insérer. *
* over = indique si les appels doivent être sautés ou non. *
* *
* Description : Sème des points d'arrêt sur les instructions suivantes. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_binary_debugger_spread_breakpoints(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t pc, RawBpOrigin type, bool over)
{
bool result; /* Bilan à retourner */
virt_t *next_list; /* Liste de points d'arrêts */
size_t next_count; /* Taille de cette liste */
GArchProcessor *proc; /* Processeur lié au binaire */
size_t i; /* Boucle de parcours */
vmpa2t addr; /* Format d'adresse complet */
GArchInstruction *instr; /* Instruction ciblée */
bool valid; /* Point d'arrêt pertinent ? */
const char *encoding; /* Encodage de l'instruction */
result = true;
next_list = g_binary_debugger_get_next_pcs(debugger, pc, over, &next_count);
if (next_count == 0)
log_variadic_message(LMT_WARNING, "No instruction found to break after 0x%" PRIx64, pc);
proc = g_loaded_binary_get_processor(debugger->binary);
/**
* Le verrou sur la liste des points est normalement déjà posé.
*/
for (i = 0; i < next_count && result; i++)
{
/**
* Des données peuvent suivre du code (typiquement en ARM).
* On réalise une validation minimale au préalable donc.
*/
init_vmpa(&addr, VMPA_NO_PHYSICAL, next_list[i]);
instr = g_arch_processor_find_instr_by_address(proc, &addr);
if (instr == NULL)
valid = false;
else
{
encoding = g_arch_instruction_get_encoding(instr);
valid = strcmp(encoding, _("String")) != 0
&& strcmp(encoding, _("Raw")) != 0
&& strcmp(encoding, _("Undefined")) != 0;
g_object_unref(G_OBJECT(instr));
}
if (valid)
result = g_binary_debugger_insert_memory_breakpoint(debugger, next_list[i], type, tid, pc);
}
if (next_list != NULL)
free(next_list);
g_object_unref(G_OBJECT(proc));
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* tid = identifiant du thread concerné. *
* prev = adresse d'instruction qui a conduit à des poses. *
* type = type de point d'arrêt à insérer. *
* *
* Description : Retire tous les points d'arrêt issus d'un adresse. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_debugger_remove_same_breakpoints(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t prev, RawBpOrigin type)
{
size_t i; /* Boucle de parcours */
raw_breakpoint *bp; /* Confort de l'accès rapide */
/**
* Le verrou sur la liste des points est normalement déjà posé.
*/
for (i = 0; i < debugger->bp_count; i++)
{
bp = debugger->bpoints[i];
if (has_raw_breakpoint_previous_address(bp, type, tid, prev))
unset_raw_breakpoint_origin(bp, type, tid);
}
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* tid = identifiant du thread concerné. *
* pc = adresse de l'instruction courante. *
* *
* Description : Met à jour les points d'arrêt suite à un arrêt. *
* *
* Retour : true si l'exécution a été relancée automatiquement. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_binary_debugger_update_breakpoints_on_stop(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t pc)
{
bool result; /* Indication à faire remonter */
size_t index; /* Indice de ce point d'arrêt */
bool found; /* Présence d'un point d'arrêt */
raw_breakpoint *bp; /* Confort de l'accès rapide */
virt_t previous; /* Adresse d'origine */
bool status; /* Bilan d'un retrait */
size_t i; /* Boucle de parcours */
virt_t addr; /* Localisation d'un point */
result = false;
g_rw_lock_writer_lock(&debugger->bp_lock);
found = bsearch_index(&pc, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *),
(__compar_fn_t)compare_raw_breakpoint_with_addr, &index);
if (found)
{
bp = debugger->bpoints[index];
/* S'il s'agissait d'un point d'arrêt à usage interne */
if (has_raw_breakpoint_origin(bp, RBO_INTERNAL, tid))
{
previous = get_raw_breakpoint_prev_addr(bp);
assert(previous != VMPA_NO_VIRTUAL);
g_binary_debugger_remove_same_breakpoints(debugger, tid, previous, RBO_INTERNAL);
assert(!has_raw_breakpoint_origin(bp, RBO_INTERNAL, tid));
result = true;
}
/* S'il s'agissait d'un arrêt demandé par l'utilisateur */
if (has_raw_breakpoint_origin(bp, RBO_USER, tid))
{
status = G_BINARY_DEBUGGER_GET_CLASS(debugger)->disable_bp(debugger, bp);
if (!status)
log_variadic_message(LMT_ERROR, "Error while removing the breakpoint at 0x%" PRIx64, pc);
g_binary_debugger_spread_breakpoints(debugger, tid, pc, RBO_INTERNAL, false);
result = false;
}
/* S'il s'agissait d'une progression pas à pas */
if (has_raw_breakpoint_origin(bp, RBO_STEP, tid))
{
previous = get_raw_breakpoint_prev_addr(bp);
assert(previous != VMPA_NO_VIRTUAL);
g_binary_debugger_remove_same_breakpoints(debugger, tid, previous, RBO_STEP);
assert(!has_raw_breakpoint_origin(bp, RBO_STEP, tid));
result = false;
}
/* En conclusion, on supprime les points inutiles */
for (i = 0; i < debugger->bp_count; )
{
bp = debugger->bpoints[i];
if (is_breakpoint_useless(bp))
{
status = g_binary_debugger_remove_memory_breakpoint(debugger, i);
if (!status)
{
addr = get_raw_breakpoint_addr(bp);
log_variadic_message(LMT_ERROR, "Error while removing the breakpoint at 0x%" PRIx64, addr);
i++;
}
}
else i++;
}
}
g_rw_lock_writer_unlock(&debugger->bp_lock);
if (result)
G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger);
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* addr = emplacement du point mémoire à traiter. *
* *
* Description : Ajoute un point d'arrêt basique en mémoire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_add_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr)
{
bool result; /* Bilan à retourner */
dbg_thread_id_t tid; /* Identifiant de thread */
tid = 1;/// FIXME
g_rw_lock_writer_lock(&debugger->bp_lock);
result = g_binary_debugger_insert_memory_breakpoint(debugger, addr, RBO_USER, tid, VMPA_NO_VIRTUAL);
g_rw_lock_writer_unlock(&debugger->bp_lock);
if (result)
g_signal_emit_by_name(debugger, "mem-bp-handled", true, addr);
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à manipuler ici. *
* addr = emplacement du point mémoire à traiter. *
* *
* Description : Retire un point d'arrêt basique en mémoire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_delete_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr)
{
bool result; /* Bilan à retourner */
size_t index; /* Indice de ce point d'arrêt */
raw_breakpoint *bp; /* Confort de l'accès rapide */
dbg_thread_id_t tid; /* Identifiant de thread */
g_rw_lock_writer_lock(&debugger->bp_lock);
result = bsearch_index(&addr, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *),
(__compar_fn_t)compare_raw_breakpoint_with_addr, &index);
if (result)
{
bp = debugger->bpoints[index];
tid = 1;/// FIXME
result = has_raw_breakpoint_origin(bp, RBO_USER, tid);
if (result)
{
unset_raw_breakpoint_origin(bp, RBO_USER, tid);
if (is_breakpoint_useless(bp))
result = g_binary_debugger_remove_memory_breakpoint(debugger, index);
}
}
g_rw_lock_writer_unlock(&debugger->bp_lock);
if (result)
g_signal_emit_by_name(debugger, "mem-bp-handled", false, addr);
return result;
}
/* ---------------------------------------------------------------------------------- */
/* CONTROLE DU FLOT D'EXECUTION */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : debugger = débogueur à redémarrer. *
* *
* Description : Redémarre le processus de débogage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_restart(GBinaryDebugger *debugger)
{
return G_BINARY_DEBUGGER_GET_CLASS(debugger)->restart(debugger);
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* *
* Description : Remet en marche le débogueur courant. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_resume(GBinaryDebugger *debugger)
{
bool result; /* Bilan à retourner */
result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger);
return result;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* over = indique si les appels doivent être sautés ou non. *
* *
* Description : Relance l'exécution pour une seule instruction. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_debugger_stepi(GBinaryDebugger *debugger, bool over)
{
bool result; /* Bilan à retourner */
virt_t pc; /* Position courante */
dbg_thread_id_t tid; /* Identifiant de thread */
result = g_binary_debugger_get_current_pc(debugger, &pc);
if (result)
{
tid = 1;/// FIXME
g_rw_lock_writer_lock(&debugger->bp_lock);
result = g_binary_debugger_spread_breakpoints(debugger, tid, pc, RBO_STEP, over);
g_rw_lock_writer_unlock(&debugger->bp_lock);
}
if (result)
result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger);
return result;
}