diff options
Diffstat (limited to 'src/debug/gdbrsp')
| -rw-r--r-- | src/debug/gdbrsp/Makefile.am | 24 | ||||
| -rw-r--r-- | src/debug/gdbrsp/aops.h | 57 | ||||
| -rw-r--r-- | src/debug/gdbrsp/gdb-int.h | 116 | ||||
| -rw-r--r-- | src/debug/gdbrsp/gdb.c | 1524 | ||||
| -rw-r--r-- | src/debug/gdbrsp/gdb.h | 61 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers.c | 224 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers.h | 62 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm.c | 252 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm.h | 37 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm64.c | 97 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm64.h | 40 | ||||
| -rw-r--r-- | src/debug/gdbrsp/packet.c | 389 | ||||
| -rw-r--r-- | src/debug/gdbrsp/packet.h | 82 | ||||
| -rw-r--r-- | src/debug/gdbrsp/stream-int.h | 89 | ||||
| -rw-r--r-- | src/debug/gdbrsp/stream.c | 696 | ||||
| -rw-r--r-- | src/debug/gdbrsp/stream.h | 68 | ||||
| -rw-r--r-- | src/debug/gdbrsp/support.c | 598 | ||||
| -rw-r--r-- | src/debug/gdbrsp/support.h | 73 | ||||
| -rw-r--r-- | src/debug/gdbrsp/target.c | 950 | ||||
| -rw-r--r-- | src/debug/gdbrsp/target.h | 72 | ||||
| -rw-r--r-- | src/debug/gdbrsp/tcp.c | 280 | ||||
| -rw-r--r-- | src/debug/gdbrsp/tcp.h | 57 | ||||
| -rw-r--r-- | src/debug/gdbrsp/utils.c | 351 | ||||
| -rw-r--r-- | src/debug/gdbrsp/utils.h | 58 | 
24 files changed, 6257 insertions, 0 deletions
| diff --git a/src/debug/gdbrsp/Makefile.am b/src/debug/gdbrsp/Makefile.am new file mode 100644 index 0000000..fd46f27 --- /dev/null +++ b/src/debug/gdbrsp/Makefile.am @@ -0,0 +1,24 @@ + +noinst_LTLIBRARIES = libdebuggdbrsp.la + +libdebuggdbrsp_la_SOURCES =				\ +	aops.h								\ +	gdb-int.h							\ +	gdb.h gdb.c							\ +	helpers.h helpers.c					\ +	helpers_arm.h helpers_arm.c			\ +	helpers_arm64.h helpers_arm64.c		\ +	packet.h packet.c					\ +	stream-int.h						\ +	stream.h stream.c					\ +	support.h support.c					\ +	target.h target.c					\ +	tcp.h tcp.c							\ +	utils.h utils.c + +libdebuggdbrsp_la_CFLAGS = $(AM_CFLAGS) + + +AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) diff --git a/src/debug/gdbrsp/aops.h b/src/debug/gdbrsp/aops.h new file mode 100644 index 0000000..02d4982 --- /dev/null +++ b/src/debug/gdbrsp/aops.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * aops.h - prototypes pour les compléments utiles à GDB pour l'architecture ARM + * + * Copyright (C) 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_AOPS_H +#define _DEBUG_GDBRSP_AOPS_H + + +#include "gdb.h" + + + +/* Détermine le point d'exécution courant. */ +typedef bool (* get_pc_fc) (GGdbDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +typedef bool (* compute_call_stack_fc) (const GGdbDebugger *, virt_t **, size_t *); + +/* Complète la commande manipulant des points d'arrêt. */ +typedef const char * (* get_bp_kind_fc) (const GGdbDebugger *, virt_t); + +/* Construit une instruction provoquant un arrêt d'exécution. */ +typedef const uint8_t * (* get_bp_data_fc) (const GGdbDebugger *, virt_t, size_t *); + + +/* Procédures spécifiques pour une architecture */ +typedef struct _gdb_arch_ops +{ +    get_pc_fc get_pc;                       /* Obtention du point d'exéc.  */ +    compute_call_stack_fc compute_cstack;   /* Calcule la pile d'appels    */ +    get_bp_kind_fc get_bp_kind;             /* Fournit le type d'un point  */ +    get_bp_data_fc get_bp_data;             /* Code d'un point d'arrêt     */ + +} gdb_arch_ops; + + + +#endif  /* _DEBUG_GDBRSP_AOPS_H */ diff --git a/src/debug/gdbrsp/gdb-int.h b/src/debug/gdbrsp/gdb-int.h new file mode 100644 index 0000000..5055fbf --- /dev/null +++ b/src/debug/gdbrsp/gdb-int.h @@ -0,0 +1,116 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.h - prototypes pour le débogage à l'aide de gdb. + * + * Copyright (C) 2009-2012 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_GDB_INT_H +#define _DEBUG_GDBRSP_GDB_INT_H + + +#include "aops.h" +#include "gdb.h" +#include "stream.h" +#include "support.h" +#include "target.h" +#include "../break-int.h" +#include "../debugger-int.h" + + + + + + + + + + +/* Définition d'un point d'arrêt appliqué */ +typedef struct _gdb_breakpoint +{ +    raw_breakpoint raw;                     /* A laisser en premier        */ + +    bool is_z;                              /* Usage de commande dédiée ?  */ + +    union +    { +        const char *kind;                   /* Précision de taille         */ + +        struct +        { +            uint8_t memory[16];             /* Données d'origine remplacées*/ +            size_t len;                     /* Quantité de ces données     */ +        }; + +    }; + +} gdb_breakpoint; + + + + + + + + + + + + + + +/* Débogueur utilisant un serveur GDB (instance) */ +struct _GGdbDebugger +{ +    GBinaryDebugger parent;                 /* A laisser en premier        */ + +    SourceEndian endian;                    /* Boutisme du format          */ +    MemoryDataSize msize;                   /* Taille des adresses         */ + +    const gdb_arch_ops *ops;                /* Opérations spécifiques      */ + +    GGdbStream *stream;                     /* Flux de communication       */ +    GGdbSupport *support;                   /* Configuration à adopter     */ +    GGdbTarget *target;                     /* Architecture ciblée par GDB */ + +}; + + +/* Débogueur utilisant un serveur GDB (classe) */ +struct _GGdbDebuggerClass +{ +    GBinaryDebuggerClass parent;            /* A laisser en premier        */ + +}; + + + +/* ------------------------ ACCUEIL D'EVENEMENTS ASYNCHRONES ------------------------ */ + + +/* Réagit à la réception d'un signal par le programme étudié. */ +void g_gdb_debugger_receive_signal_reply(GGdbDebugger *, int); + +/* Réagit à la sortie d'exécution d'un programme étudié. */ +void g_gdb_debugger_receive_exit_reply(GGdbDebugger *, int, pid_t); + + + +#endif  /* _DEBUG_GDBRSP_GDB_INT_H */ diff --git a/src/debug/gdbrsp/gdb.c b/src/debug/gdbrsp/gdb.c new file mode 100644 index 0000000..37a7a73 --- /dev/null +++ b/src/debug/gdbrsp/gdb.c @@ -0,0 +1,1524 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.c - débogage à l'aide de gdb. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#include "gdb.h" + + + +#include <assert.h> +#include <malloc.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + + +#include "gdb-int.h" +#include "helpers.h" +#include "helpers_arm.h" +#include "helpers_arm64.h" +#include "tcp.h" +#include "utils.h" +#include "../../common/cpp.h" +#include "../../format/format.h" + + + + + + + + +/* Initialise la classe du débogueur utilisant gdb. */ +static void g_gdb_debugger_class_init(GGdbDebuggerClass *); + +/* Procède à l'initialisation du débogueur utilisant gdb. */ +static void g_gdb_debugger_init(GGdbDebugger *); + +/* Supprime toutes les références externes. */ +static void g_gdb_debugger_dispose(GGdbDebugger *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_debugger_finalize(GGdbDebugger *); + + +/* Met en marche le débogueur utilisant un serveur GDB. */ +static bool g_gdb_debugger_run(GGdbDebugger *); + +/* Remet en marche le débogueur utilisant un serveur GDB. */ +//static bool g_gdb_debugger_resume(GGdbDebugger *); + +/* Tue le débogueur utilisant un serveur GDB. */ +static bool g_gdb_debugger_kill(GGdbDebugger *); + + + + + + +/* --------------------------- ENTREES / SORTIES BASIQUES --------------------------- */ + + +/* Lit une valeur quelconque à une adresse arbitraire.  */ +static bool g_gdb_debugger_read_memory(GGdbDebugger *, virt_t, size_t, ...); + +/* Ecrit une valeur quelconque à une adresse arbitraire.  */ +static bool g_gdb_debugger_write_memory(GGdbDebugger *, virt_t, size_t, ...); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +static char **g_gdb_debugger_get_register_names(const GGdbDebugger *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +static unsigned int g_gdb_debugger_get_register_size(const GGdbDebugger *, const char *); + +/* Effectue la lecture d'un registre donné. */ +static bool g_gdb_debugger_read_register(GGdbDebugger *, const char *, size_t, ...); + +/* Effectue l'écriture d'un registre donné. */ +static bool g_gdb_debugger_write_register(GGdbDebugger *, const char *, size_t, ...); + + + +/* ------------------------- MANIPULATION DE L'ETAT COURANT ------------------------- */ + + +/* Détermine le point d'exécution courant. */ +static bool g_gdb_debugger_get_current_pc(GGdbDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +static bool g_gdb_debugger_compute_call_stack(GGdbDebugger *, virt_t **, size_t *); + + + +/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ + + +/* Ajoute un point d'arrêt basique en mémoire. */ +static gdb_breakpoint *g_gdb_debugger_enable_memory_breakpoint(GGdbDebugger *, virt_t); + +/* Retire un point d'arrêt basique en mémoire. */ +static bool g_gdb_debugger_disable_memory_breakpoint(GGdbDebugger *, gdb_breakpoint *); + + + +/* -------------------------- CONTROLE DU FLOT D'EXECUTION -------------------------- */ + + +/* Redémarre le processus de débogage lié à un serveur GDB. */ +static bool g_gdb_debugger_restart(GGdbDebugger *); + +/* Remet en marche le débogueur utilisant un serveur GDB. */ +static bool g_gdb_debugger_resume(GGdbDebugger *); + + + + + + + + + + + + +/* Détermine l'identifiant du thread principal courant. */ +static char *g_gdb_debugger_get_active_thread(GGdbDebugger *); + + + + + + +/* ------------------------ ACCUEIL D'EVENEMENTS ASYNCHRONES ------------------------ */ + + + + + + + + +/* Indique le type défini par la GLib pour le débogueur gdb. */ +G_DEFINE_TYPE(GGdbDebugger, g_gdb_debugger, G_TYPE_BINARY_DEBUGGER); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe de débogueur à initialiser.                   * +*                                                                             * +*  Description : Initialise la classe du débogueur utilisant gdb.             * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_class_init(GGdbDebuggerClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ +    GBinaryDebuggerClass *parent;           /* Version en classe parente   */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_debugger_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_debugger_finalize; + +    parent = G_BINARY_DEBUGGER_CLASS(klass); + +    parent->read_mem = (read_mem_any_fc)g_gdb_debugger_read_memory; +    parent->write_mem = (write_mem_any_fc)g_gdb_debugger_write_memory; +    parent->get_reg_names = (get_reg_names_fc)g_gdb_debugger_get_register_names; +    parent->get_reg_size = (get_reg_size_fc)g_gdb_debugger_get_register_size; +    parent->read_reg = (read_write_reg_any_fc)g_gdb_debugger_read_register; +    parent->write_reg = (read_write_reg_any_fc)g_gdb_debugger_write_register; + +    parent->get_current_pc = (get_current_pc_fc)g_gdb_debugger_get_current_pc; +    parent->get_call_stack = (get_call_stack_fc)g_gdb_debugger_compute_call_stack; + +    parent->enable_bp = (enable_mem_bp_fc)g_gdb_debugger_enable_memory_breakpoint; +    parent->disable_bp = (disable_mem_bp_fc)g_gdb_debugger_disable_memory_breakpoint; + +    parent->restart = (restart_debugger_fc)g_gdb_debugger_restart; +    parent->resume = (resume_debugger_fc)g_gdb_debugger_resume; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance de débogueur à préparer.                 * +*                                                                             * +*  Description : Procède à l'initialisation du débogueur utilisant gdb.       * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_init(GGdbDebugger *debugger) +{ +    GBinaryDebugger *parent;                /* Instance parente            */ + +    parent = G_BINARY_DEBUGGER(debugger); + +    parent->run = (basic_debugger_fc)g_gdb_debugger_run; +    //parent->resume = (resume_debugger_fc)g_gdb_debugger_resume; +    parent->kill = (basic_debugger_fc)g_gdb_debugger_kill; + +    //parent->get_reg_values = (get_register_values_fc)get_register_values_using_gdb_debugger; + +    //debugger->cond = g_cond_new(); +    //debugger->mutex = g_mutex_new(); + + +    // FIXME +    //debugger->compute_cstack = compute_call_stack_for_arm64; +    //debugger->fill_mem_bp = fill_memory_breakpoint_cmd_for_arm64; + + + + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance d'objet GLib à traiter.                  * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_dispose(GGdbDebugger *debugger) +{ +    if (debugger->stream != NULL) +        g_object_unref(G_OBJECT(debugger->stream)); + +    if (debugger->support != NULL) +        g_object_unref(G_OBJECT(debugger->support)); + +    if (debugger->target != NULL) +        g_object_unref(G_OBJECT(debugger->target)); + +    G_OBJECT_CLASS(g_gdb_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_gdb_debugger_finalize(GGdbDebugger *debugger) +{ +    G_OBJECT_CLASS(g_gdb_debugger_parent_class)->finalize(G_OBJECT(debugger)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : binary = binaire représenter à déboguer.                     * +*                server = nom ou adresse du serveur à contacter.              * +*                port   = port de connexion.                                  * +*                                                                             * +*  Description : Crée un débogueur utilisant un serveur GDB distant.          * +*                                                                             * +*  Retour      : Instance de débogueur mise en place ou NULL.                 * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GBinaryDebugger *g_gdb_debugger_new(GLoadedBinary *binary, const char *server, unsigned short port) +{ +    GGdbDebugger *result;                   /* Débogueur à retourner       */ +    GExeFormat *format;                     /* Format du binaire chargé    */ +    const char *arch;                       /* Architecture d'exécution    */ +    GArchProcessor *proc;                   /* Processeur lié au binaire   */ +    char service[sizeof(XSTR(UINT16_MAX)) + 1]; /* Conversion requise      */ + +    result = g_object_new(G_TYPE_GDB_DEBUGGER, NULL); + +    G_BINARY_DEBUGGER(result)->binary = binary; +    g_object_ref(G_OBJECT(binary)); + +    /* Propriétés de la cible */ + +    format = g_loaded_binary_get_format(binary); + +    result->endian = g_binary_format_get_endianness(G_BIN_FORMAT(format)); + +    arch = g_exe_format_get_target_machine(format); + +    if (strcmp(arch, "armv7") == 0) +        result->ops = get_arm_operations(); +    else +        result->ops = NULL; + +    g_object_unref(G_OBJECT(format)); + +    if (result->ops == NULL) +        goto ggdn_error; + +    proc = g_loaded_binary_get_processor(binary); + +    result->msize = g_arch_processor_get_memory_size(proc); + +    g_object_unref(G_OBJECT(proc)); + +    /* Mise en place des modules auxialiaires */ + +    snprintf(service, sizeof(service), "%hu", port); + +    result->stream = g_gdb_tcp_client_new(server, service, result); +    if (result->stream == NULL) goto ggdn_error; + +    result->support = g_gdb_support_new(result->stream); + +    result->target = g_gdb_target_new(result->stream); +    if (result->target == NULL) goto ggdn_error; + +    return G_BINARY_DEBUGGER(result); + + ggdn_error: + +    g_object_unref(G_OBJECT(result)); + +    return NULL; + +} + + + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à lancer.                               * +*                                                                             * +*  Description : Met en marche le débogueur utilisant un serveur GDB.         * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_run(GGdbDebugger *debugger) +{ + + + +    GGdbPacket *packet; + +    bool test; + +    const char *data; +    size_t len; + + +    int sig; +    vmpa_t addr; +    pid_t thread; + + +    debugger->stream = g_gdb_tcp_client_new("127.0.0.1", "6666", NULL); +    if (debugger->stream == NULL) return false; + + +    printf("Connection done !\n"); + + + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "?"); + + +    test = g_gdb_stream_send_packet(debugger->stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", "?", test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << Réception de '%s'\n", data); + + + + + +    get_stop_reply_sig_info(packet, &sig, &addr, &thread, SRE_LITTLE); + +    g_signal_emit_by_name(debugger, "halted", sig, addr, thread); + + + +    return true; + +} + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à relancer.                             * +*                                                                             * +*  Description : Tue le débogueur utilisant un serveur GDB.                   * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_kill(GGdbDebugger *debugger) +{ + + +#if 0 +    int ret;                                /* Bilan de l'appel système    */ + +    ret = kill(debugger->child, SIGKILL); +    if (ret != 0) perror("kill"); + +    debugger->child = 0; + +    g_mutex_lock(debugger->mutex); +    debugger->run_again = TRUE; +    g_cond_signal(debugger->cond); +    g_mutex_unlock(debugger->mutex); +#endif +    return true; + +} + + + + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                                                                             * +*  Description : Détermine l'identifiant du thread principal courant.         * +*                                                                             * +*  Retour      : Identifiant du thread actif principal ou NULL en cas d'échec.* +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static char *g_gdb_debugger_get_active_thread(GGdbDebugger *debugger) +{ +    char *result;                           /* Identifiant à renvoyer      */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    bool status;                            /* Bilan d'une communication   */ +    const char *data;                       /* Données reçues à analyser   */ +    const char *start;                      /* Début d'identification      */ +    const char *end;                        /* Fin d'identification        */ + +    result = NULL; + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "?"); + +    status = g_gdb_stream_send_packet(debugger->stream, packet); + +    if (!status) +        goto ggdgat_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    start = strstr(data, "thread:"); +    if (start == NULL) goto ggdgat_exit; + +    start += sizeof("thread:") - 1 /* '\0' */; + +    end = strstr(start, ";"); +    if (end == NULL) goto ggdgat_exit; + +    result = strndup(start, end - start); + + ggdgat_exit: + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    return result; + +} + + + + + + + + + + + + + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/*                             ENTREES / SORTIES BASIQUES                             */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                addr     = emplacement en mémoire à venir consulter.         * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur lue à conserver. [OUT]   * +*                                                                             * +*  Description : Lit une valeur quelconque à une adresse arbitraire.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_read_memory(GGdbDebugger *debugger, virt_t addr, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    char cmd[1 + VMPA_MAX_LEN + 3];         /* Commande à émettre          */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ +    va_list ap;                             /* Liste variable d'arguments  */ +    uint8_t *val8;                          /* Valeur sur 8 bits           */ +    uint16_t *val16;                        /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    uint32_t *val32;                        /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    uint64_t *val64;                        /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ + +    /* Envoi de la requête */ + +    cmd[0] = 'm'; + +    result = translate_virt_to_hex(debugger, addr, &cmd[1]); + +    switch (size) +    { +        case 8: +            strcat(cmd, ",1"); +            break; + +        case 16: +            strcat(cmd, ",2"); +            break; + +        case 32: +            strcat(cmd, ",4"); +            break; + +        case 64: +            strcat(cmd, ",8"); +            break; + +        default: +            assert(false); +            result = false; +            goto ggdrm_exit; +            break; + +    } + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, cmd); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdrm_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (is_error_code(data, len)) +    { +        result = false; +        goto ggdrm_error; +    } + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = hex_to_u8(data, val8); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            result = hex_to_u16(data, &conv16); +            *val16 = from_u16(&conv16, debugger->endian); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            result = hex_to_u32(data, &conv32); +            *val32 = from_u32(&conv32, debugger->endian); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            result = hex_to_u64(data, &conv64); +            *val64 = from_u64(&conv64, debugger->endian); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + + ggdrm_error: + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + + ggdrm_exit: + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler.                            * +*                addr     = emplacement en mémoire à venir consulter.         * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur lue à conserver. [OUT]   * +*                                                                             * +*  Description : Lit une valeur quelconque à une adresse arbitraire.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_write_memory(GGdbDebugger *debugger, virt_t addr, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    char cmd[1 + 3 * VMPA_MAX_LEN + 3];     /* Commande à émettre          */ +    va_list ap;                             /* Liste variable d'arguments  */ +    const uint8_t *val8;                    /* Valeur sur 8 bits           */ +    const uint16_t *val16;                  /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    const uint32_t *val32;                  /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    const uint64_t *val64;                  /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ +    char hexval[17];                        /* Valeur sous forme hexa      */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ + +    /* Envoi de la requête */ + +    cmd[0] = 'M'; + +    result = translate_virt_to_hex(debugger, addr, &cmd[1]); + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = u8_to_hex(val8, hexval); + +            strcat(cmd, ",1:"); +            strcat(cmd, hexval); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            conv16 = to_u16(val16, debugger->endian); +            result = u16_to_hex(&conv16, hexval); + +            strcat(cmd, ",2:"); +            strcat(cmd, hexval); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            conv32 = to_u32(val32, debugger->endian); +            result = u32_to_hex(&conv32, hexval); + +            strcat(cmd, ",4:"); +            strcat(cmd, hexval); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            conv64 = to_u64(val64, debugger->endian); +            result = u16_to_hex(&conv64, hexval); + +            strcat(cmd, ",8:"); +            strcat(cmd, hexval); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    if (!result) +        goto ggdwm_exit; + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, cmd); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdwm_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (len == 3 && data[0] == 'E') +    { +        result = false; +        goto ggdrm_error; +    } + + ggdrm_error: + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + + ggdwm_exit: + +    va_end(ap); + +    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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static char **g_gdb_debugger_get_register_names(const GGdbDebugger *debugger, const char *group, size_t *count) +{ +    return g_gdb_target_get_register_names(debugger->target, 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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static unsigned int g_gdb_debugger_get_register_size(const GGdbDebugger *debugger, const char *name) +{ +    return g_gdb_target_get_register_size(debugger->target, name); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                reg      = désignation humaine du register à consulter.      * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur lue à conserver. [OUT]   * +*                                                                             * +*  Description : Effectue la lecture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_read_register(GGdbDebugger *debugger, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    va_list ap;                             /* Liste variable d'arguments  */ +    uint8_t *val8;                          /* Valeur sur 8 bits           */ +    uint16_t *val16;                        /* Valeur sur 16 bits          */ +    uint32_t *val32;                        /* Valeur sur 32 bits          */ +    uint64_t *val64;                        /* Valeur sur 64 bits          */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 8, val8); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 16, val16); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 32, val32); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 64, val64); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler.                            * +*                reg      = désignation humaine du register à consulter.      * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur à écrire.                * +*                                                                             * +*  Description : Effectue l'écriture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_write_register(GGdbDebugger *debugger, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    va_list ap;                             /* Liste variable d'arguments  */ +    const uint8_t *val8;                    /* Valeur sur 8 bits           */ +    const uint16_t *val16;                  /* Valeur sur 16 bits          */ +    const uint32_t *val32;                  /* Valeur sur 32 bits          */ +    const uint64_t *val64;                  /* Valeur sur 64 bits          */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, const uint8_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 8, val8); +            break; + +        case 16: +            val16 = va_arg(ap, const uint16_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 16, val16); +            break; + +        case 32: +            val32 = va_arg(ap, const uint32_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 32, val32); +            break; + +        case 64: +            val64 = va_arg(ap, const uint64_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 64, val64); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + +    return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/*                           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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_get_current_pc(GGdbDebugger *debugger, virt_t *pc) +{ +    bool result;                            /* Bilan à retourner           */ + +    result = debugger->ops->get_pc(debugger, pc); + +    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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_compute_call_stack(GGdbDebugger *debugger, virt_t **callstack, size_t *size) +{ +    bool result;                            /* Bilan global à retourner    */ + +    if (debugger->ops->compute_cstack != NULL) +        result = debugger->ops->compute_cstack(debugger, callstack, size); + +    else +        result = false; + +    return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/*                             GESTION DES POINTS D'ARRET                             */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  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      : Structure de suivi mise en place pour l'occasion, voire NULL.* +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static gdb_breakpoint *g_gdb_debugger_enable_memory_breakpoint(GGdbDebugger *debugger, virt_t addr) +{ +    gdb_breakpoint *result;                 /* Nouveau suivi à retourner   */ +    char cmd[3 + VMPA_MAX_LEN + 3];         /* Commande à émettre          */ +    bool status;                            /* Bilan d'une opération       */ +    const char *kind;                       /* Taille spécifique du point  */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ +    GBinaryDebugger *dbg;                   /* Autre version du débogueur  */ +    const uint8_t *bp;                      /* Données du point d'arrêt    */ +    size_t bp_len;                          /* Quantité de ces données     */ +    uint8_t memory[16];                     /* Sauvegarde de la mémoire    */ + +    result = NULL; + +    /* Si l'utilisation de la commande dédiée est possible */ +    if (1)  //////// TODO +    { +        /* Envoi de la requête */ + +        strcpy(cmd, "Z0,"); + +        status = translate_virt_to_hex(debugger, addr, &cmd[3]); + +        if (!status) +            goto ggdemb_exit; + +        kind = debugger->ops->get_bp_kind(debugger, addr); + +        if (kind == NULL) +            goto ggdemb_exit; + +        strcat(cmd, kind); + +        packet = g_gdb_stream_get_free_packet(debugger->stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, cmd); + +        status = g_gdb_stream_send_packet(debugger->stream, packet); + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        if (!status) +            goto ggdemb_exit; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(debugger->stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        if (is_error_code(data, len)) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggdemb_fallback; +        } + +        if (strcmp(data, "OK") != 0) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggdemb_fallback; +        } + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        /* Constitution d'un dossier de suivi */ + +        result = (gdb_breakpoint *)malloc(sizeof(gdb_breakpoint)); + +        result->is_z = true; + +        result->kind = kind; + +    } + +    else +    { + + ggdemb_fallback: + +        dbg = G_BINARY_DEBUGGER(debugger); + +        /* Détermination du point d'arrêt */ + +        bp = debugger->ops->get_bp_data(debugger, addr, &bp_len); + +        assert(bp_len <= 16); + +        /* Sauvegarde de la mémoire courante */ + +        status = g_binary_debugger_read_memory_data(dbg, addr, memory, bp_len); + +        if (!status) goto ggdemb_exit; + +        /* Application du point d'arrêt */ + +        status = g_binary_debugger_write_memory_data(dbg, addr, bp, bp_len); + +        if (!status) goto ggdemb_exit; + +        /* Constitution d'un dossier de suivi */ + +        result = (gdb_breakpoint *)malloc(sizeof(gdb_breakpoint)); + +        result->is_z = false; + +        memcpy(result->memory, memory, bp_len); +        result->len = bp_len; + +    } + +    init_raw_breakpoint((raw_breakpoint *)result, addr); + + ggdemb_exit: + +    return result; +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                bp       = point d'arrêt à traiter.                          * +*                                                                             * +*  Description : Retire un point d'arrêt basique de la mémoire ciblée.        * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_disable_memory_breakpoint(GGdbDebugger *debugger, gdb_breakpoint *bp) +{ +    bool result;                            /* Bilan à retourner           */ +    char cmd[3 + VMPA_MAX_LEN + 3];         /* Commande à émettre          */ +    bool status;                            /* Bilan d'une opération       */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ +    GBinaryDebugger *dbg;                   /* Autre version du débogueur  */ + +    result = false; + +    /* Si l'utilisation de la commande dédiée est requise */ +    if (bp->is_z) +    { +        /* Envoi de la requête */ + +        strcpy(cmd, "z0,"); + +        status = translate_virt_to_hex(debugger, bp->raw.addr, &cmd[3]); + +        if (!status) +            goto ggddmb_exit; + +        strcat(cmd, bp->kind); + +        packet = g_gdb_stream_get_free_packet(debugger->stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, cmd); + +        status = g_gdb_stream_send_packet(debugger->stream, packet); + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        if (!status) +            goto ggddmb_exit; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(debugger->stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        if (is_error_code(data, len)) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggddmb_exit; +        } + +        if (strcmp(data, "OK") != 0) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggddmb_exit; +        } + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        result = true; + +    } + +    else +    { +        dbg = G_BINARY_DEBUGGER(debugger); + +        result = g_binary_debugger_write_memory_data(dbg, bp->raw.addr, bp->memory, bp->len); + +    } + + ggddmb_exit: + +    return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/*                            CONTROLE DU FLOT D'EXECUTION                            */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à redémarrer.                           * +*                                                                             * +*  Description : Redémarre le processus de débogage lié à un serveur GDB.     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_restart(GGdbDebugger *debugger) +{ +    bool result;                            /* Bilan à retourner           */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    return true; +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "R00"); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à relancer.                             * +*                                                                             * +*  Description : Remet en marche le débogueur utilisant un serveur GDB.       * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_resume(GGdbDebugger *debugger) +{ +    bool result;                            /* Bilan à retourner           */ +    //char *id;                               /* Identifiant de thread       */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    //const char *data;                       /* Données reçues à analyser   */ + +    static bool _twice = false; + + +    if (!_twice && 0) +    { + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "$QPassSignals:e;10;14;17;1a;1b;1c;21;24;25;2c;4c;"); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdhmb_exit; + +    } + + + + + + +    /* Envoi de la requête */ + +    /* +    id = g_gdb_debugger_get_active_thread(debugger); +    if (id == NULL) return false; + +    printf("ID : %s\n", id); +    */ + +    /* +    id = g_gdb_support_get_id(debugger->support); +    if (id == NULL) return false; + +    printf("ID : %s\n", id); +    */ + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "vCont;c:-1"); +    //g_gdb_packet_append(packet, "vCont;c:p256f.-1"); + + +    /* +    if (_twice) +    { +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "vCont;c:p"); +    g_gdb_packet_append(packet, id); +    g_gdb_packet_append(packet, "."); +    g_gdb_packet_append(packet, id); +    } +    else +    { +        _twice = true; +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "vCont;c:p"); +    g_gdb_packet_append(packet, id); +    g_gdb_packet_append(packet, ".-1"); +    } +    */ + + + + + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdhmb_exit; + +    /* Réception de la réponse */ +    /* +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    printf("Ack cont...\n"); + +    //result = (strcmp(data, "OK") == 0); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +    */ + ggdhmb_exit: + +    _twice = true; + +    return result; + +} + + + + + + + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/*                          ACCUEIL D'EVENEMENTS ASYNCHRONES                          */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance liée à un débogueur GDB à manipuler.     * +*                signum   = indentifiant du signal concerné.                  * +*                                                                             * +*  Description : Réagit à la réception d'un signal par le programme étudié.   * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_debugger_receive_signal_reply(GGdbDebugger *debugger, int signum) +{ +    virt_t pc;                              /* Position courante du CPU    */ +    bool status;                            /* Bilan d'une opération       */ +    GBinaryDebugger *base;                  /* Version basique du débogueur*/ + +    base = G_BINARY_DEBUGGER(debugger); + +    status = g_binary_debugger_get_current_pc(base, &pc); + +    if (!status) +        pc = VMPA_NO_VIRTUAL; + +    on_binary_debugger_stopped(base, pc); + +    g_signal_emit_by_name(debugger, "signaled", signum); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance liée à un débogueur GDB à manipuler.     * +*                status   = indication d'état à la sortie.                    * +*                pid      = éventuel identifiant de processus concerné ou -1. * +*                                                                             * +*  Description : Réagit à la sortie d'exécution d'un programme étudié.        * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_debugger_receive_exit_reply(GGdbDebugger *debugger, int status, pid_t pid) +{ +    GBinaryDebugger *base;                  /* Version basique du débogueur*/ + +    base = G_BINARY_DEBUGGER(debugger); + +    on_binary_debugger_finished(base, pid); + +    g_signal_emit_by_name(debugger, "exited", status, pid); + +} diff --git a/src/debug/gdbrsp/gdb.h b/src/debug/gdbrsp/gdb.h new file mode 100644 index 0000000..7faa044 --- /dev/null +++ b/src/debug/gdbrsp/gdb.h @@ -0,0 +1,61 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.h - prototypes pour le débogage à l'aide de gdb. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_GDB_H +#define _DEBUG_GDBRSP_GDB_H + + +#include <glib-object.h> + + +#include "../debugger.h" + + + +#define G_TYPE_GDB_DEBUGGER            (g_gdb_debugger_get_type()) +#define G_GDB_DEBUGGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GDB_DEBUGGER, GGdbDebugger)) +#define G_IS_GDB_DEBUGGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GDB_DEBUGGER)) +#define G_GDB_DEBUGGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_DEBUGGER, GGdbDebuggerClass)) +#define G_IS_GDB_DEBUGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_DEBUGGER)) +#define G_GDB_DEBUGGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_DEBUGGER, GGdbDebuggerClass)) + + +/* Débogueur utilisant un serveur GDB (instance) */ +typedef struct _GGdbDebugger GGdbDebugger; + +/* Débogueur utilisant un serveur GDB (classe) */ +typedef struct _GGdbDebuggerClass GGdbDebuggerClass; + + +/* Indique le type défini par la GLib pour le débogueur gdb. */ +GType g_gdb_debugger_get_type(void); + +/* Crée un débogueur utilisant un serveur GDB distant. */ +GBinaryDebugger *g_gdb_debugger_new(GLoadedBinary *, const char *, unsigned short); + + +void test_gdb(void); + + + +#endif  /* _DEBUG_GDBRSP_GDB_H */ diff --git a/src/debug/gdbrsp/helpers.c b/src/debug/gdbrsp/helpers.c new file mode 100644 index 0000000..296d642 --- /dev/null +++ b/src/debug/gdbrsp/helpers.c @@ -0,0 +1,224 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers.c - assistanat dans la manipulation des paquets GDB + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#include "helpers.h" + + +#include <regex.h> +#include <string.h> + + +#include "gdb-int.h" +#include "utils.h" + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                addr     = emplacement en mémoire à venir consulter.         * +*                out      = zone d'impression en hexadécimal. [OUT]           * +*                                                                             * +*  Description : Traduit une adresse en chaîne hexadécimale pour GDB.         * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool translate_virt_to_hex(const GGdbDebugger *debugger, virt_t addr, char *out) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    uint8_t conv8;                          /* Valeur adaptée sur 8 bits   */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ +    char hexval[17];                        /* Valeur sous forme hexa      */ +    bool got_msn;                           /* Obtention d'un quartet ?    */ +    size_t i;                               /* Boucle de parcours          */ + +    /* Conversion */ + +    switch (debugger->msize) +    { +        case MDS_8_BITS: +            conv8 = addr; +            result = u8_to_hex(&conv8, hexval); +            break; + +        case MDS_16_BITS: +            conv16 = addr; +            conv16 = to_u16(&conv16, SRE_BIG); +            result = u16_to_hex(&conv16, hexval); +            break; + +        case MDS_32_BITS: +            conv32 = addr; +            conv32 = to_u32(&conv32, SRE_BIG); +            result = u32_to_hex(&conv32, hexval); +            break; + +        case MDS_64_BITS: +            conv64 = addr; +            conv64 = to_u64(&conv64, SRE_BIG); +            result = u64_to_hex(&conv64, hexval); +            break; + +        default: +            result = false; +            break; + +    } + +    /* On saute les zéros préliminaires... */ + +    if (result) +    { +        got_msn = false; + +        for (i = 0; i < 17; i++) +        { +            if (!got_msn) +            { +                if (hexval[i] == '0') +                    continue; +                else +                    got_msn = true; +            } + +            *out = hexval[i]; +            out++; + +        } + +        *out = '\0'; + +    } + +    return result; + +} + + + + + + + + +/* -------------------------- PAQUETS DES REPONSES D'ARRET -------------------------- */ + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet = paquet de deonnées à interpréter.                   * +*                sig    = identifiant du signal source. [OUT]                 * +*                addr   = adresse de l'instruction courante. [OUT]            * +*                thread = identifiant du thread concerné. [OUT]               * +*                endian = boutisme de la plateforme ciblée.                   * +*                                                                             * +*  Description : Récupère les informations liées à un arrêt suite à signal.   * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : Les données sont la forme :                                  * +*                T0505:00000000;04:a08de6bf;08:505878b7;thread:50dc;          * +*                                                                             * +******************************************************************************/ + +bool get_stop_reply_sig_info(const GGdbPacket *packet, int *sig, vmpa_t *addr, pid_t *thread, SourceEndian endian) +{ +    const char *data;                       /* Données brutes du paquet    */ +    size_t length;                          /* Quantité de ces données     */ +    uint8_t index;                          /* Indice de 8 bits quelconque */ +    size_t pos;                             /* Tête de lecture courante    */ +    regex_t preg;                           /* Expression régulière        */ +    int ret;                                /* Bilan d'un appel            */ +    regmatch_t pmatch[3];                   /* Zones remarquées            */ +    size_t key_len;                         /* Taille de l'indicatif       */ + +    *addr = 0ull; + +    g_gdb_packet_get_data(packet, &data, &length, NULL); + +    pos = 1; + +    /* Lecture du numéro du signal */ + +    if (!strtou8(&index, data, &pos, length, SRE_LITTLE)) +        return false; + +    *sig = index; + +    /* Reste des informations */ + +    ret = regcomp(&preg, "([^:]+):([^;]+);", REG_EXTENDED | REG_ICASE); +    if (ret != 0) return false; + +    for (ret = regexec(&preg, &data[pos], 3, pmatch, 0); +         ret != REG_NOMATCH; +         ret = regexec(&preg, &data[pos], 3, pmatch, 0)) +    { +        key_len = pmatch[1].rm_eo - pmatch[1].rm_so; + +        /* Indication sur le thread */ +        if (key_len == strlen("thread") +            && strncmp(&data[pos + pmatch[1].rm_so], "thread", key_len) == 0) +        { + +            /* TODO printf("Thread found !\n"); */ + +        } + +        /* Valeur de registre ? */ +        else if (key_len == 2) +        { +            if (!strtou8(&index, data, (size_t []) { pos + pmatch[1].rm_so }, length, SRE_LITTLE)) +                return false; + +            if (index != 8 /* FIXME */) +                goto next_field; + +            if (!strtou32(addr, data, (size_t []) { pos + pmatch[2].rm_so }, length, SRE_LITTLE/* FIXME */)) +                return false; + +        } + +    next_field: +        pos += pmatch[0].rm_eo; + +    } + +    regfree(&preg); + +    return (*addr != 0ull); + +} + + + diff --git a/src/debug/gdbrsp/helpers.h b/src/debug/gdbrsp/helpers.h new file mode 100644 index 0000000..5f5f301 --- /dev/null +++ b/src/debug/gdbrsp/helpers.h @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers.h - prototypes pour un assistanat dans la manipulation des paquets GDB + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_HELPERS_H +#define _DEBUG_GDBRSP_HELPERS_H + + +#include "gdb.h" +#include "packet.h" + + + +/* Traduit une adresse en chaîne hexadécimale pour GDB. */ +bool translate_virt_to_hex(const GGdbDebugger *, virt_t, char *); + + + + + + +/* -------------------------- PAQUETS DES REPONSES D'ARRET -------------------------- */ + + +/* Récupère les informations liées à un arrêt suite à signal. */ +bool get_stop_reply_sig_info(const GGdbPacket *, int *, vmpa_t *, pid_t *, SourceEndian); + + + +/* ---------------------------------------------------------------------------------- */ +/*                            PAQUETS DES REPONSES D'ARRET                            */ +/* ---------------------------------------------------------------------------------- */ + + + + + + + + + + +#endif  /* _DEBUG_GDBRSP_HELPERS_H */ diff --git a/src/debug/gdbrsp/helpers_arm.c b/src/debug/gdbrsp/helpers_arm.c new file mode 100644 index 0000000..e2491ae --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm.c @@ -0,0 +1,252 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm.c - compléments utiles à GDB pour l'architecture ARM + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#include "helpers_arm.h" + + +#include <malloc.h> +#include <string.h> + + +#include "gdb-int.h" + + + +/* Détermine le point d'exécution courant. */ +static bool get_arm_pc(GGdbDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +static bool compute_call_stack_for_arm(const GGdbDebugger *, virt_t **, size_t *); + +/* Complète la commande manipulant des points d'arrêt. */ +static const char *get_breakpoint_kind_for_arm(const GGdbDebugger *, virt_t); + +/* Construit une instruction provoquant un arrêt d'exécution. */ +static const uint8_t *get_arm_breakpoint_data(const GGdbDebugger *, virt_t, size_t *); + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : -                                                            * +*                                                                             * +*  Description : Fournit les fonctions adaptées aux opérations pour ARM.      * +*                                                                             * +*  Retour      : Opérations spécifiques adaptées à ARM.                       * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +const gdb_arch_ops *get_arm_operations(void) +{ +    static const gdb_arch_ops arm_ops = { + +        .get_pc = get_arm_pc, +        .compute_cstack = compute_call_stack_for_arm, +        .get_bp_kind = get_breakpoint_kind_for_arm, +        .get_bp_data = get_arm_breakpoint_data + +    }; + +    return &arm_ops; + +} + + +/****************************************************************************** +*                                                                             * +*  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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool get_arm_pc(GGdbDebugger *debugger, virt_t *pc) +{ +    bool result;                            /* Bilan à retourner           */ +    uint32_t value; + +    result = g_binary_debugger_read_register_u32(G_BINARY_DEBUGGER(debugger), "pc", &value); + +    if (result) +        *pc = value; + +    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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool compute_call_stack_for_arm(const GGdbDebugger *debugger, virt_t **callstack, size_t *size) +{ +    bool result;                            /* Bilan global à retourner    */ +    GBinaryDebugger *base;                  /* Version basique d'instance  */ +    uint32_t lr;                            /* Retour de fonction          */ +    uint32_t fp;                            /* Pointeur de cadre à suivre  */ + +    base = G_BINARY_DEBUGGER(debugger); + +    result = g_binary_debugger_read_register_u32(base, "lr", &lr); + +    if (result && lr != 0) +    { +        *callstack = (virt_t *)realloc(*callstack, ++(*size) * sizeof(virt_t)); + +        (*callstack)[*size - 1] = lr; + +    } + +    result &= g_binary_debugger_read_register_u32(base, "r11", &fp); + +    while (result && fp != 0) +    { +        /** +         * fp[-0] : pc sauvegardé +         * fp[-1] : lr sauvegardé +         * fp[-2] : sp précédent +         * fp[-3] : fp précédent +         */ + +        result = g_binary_debugger_read_memory_u32(base, fp - 2 * sizeof(uint32_t), &lr); +        if (!result) break; + +        if (lr != 0) +        { +            *callstack = (virt_t *)realloc(*callstack, ++(*size) * sizeof(virt_t)); + +            (*callstack)[*size - 1] = lr; + +        } + +        result = g_binary_debugger_read_memory_u32(base, fp - 4 * sizeof(uint32_t), &fp); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                virt     = emplacement du point mémoire à traiter.           * +*                                                                             * +*  Description : Complète la commande manipulant des points d'arrêt.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static const char *get_breakpoint_kind_for_arm(const GGdbDebugger *debugger, virt_t virt) +{ +    const char *result;                     /* Indication à retourner      */ +    GArchProcessor *proc;                   /* Processeur lié au binaire   */ +    vmpa2t addr;                            /* Format d'adresse complet    */ +    GArchInstruction *instr;                /* Instruction ciblée          */ +    const char *encoding;                   /* Encodage de l'instruction   */ + +    proc = g_loaded_binary_get_processor(G_BINARY_DEBUGGER(debugger)->binary); + +    init_vmpa(&addr, VMPA_NO_PHYSICAL, virt); +    instr = g_arch_processor_find_instr_by_address(proc, &addr); + +    if (instr == NULL) +        result = NULL; + +    else +    { +        encoding = g_arch_instruction_get_encoding(instr); + +        if (strcmp(encoding, "Thumb/16") == 0) +            result = ",2"; + +        if (strcmp(encoding, "Thumb/32") == 0) +            result = ",3"; + +        if (strcmp(encoding, "ARM") == 0) +            result = ",4"; + +        else +            result = NULL; + +        g_object_unref(G_OBJECT(instr)); + +    } + +    g_object_unref(G_OBJECT(proc)); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                addr     = emplacement du point mémoire à traiter.           * +*                len      = quantité de mémoire à remplacer. [OUT]            * +*                                                                             * +*  Description : Construit une instruction provoquant un arrêt d'exécution.   * +*                                                                             * +*  Retour      : Définition du point d'arrêt à placer à l'adresse donnée.     * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static const uint8_t *get_arm_breakpoint_data(const GGdbDebugger *debugger, virt_t addr, size_t *len) +{ +    const uint8_t *result;                  /* Données à placer en mémoire */ + +    /* Version point d'arrêt */ +    static const uint32_t bkpt_code[] = { 0xe1200070 }; + +    *len = sizeof(bkpt_code);; + +    result = (const uint8_t *)bkpt_code; + +    return result; + +} diff --git a/src/debug/gdbrsp/helpers_arm.h b/src/debug/gdbrsp/helpers_arm.h new file mode 100644 index 0000000..70977bc --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm.h - prototypes pour les compléments utiles à GDB pour l'architecture ARM + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_HELPERS_ARM_H +#define _DEBUG_GDBRSP_HELPERS_ARM_H + + +#include "aops.h" + + + +/* Fournit les fonctions adaptées aux opérations pour ARM. */ +const gdb_arch_ops *get_arm_operations(void); + + + +#endif  /* _DEBUG_GDBRSP_HELPERS_ARM_H */ diff --git a/src/debug/gdbrsp/helpers_arm64.c b/src/debug/gdbrsp/helpers_arm64.c new file mode 100644 index 0000000..6807662 --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm64.c @@ -0,0 +1,97 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm64.c - compléments utiles à GDB pour l'architecture AArch64 + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#include "helpers_arm64.h" + + +#include <malloc.h> + + +#include "gdb-int.h" + + + +/****************************************************************************** +*                                                                             * +*  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 compute_call_stack_for_arm64(GGdbDebugger *debugger, virt_t **callstack, size_t *size) +{ +    bool result;                            /* Bilan global à retourner    */ +    GBinaryDebugger *base;                  /* Version basique d'instance  */ +    uint64_t fp;                            /* Pointeur de cadre à suivre  */ +    uint64_t previous;                      /* Appel de fonction précédent */ + +    base = G_BINARY_DEBUGGER(debugger); + +    result = g_binary_debugger_read_register_u64(base, "x29", &fp); + +    while (result && fp != 0) +    { +        result = g_binary_debugger_read_memory_u64(base, fp + sizeof(uint64_t), &previous); +        if (!result) break; + +        *callstack = (virt_t *)realloc(*callstack, ++(*size) * sizeof(virt_t)); + +        (*callstack)[*size - 1] = previous; + +        result = g_binary_debugger_read_memory_u64(base, fp, &fp); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                addr     = emplacement du point mémoire à traiter.           * +*                cmd      = commande en cours de constitution. [OUT]          * +*                                                                             * +*  Description : Complète la commande manipulant des points d'arrêt.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool fill_memory_breakpoint_cmd_for_arm64(GGdbDebugger *debugger, virt_t addr, char *cmd) +{ +    strcat(cmd, ",4"); + +    return true; + +} diff --git a/src/debug/gdbrsp/helpers_arm64.h b/src/debug/gdbrsp/helpers_arm64.h new file mode 100644 index 0000000..80f9312 --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm64.h @@ -0,0 +1,40 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm64.h - prototypes pour les compléments utiles à GDB pour l'architecture AArch64 + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_HELPERS_ARM64_H +#define _DEBUG_GDBRSP_HELPERS_ARM64_H + + +#include "gdb.h" + + + +/* Remonte la pile d'appels jusqu'au point courant. */ +bool compute_call_stack_for_arm64(GGdbDebugger *, virt_t **, size_t *); + +/* Complète la commande manipulant des points d'arrêt. */ +bool fill_memory_breakpoint_cmd_for_arm64(GGdbDebugger *, virt_t, char *); + + + +#endif  /* _DEBUG_GDBRSP_HELPERS_ARM64_H */ diff --git a/src/debug/gdbrsp/packet.c b/src/debug/gdbrsp/packet.c new file mode 100644 index 0000000..4a70a79 --- /dev/null +++ b/src/debug/gdbrsp/packet.c @@ -0,0 +1,389 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * packet.c - manipulation des paquets de données GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#include "packet.h" + + +#include <malloc.h> +#include <string.h> + + +#include "../../common/dllist.h" + + + +/* Répresentation d'un paquet GDB (instance) */ +struct _GGdbPacket +{ +    GObject parent;                         /* A laisser en premier        */ + +    DL_LIST_ITEM(link);                     /* Lien vers les autres        */ + +    char *buffer;                           /* Données à traiter           */ +    size_t len;                             /* Quantité de ces données     */ +    size_t allocated;                       /* Taille du tampon            */ + +    uint8_t checksum;                       /* Empreinte de contrôle       */ + +}; + + +/* Répresentation d'un paquet GDB (classe) */ +struct _GGdbPacketClass +{ +    GObjectClass parent;                    /* A laisser en premier        */ + +}; + + +/* Initialise la classe des représentations des paquets GDB. */ +static void g_gdb_packet_class_init(GGdbPacketClass *); + +/* Initialise une instance de représentation de paquet GDB. */ +static void g_gdb_packet_init(GGdbPacket *); + + + +/* Indique le type défini pour une répresentation de paquet GDB. */ +G_DEFINE_TYPE(GGdbPacket, g_gdb_packet, G_TYPE_OBJECT); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe à initialiser.                                * +*                                                                             * +*  Description : Initialise la classe des représentations des paquets GDB.    * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_packet_class_init(GGdbPacketClass *klass) +{ + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet = instance à initialiser.                             * +*                                                                             * +*  Description : Initialise une instance de représentation de paquet GDB.     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_packet_init(GGdbPacket *packet) +{ +    DL_LIST_ITEM_INIT(&packet->link); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : -                                                            * +*                                                                             * +*  Description : Crée une représentation de paquet GDB.                       * +*                                                                             * +*  Retour      : Adresse de la structure mise en place.                       * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbPacket *g_gdb_packet_new(void) +{ +    GGdbPacket *result;                     /* Structure à retourner       */ + +    result = g_object_new(G_TYPE_GDB_PACKET, NULL); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet = paquet à préparer pour une émission.                * +*                                                                             * +*  Description : Prépare un paquet pour un envoi prochain.                    * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_packet_start_new_command(GGdbPacket *packet) +{ +    if (packet->allocated == 0) +    { +        packet->allocated = 1; +        packet->buffer = (char *)calloc(packet->allocated, sizeof(char)); +    } + +    packet->buffer[0] = '\0'; +    packet->len = 0; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet = paquet à préparer pour une émission.                * +*                string = chaîne à inclure dans le paquet.                    * +*                                                                             * +*  Description : Complète un paquet pour un envoi prochain.                   * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_packet_append(GGdbPacket *packet, const char *string) +{ +    size_t len;                             /* Taille de la chaîne donnée  */ + +    len = strlen(string); + +    /* Si la place n'est pas assez grande */ +    if ((packet->len + len + 1) > packet->allocated) +    { +        packet->buffer = (char *)realloc(packet->buffer, (packet->len + len + 1) * sizeof(char)); +        packet->allocated = packet->len + len + 1; +    } + + +    memcpy(packet->buffer + packet->len, string, len + 1); +    //strcat(packet->buffer, string); + +    packet->len += len; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet = paquet à analyser.                                  * +*                                                                             * +*  Description : Détermine l'empreinte des données d'un paquet GDB.           * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_packet_compute_checksum(GGdbPacket *packet) +{ +    int sum;                                /* Valeur cumulée des données  */ +    size_t i;                               /* Boucle de parcours          */ + +    sum = 0; + +    for (i = 0; i < packet->len; i++) +        sum += packet->buffer[i]; + +    packet->checksum = sum % 256; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet   = paquet à analyser.                                * +*                checksum = contrôle d'intégrité à retrouver.                 * +*                                                                             * +*  Description : Contrôle l'intégrité des données d'un paquet GDB.            * +*                                                                             * +*  Retour      : Bilan de la vérification.                                    * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_packet_verify_checksum(GGdbPacket *packet, uint8_t checksum) +{ +    g_gdb_packet_compute_checksum(packet); + +    return checksum == packet->checksum; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet = paquet à décoder et/ou décompresser.                * +*                                                                             * +*  Description : Décode et/ou décompresse un paquet GDB.                      * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_packet_decode(GGdbPacket *packet) +{ +    bool result;                            /* Bilan à retourner           */ +    char *buffer;                           /* Données transcrites         */ +    size_t allocated;                       /* Quantité de données gérées  */ +    size_t i;                               /* Boucle de parcours          */ +    size_t k;                               /* Point d'insertion           */ +    size_t repeat;                          /* Nombre de répétitions       */ + +    result = true; + +    allocated = packet->len + 1; +    buffer = (char *)calloc(allocated, sizeof(char)); + +    for (i = 0, k = 0; i < packet->len && result; i++) +        switch (packet->buffer[i]) +        { +            case '#': +            case '$': +                result = false; +                break; + +            case '*': + +                if (++i == packet->len || k == 0) +                { +                    result = false; +                    break; +                } + +                repeat = packet->buffer[i] - ' ' + 3; + +                allocated += repeat; +                buffer = (char *)realloc(buffer, allocated * sizeof(char)); + +                memset(&buffer[k], buffer[k - 1], repeat); +                k += repeat; + +                break; + +            case '}': + +                if (++i == packet->len) +                { +                    result = false; +                    break; +                } + +                buffer[k++] = packet->buffer[i] ^ 0x20; + +                break; + +            default: +                buffer[k++] = packet->buffer[i]; +                break; + +        } + +    if (packet->buffer != NULL) +        free(packet->buffer); + +    buffer[k] = '\0'; + +    packet->buffer = buffer; +    packet->len = k; +    packet->allocated = allocated; + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : packet   = paquet à analyser.                                * +*                data     = données contenues dans le paquet. [OUT]           * +*                len      = quantité de ces données ou NULL. [OUT]            * +*                checksum = contrôle d'intégrité des données ou NULL. [OUT]   * +*                                                                             * +*  Description : Fournit le contenu du paquet.                                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_packet_get_data(const GGdbPacket *packet, const char **data, size_t *len, uint8_t *checksum) +{ +    *data = packet->buffer; + +    if (len != NULL) +        *len = packet->len; + +    if (checksum != NULL) +        *checksum = packet->checksum; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : list = liste de paquets à compléter.                         * +*                item = paquet à ajouter à la liste.                          * +*                                                                             * +*  Description : Ajoute un paquet à une liste de paquets.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_packet_push(GGdbPacket **list, GGdbPacket *item) +{ +    dl_list_push(item, list, GGdbPacket, link); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : list = liste de paquets à consulter.                         * +*                                                                             * +*  Description : Retire et fournit le premier élément d'une liste de paquets. * +*                                                                             * +*  Retour      : Elément dépilé de la liste de paquets.                       * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbPacket *g_gdb_packet_pop(GGdbPacket **list) +{ +    return dl_list_pop(list, GGdbPacket, link); + +} diff --git a/src/debug/gdbrsp/packet.h b/src/debug/gdbrsp/packet.h new file mode 100644 index 0000000..2e8abb7 --- /dev/null +++ b/src/debug/gdbrsp/packet.h @@ -0,0 +1,82 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * packet.h - prototypes pour la manipulation des paquets de données GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_PACKET_H +#define _DEBUG_GDBRSP_PACKET_H + + +#include <glib-object.h> +#include <stdbool.h> +#include <stdint.h> + + + +#define G_TYPE_GDB_PACKET               g_gdb_packet_get_type() +#define G_GDB_PACKET(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_gdb_packet_get_type(), GGdbPacket)) +#define G_IS_GDB_PACKET(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_gdb_packet_get_type())) +#define G_GDB_PACKET_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_PACKET, GGdbPacketClass)) +#define G_IS_GDB_PACKET_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_PACKET)) +#define G_GDB_PACKET_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_PACKET, GGdbPacketClass)) + + +/* Répresentation d'un paquet GDB (instance) */ +typedef struct _GGdbPacket GGdbPacket; + +/* Répresentation d'un paquet GDB (classe) */ +typedef struct _GGdbPacketClass GGdbPacketClass; + + + +/* Indique le type défini pour une répresentation de paquet GDB. */ +GType g_gdb_packet_get_type(void); + +/* Crée une représentation de paquet GDB. */ +GGdbPacket *g_gdb_packet_new(void); + +/* Prépare un paquet pour un envoi prochain. */ +void g_gdb_packet_start_new_command(GGdbPacket *); + +/* Complète un paquet pour un envoi prochain. */ +void g_gdb_packet_append(GGdbPacket *, const char *); + +/* Détermine l'empreinte des données d'un paquet GDB. */ +void g_gdb_packet_compute_checksum(GGdbPacket *); + +/* Contrôle l'intégrité des données d'un paquet GDB. */ +bool g_gdb_packet_verify_checksum(GGdbPacket *, uint8_t); + +/* Décode et/ou décompresse un paquet GDB. */ +bool g_gdb_packet_decode(GGdbPacket *); + +/* Fournit le contenu du paquet. */ +void g_gdb_packet_get_data(const GGdbPacket *, const char **, size_t *, uint8_t *); + +/* Ajoute un paquet à une liste de paquets. */ +void g_gdb_packet_push(GGdbPacket **, GGdbPacket *); + +/* Retire et fournit le premier élément d'une liste de paquets. */ +GGdbPacket *g_gdb_packet_pop(GGdbPacket **); + + + +#endif  /* _DEBUG_GDBRSP_PACKET_H */ diff --git a/src/debug/gdbrsp/stream-int.h b/src/debug/gdbrsp/stream-int.h new file mode 100644 index 0000000..db61d13 --- /dev/null +++ b/src/debug/gdbrsp/stream-int.h @@ -0,0 +1,89 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream-int.h - prototypes internes pour la gestion des connexions aux serveurs GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_STREAM_INT_H +#define _DEBUG_GDBRSP_STREAM_INT_H + + +#include "gdb.h" +#include "stream.h" + + + +/* Envoie des données à un serveur GDB. */ +typedef bool (* send_gdb_data_fc) (GGdbStream *, const char *, size_t); + +/* Réceptionne un octet de donnée d'un serveur GDB. */ +typedef bool (* recv_gdb_byte_fc) (GGdbStream *, char *); + + +/* Flux de communication avec un serveur GDB (instance) */ +struct _GGdbStream +{ +    GObject parent;                         /* A laisser en premier        */ + +    int fd;                                 /* Flux ouvert en L./E.        */ + +    GGdbDebugger *owner;                    /* Propriétaire du flux        */ + +    send_gdb_data_fc send_data;             /* Envoi d'un paquet GDB       */ +    recv_gdb_byte_fc recv_byte;             /* Réception d'un paquet GDB   */ + +    GThread *listening;                     /* Thread pour les réceptions  */ + +    GGdbPacket *free_packets;               /* Liste des disponibles       */ +    GMutex free_mutex;                      /* Accès à la liste            */ + +    GGdbPacket *recv_packets;               /* Liste des paquets reçus     */ +    GCond recv_cond;                        /* Attente de disponibilité    */ +    GMutex recv_mutex;                      /* Accès à la liste            */ + +    GGdbPacket *status_packets;             /* Liste des paquets d'état    */ +    GCond status_cond;                      /* Attente de disponibilité    */ +    GMutex status_mutex;                    /* Accès à la liste            */ + + + + +    bool skip_ack; + +    bool want_status; + + +}; + + +/* Flux de communication avec un serveur GDB (classe) */ +struct _GGdbStreamClass +{ +    GObjectClass parent;                    /* A laisser en premier        */ + +}; + + +/* Lance l'écoute d'un flux de communication avec GDB. */ +bool g_gdb_stream_listen(GGdbStream *); + + + +#endif  /* _DEBUG_GDBRSP_STREAM_INT_H */ diff --git a/src/debug/gdbrsp/stream.c b/src/debug/gdbrsp/stream.c new file mode 100644 index 0000000..979ed9b --- /dev/null +++ b/src/debug/gdbrsp/stream.c @@ -0,0 +1,696 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.c - gestion des connexions aux serveurs GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#include "stream.h" + + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <glib/gthread.h> +#include <sys/select.h> + + +#include "gdb-int.h" +#include "stream-int.h" +#include "utils.h" +#include "../../common/dllist.h" +#include "../../gui/panels/log.h" + + + +/* Initialise la classe des flux de communication avec GDB. */ +static void g_gdb_stream_class_init(GGdbStreamClass *); + +/* Initialise une instance de flux de communication avec GDB. */ +static void g_gdb_stream_init(GGdbStream *); + +/* Supprime toutes les références externes. */ +static void g_gdb_stream_dispose(GGdbStream *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_stream_finalize(GGdbStream *); + +/* Envoie un acquittement pour la dernière réception. */ +static bool gdb_stream_ack(GGdbStream *); + +/* Ecoute une connexion à un serveur GDB. */ +static void *gdb_stream_thread(GGdbStream *); + +/* Reste en alerte quant au changement de statut de l'exécution. */ +static void *gdb_stream_status_thread(GGdbStream *); + +/* Réceptionne un paquet d'un serveur GDB. */ +static bool g_gdb_stream_read_packet(GGdbStream *, GGdbPacket *); + + + +/* Indique le type défini pour un flux de communication avec un serveur GDB. */ +G_DEFINE_TYPE(GGdbStream, g_gdb_stream, G_TYPE_OBJECT); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe à initialiser.                                * +*                                                                             * +*  Description : Initialise la classe des flux de communication avec GDB.     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_stream_class_init(GGdbStreamClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_stream_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_stream_finalize; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance à initialiser.                             * +*                                                                             * +*  Description : Initialise une instance de flux de communication avec GDB.   * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_stream_init(GGdbStream *stream) +{ +    g_mutex_init(&stream->free_mutex); + +    g_cond_init(&stream->recv_cond); +    g_mutex_init(&stream->recv_mutex); + +    g_cond_init(&stream->status_cond); +    g_mutex_init(&stream->status_mutex); + +    stream->skip_ack = false; + +    stream->want_status = false; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_stream_dispose(GGdbStream *stream) +{ +    g_object_unref(G_OBJECT(stream->owner)); + + +    /* TODO... */ + + +    G_OBJECT_CLASS(g_gdb_stream_parent_class)->dispose(G_OBJECT(stream)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_stream_finalize(GGdbStream *stream) +{ + +    /* TODO */ + + +    G_OBJECT_CLASS(g_gdb_stream_parent_class)->finalize(G_OBJECT(stream)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance à modifier.                                * +*                                                                             * +*  Description : Ne participe plus aux acquitements de paquets.               * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_stream_do_not_ack(GGdbStream *stream) +{ +    stream->skip_ack = true; + +} + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance à réellement lancer.                       * +*                                                                             * +*  Description : Lance l'écoute d'un flux de communication avec GDB.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_stream_listen(GGdbStream *stream) +{ +    bool result;                            /* Bilan final à retourner     */ + +    result = true; + +    if (!g_thread_new("chrysalide_gdb_stream", (GThreadFunc)gdb_stream_thread, stream)) +        result = false; + +    if (!g_thread_new("chrysalide_gdb_status", (GThreadFunc)gdb_stream_status_thread, stream)) +        result = false; + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = encadrement associée à l'opération.                 * +*                                                                             * +*  Description : Envoie un acquittement pour la dernière réception.           * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool gdb_stream_ack(GGdbStream *stream) +{ +    /// +    //return true; + +    bool result;                            /* Bilan à retourner           */ +    GGdbPacket *packet;                     /* Paquet à envoyer            */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "+"); + +    result = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = encadrement associée à l'opération.                 * +*                                                                             * +*  Description : Ecoute une connexion à un serveur GDB.                       * +*                                                                             * +*  Retour      : ???                                                          * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void *gdb_stream_thread(GGdbStream *stream) +{ +    fd_set rfds;                            /* Liste des flux à surveiller */ +    int ret;                                /* Bilan d'un appel            */ +    GGdbPacket *packet;                     /* Nouveau paquet reçu         */ + + +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ + + + +    while (1) +    { +        FD_ZERO(&rfds); +        FD_SET(stream->fd, &rfds); + +        ret = select(stream->fd + 1, &rfds, NULL, NULL, NULL); + +        switch (ret) +        { +            case -1: +                perror("select()"); +                break; + +            case 0: +                break; + +            default: + +                packet = g_gdb_stream_get_free_packet(stream); + +                g_gdb_packet_start_new_command(packet); + +                if (g_gdb_stream_read_packet(stream, packet)) +                { +                    /* Acquittement ? */ +                    if (!stream->skip_ack) +                    { +                        if (!gdb_stream_ack(stream)) goto bad_recv; +                    } + + +                    /* On conserve le résultat ? */ + + +                    g_gdb_packet_get_data(packet, &data, &len, NULL); + + +                    //printf("---------------------------\n"); +                    //printf(">> want status ? %d\n", stream->want_status); +                    //printf(">> got '%s'\n", data); + + +                    if (len >= 1) +                    { +                        if (stream->want_status) +                            stream->want_status = false; + +                        else if (index("STWX", data[0]) != NULL) +                        { +                            g_mutex_lock(&stream->status_mutex); +                            g_gdb_packet_push(&stream->status_packets, packet); +                            g_mutex_unlock(&stream->status_mutex); + +                            g_cond_signal(&stream->status_cond); + +                            break; +                        } + +                        // else message inconnu -> log_message() ! + +                    } + + + + +                    g_mutex_lock(&stream->recv_mutex); +                    g_gdb_packet_push(&stream->recv_packets, packet); +                    g_mutex_unlock(&stream->recv_mutex); + +                    g_cond_signal(&stream->recv_cond); + +                } + +                else +                    g_gdb_stream_mark_packet_as_free(stream, packet); + +                break; + + bad_recv: + +                printf("bad things happend...\n"); + +                g_gdb_stream_mark_packet_as_free(stream, packet); + +                break; + +        } + +    } + + +    printf("Oh noes....\n"); + + +    return NULL; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = encadrement associée à l'opération.                 * +*                                                                             * +*  Description : Reste en alerte quant au changement de statut de l'exécution.* +*                                                                             * +*  Retour      : ???                                                          * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void *gdb_stream_status_thread(GGdbStream *stream) +{ +    GGdbPacket *packet;                     /* Nouveau paquet reçu         */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ +    bool malformed;                         /* Echec d'interprétation      */ +    uint8_t byte;                           /* Valeur quelconque sur 8 bits*/ +    bool ret;                               /* Bilan d'un appel            */ + +    while (1) +    { +        /* Réception d'un nouveau paquet de statut */ + +        g_mutex_lock(&stream->status_mutex); + +        if (dl_list_empty(stream->status_packets)) +            g_cond_wait(&stream->status_cond, &stream->status_mutex); + +        packet = g_gdb_packet_pop(&stream->status_packets); + +        g_mutex_unlock(&stream->status_mutex); + +        /* Traitement du paquet reçu */ + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        malformed = false; + +        switch (data[0]) +        { +            case 'S': + +                ret = read_fixed_byte(data + 1, len - 1, &byte); + +                if (!ret) +                { +                    malformed = true; +                    goto gsst_processed; +                } + +                g_gdb_debugger_receive_signal_reply(stream->owner, byte); +                break; + +            case 'T': +                assert(false);  // TODO +                break; + +            case 'W': + +                ret = read_fixed_byte(data + 1, len - 1, &byte); + +                if (!ret) +                { +                    malformed = true; +                    goto gsst_processed; +                } + + +                // TODO : ";process:pid" + + +                printf("Program exited (status=%hhu)\n", byte); + + +                g_gdb_debugger_receive_exit_reply(stream->owner, byte, -1); + + +                // log_message en cas de mauvais format... + + +                break; + + +            default: +                assert(false); +                break; + +        } + + gsst_processed: + +        if (malformed && true/* TODO : config->show_... */) +            log_variadic_message(LMT_WARNING, "Malformed GDB status reply: '%s'", data); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +    } + +    return NULL; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication avec GDB à consulter.         * +*                                                                             * +*  Description : Fournit un paquet prêt à emploi.                             * +*                                                                             * +*  Retour      : Paquet prêt à emploi.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *stream) +{ +    GGdbPacket *result;                     /* Paquet à retourner          */ + +    g_mutex_lock(&stream->free_mutex); + +    if (dl_list_empty(stream->free_packets)) +        result = g_gdb_packet_new(); + +    else +        result = g_gdb_packet_pop(&stream->free_packets); + +    g_mutex_unlock(&stream->free_mutex); + +    // ??? +    //g_gdb_packet_start_new_command(result); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication avec GDB à mettre à jour.     * +*                packet = paquet à considérer comme disponible.               * +*                                                                             * +*  Description : Place un paquet en attente d'une future utilisation.         * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_stream_mark_packet_as_free(GGdbStream *stream, GGdbPacket *packet) +{ +    //// Utile ? +    g_gdb_packet_start_new_command(packet); + + +    g_mutex_lock(&stream->free_mutex); + +    g_gdb_packet_push(&stream->free_packets, packet); + +    g_mutex_unlock(&stream->free_mutex); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux ouvert en lecture  à utiliser.                 * +*                packet = données à recevoir.                                 * +*                                                                             * +*  Description : Réceptionne un paquet d'un serveur GDB.                      * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_stream_read_packet(GGdbStream *stream, GGdbPacket *packet) +{ +    bool result;                            /* Bilan à renvoyer            */ +    char tmp[3];                            /* Tampon de réception         */ +    uint8_t checksum;                       /* Contrôle d'intégrité        */ + +    do +    { +        result = stream->recv_byte(stream, tmp); +        if (tmp[0] != '+') break; +    } +    while (0); + +    if (tmp[0] != '$') return false; + +    tmp[1] = '\0'; + +    while ((result = stream->recv_byte(stream, tmp))) +    { +        //printf(" .. '%c'\n", tmp[0]); + +        if (tmp[0] == '#') break; +        else g_gdb_packet_append(packet, tmp); +    } + +    if (result) +    { +        result = stream->recv_byte(stream, &tmp[0]); +        result &= stream->recv_byte(stream, &tmp[1]); + +        tmp[2] = 0; +        checksum = strtol(tmp, NULL, 16); + +    } + +    if (result) +        result = g_gdb_packet_verify_checksum(packet, checksum); + +    if (result) +        result = g_gdb_packet_decode(packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux ouvert en écriture à mettre à jour.            * +*                packet = données à transmettre.                              * +*                                                                             * +*  Description : Envoie un paquet à un serveur GDB.                           * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ +#include <string.h> +bool g_gdb_stream_send_packet(GGdbStream *stream, GGdbPacket *packet) +{ +    bool result;                            /* Bilan à renvoyer            */ +    const char *data;                       /* Données à envoyer           */ +    size_t len;                             /* Quantité de ces données     */ +    uint8_t checksum;                       /* Contrôle d'intégrité        */ +    char tmp[3];                            /* Impression du checksum      */ + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +#if 1 +    /* Ack ? */ +    if (len == 1 && data[0] == '+') +        result = stream->send_data(stream, "+", 1); + +    else +#endif +    { + +    result = stream->send_data(stream, "$", 1); +    //result = stream->send_data(stream, "+$", 2); + +    g_gdb_packet_compute_checksum(packet); +    g_gdb_packet_get_data(packet, &data, &len, &checksum); + + +    if (len == 1 && data[0] == '?') +        stream->want_status = true; + +    /* +    if (memcmp(data, "vCont;c", strlen("vCont;c")) == 0) +        stream->want_status = true; +    */ + + + +    result &= stream->send_data(stream, data, len); + +    result = stream->send_data(stream, "#", 1); + +    snprintf(tmp, 3, "%02hhx", checksum); +    result &= stream->send_data(stream, tmp, 2); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication avec GDB à consulter.         * +*                                                                             * +*  Description : Fournit un paquet reçu d'un serveur GDB.                     * +*                                                                             * +*  Retour      : Paquet GDB.                                                  * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbPacket *g_gdb_stream_recv_packet(GGdbStream *stream) +{ +    GGdbPacket *result;                     /* Paquet à retourner          */ + +    g_mutex_lock(&stream->recv_mutex); + +    if (dl_list_empty(stream->recv_packets)) +        g_cond_wait(&stream->recv_cond, &stream->recv_mutex); + +    result = g_gdb_packet_pop(&stream->recv_packets); + +    g_mutex_unlock(&stream->recv_mutex); + +    return result; + +} diff --git a/src/debug/gdbrsp/stream.h b/src/debug/gdbrsp/stream.h new file mode 100644 index 0000000..b4b483d --- /dev/null +++ b/src/debug/gdbrsp/stream.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.h - prototypes pour la gestion des connexions aux serveurs GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_STREAM_H +#define _DEBUG_GDBRSP_STREAM_H + + +#include "packet.h" + + + +#define G_TYPE_GDB_STREAM               g_gdb_stream_get_type() +#define G_GDB_STREAM(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_gdb_stream_get_type(), GGdbStream)) +#define G_IS_GDB_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_gdb_stream_get_type())) +#define G_GDB_STREAM_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_STREAM, GGdbStreamClass)) +#define G_IS_GDB_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_STREAM)) +#define G_GDB_STREAM_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_STREAM, GGdbStreamClass)) + + +/* Flux de communication avec un serveur GDB (instance) */ +typedef struct _GGdbStream GGdbStream; + +/* Flux de communication avec un serveur GDB (classe) */ +typedef struct _GGdbStreamClass GGdbStreamClass; + + + +/* Indique le type défini pour un flux de communication avec un serveur GDB. */ +GType g_gdb_stream_get_type(void); + +/* Ne participe plus aux acquitements de paquets. */ +void g_gdb_stream_do_not_ack(GGdbStream *); + +/* Fournit un paquet prêt à emploi. */ +GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *); + +/* Place un paquet en attente d'une future utilisation. */ +void g_gdb_stream_mark_packet_as_free(GGdbStream *, GGdbPacket *); + +/* Envoie un paquet à un serveur GDB. */ +bool g_gdb_stream_send_packet(GGdbStream *, GGdbPacket *); + +/* Fournit un paquet reçu d'un serveur GDB. */ +GGdbPacket *g_gdb_stream_recv_packet(GGdbStream *); + + + +#endif  /* _DEBUG_GDBRSP_STREAM_H */ diff --git a/src/debug/gdbrsp/support.c b/src/debug/gdbrsp/support.c new file mode 100644 index 0000000..5086540 --- /dev/null +++ b/src/debug/gdbrsp/support.c @@ -0,0 +1,598 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * support.c - conformité dans l'interfaçage client/serveur + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#include "support.h" + + +#include <stdlib.h> +#include <string.h> + + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +struct _GGdbSupport +{ +    GObject parent;                         /* A laisser en premier        */ + +    unsigned long packet_size;              /* Taille maximale d'un paquet */ + +    bool os_data; + + +    bool extended_mode;                     /* Mode étendu présent & actif */ + + +    char *id; + +}; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +struct _GGdbSupportClass +{ +    GObjectClass parent;                    /* A laisser en premier        */ + +}; + + +/* Initialise la classe des détails d'interfaçage GDB. */ +static void g_gdb_support_class_init(GGdbSupportClass *); + +/* Procède à l'initialisation des détails d'interfaçage GDB. */ +static void g_gdb_support_init(GGdbSupport *); + +/* Supprime toutes les références externes. */ +static void g_gdb_support_dispose(GGdbSupport *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_support_finalize(GGdbSupport *); + +/* Lit une valeur booléenne à partir des détails du serveur. */ +static bool g_gdb_support_read_bool(GGdbSupport *, const char *, const char *, bool *); + +/* Lit une valeur longue à partir des détails du serveur. */ +static bool g_gdb_support_read_ulong(GGdbSupport *, const char *, const char *, unsigned long *); + + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +G_DEFINE_TYPE(GGdbSupport, g_gdb_support, G_TYPE_OBJECT); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe de débogueur à initialiser.                   * +*                                                                             * +*  Description : Initialise la classe des détails d'interfaçage GDB.          * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_class_init(GGdbSupportClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_support_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_support_finalize; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = instance de débogueur à préparer.                  * +*                                                                             * +*  Description : Procède à l'initialisation des détails d'interfaçage GDB.    * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_init(GGdbSupport *support) +{ + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = instance d'objet GLib à traiter.                   * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_dispose(GGdbSupport *support) +{ +    G_OBJECT_CLASS(g_gdb_support_parent_class)->dispose(G_OBJECT(support)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = instance d'objet GLib à traiter.                   * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_finalize(GGdbSupport *support) +{ +    G_OBJECT_CLASS(g_gdb_support_parent_class)->finalize(G_OBJECT(support)); + +} + + + + + + + + + + + + + + +#include <string.h> + +static char *build_id(GGdbStream *stream) +{ +    char *result;                           /* Identifiant à renvoyer      */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    bool status;                            /* Bilan d'une communication   */ +    const char *data;                       /* Données reçues à analyser   */ +    const char *start;                      /* Début d'identification      */ +    const char *end;                        /* Fin d'identification        */ + +    result = NULL; + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "?"); + +    status = g_gdb_stream_send_packet(stream, packet); + +    if (!status) +        goto ggdgat_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    start = strstr(data, "thread:"); +    if (start == NULL) goto ggdgat_exit; + +    start += sizeof("thread:") - 1 /* '\0' */; + +    end = strstr(start, ";"); +    if (end == NULL) goto ggdgat_exit; + +    result = strndup(start, end - start); + + ggdgat_exit: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication ouvert avec le débogueur.     * +*                                                                             * +*  Description : Crée une définition des détails d'interfaçage GDB.           * +*                                                                             * +*  Retour      : Instance de détails mise en place ou NULL.                   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbSupport *g_gdb_support_new(GGdbStream *stream) +{ +    GGdbSupport *result;                    /* Débogueur à retourner       */ +    GGdbPacket *packet;                     /* Paquet de communication GDB */ + + +    //goto end; + +    //goto skip; + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qSupported:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, "qSupported"); + +    g_gdb_packet_append(packet, "qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+"); + + +    bool test; + +    const char *data;                       /* Données reçues à analyser   */ +    size_t len; + +    test = g_gdb_stream_send_packet(stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", "qSupported", test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << Réception de '%s'\n", data); + + + + +    result = g_object_new(G_TYPE_GDB_SUPPORT, NULL); + + + +    /* Découpage des éléments de réponse */ + +    char *answer;                           /* Réponse modifiable          */ +    char *save;                             /* Sauvegarde de position      */ +    char *token;                            /* Elément de réponse cerné    */ + +    answer = strdup(data); + +    for (token = strtok_r(answer, ";", &save); +         token != NULL; +         token = strtok_r(NULL, ";", &save)) +    { + + +        printf("TOKEN :: %s\n", token); + +        if (g_gdb_support_read_ulong(result, token, "PacketSize", &result->packet_size)) +            continue; + +        if (g_gdb_support_read_bool(result, token, "qXfer:osdata:read", &result->os_data)) +        { +            printf(" -->> %d\n", result->os_data); +            continue; +        } + + + + +    } + +    free(answer); + + + +    /** +     * Première chose : plus d'acquitement ! +     * +     * Dans les faits, c'est impossible à gérer en asynchrone. Par exemple : +     * +     *  C> vCont;c +     *  C> g           Txx... <S +     * +     * Si le client envoie une commande en même temps que le serveur envoie +     * quelque chose, le serveur attend dans tous les cas un acquitement. +     * Donc il va consommer les données envoyées par le client jusqu'à y +     * trouver ce qu'il cherche. +     */ + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "QStartNoAckMode"); + +    test = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!test) +        goto ggsn_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    if (strcmp(data, "OK") != 0) +        goto ggsn_error; + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    /* Désactivation des acquitements */ + +    g_gdb_stream_do_not_ack(stream); + +    /** +     * Passage en mode étendu. C'est obligatoire pour pouvoir redémarrer un +     * programme débogué. +     */ + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "!"); + +    test = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!test) +        goto ggsn_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    result->extended_mode = (strcmp(data, "OK") == 0); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + + + + +    result->id = build_id(stream); + + + +#if 0 +    //end: + +#define CMD "?" + + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qSupported:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, CMD); + + +    test = g_gdb_stream_send_packet(stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", CMD, test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << [pkt = %p ] Réception de '%s' (len=%d)\n", packet, data, (int)len); + + +#endif + + + +    // qfThreadInfo + + +#undef CMD + +    //#define CMD "qXfer:threads:read::0,1fff" +    //#define CMD "qXfer:btrace:read:all:0,1fff" +    //#define CMD "g" +    //#define CMD "m400000,8" +#define CMD "qsThreadInfo" + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qSupported:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, CMD); + + +    test = g_gdb_stream_send_packet(stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", CMD, test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << [pkt = %p ] Réception de '%s' (len=%d)\n", packet, data, (int)len); + + + + + + + + + + + +    return result; + + ggsn_error: + + + +    return NULL; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = ensemble de détails à préciser.                    * +*                raw     = données brutes à parcourir.                        * +*                name    = désignation de la valeur recherchée.               * +*                value   = emplacement de la valeur à inscrire.               * +*                                                                             * +*  Description : Lit une valeur booléenne à partir des détails du serveur.    * +*                                                                             * +*  Retour      : true en cas d'affectation, false dans tous les autres cas.   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_support_read_bool(GGdbSupport *support, const char *raw, const char *name, bool *value) +{ +    bool result;                            /* Bilan à retourner           */ +    size_t rlen;                            /* Taille de l'ensemble        */ +    size_t nlen;                            /* Taille du nom               */ + +    rlen = strlen(raw); +    nlen = strlen(name); + +    if ((nlen + 1) != rlen) +        return false; + +    if (strncmp(raw, name, nlen) != 0) +        return false; + +    switch (raw[nlen]) +    { +        case '+': +            *value = true; +            result = true; +            break; + +        case '-': +        case '?': +            *value = false; +            result = true; +            break; + +        default: +            result = false; +            break; + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = ensemble de détails à préciser.                    * +*                raw     = données brutes à parcourir.                        * +*                name    = désignation de la valeur recherchée.               * +*                value   = emplacement de la valeur à inscrire.               * +*                                                                             * +*  Description : Lit une valeur longue à partir des détails du serveur.       * +*                                                                             * +*  Retour      : true en cas d'affectation, false dans tous les autres cas.   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_support_read_ulong(GGdbSupport *support, const char *raw, const char *name, unsigned long *value) +{ +    size_t rlen;                            /* Taille de l'ensemble        */ +    size_t nlen;                            /* Taille du nom               */ +    unsigned long v;                        /* Valeur récupérée à assigner */ + +    rlen = strlen(raw); +    nlen = strlen(name); + +    if (strncmp(raw, name, nlen) != 0) +        return false; + +    if (raw[nlen] != '=') +        return false; + +    v = strtoul(raw + nlen + 1, NULL, 16); + +    if (v == ULONG_MAX/* && errno == ERANGE*/) +        return false; + +    *value = v; + +    return true; + +} + + + + + + +char *g_gdb_support_get_id(const GGdbSupport *support) +{ +    return support->id; + +} + + + + + diff --git a/src/debug/gdbrsp/support.h b/src/debug/gdbrsp/support.h new file mode 100644 index 0000000..6b4ab63 --- /dev/null +++ b/src/debug/gdbrsp/support.h @@ -0,0 +1,73 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * support.h - prototypes pour la conformité dans l'interfaçage client/serveur + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_SUPPORT_H +#define _DEBUG_GDBRSP_SUPPORT_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include "stream.h" + + + +#define G_TYPE_GDB_SUPPORT            (g_gdb_support_get_type()) +#define G_GDB_SUPPORT(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GDB_SUPPORT, GGdbSupport)) +#define G_IS_GDB_SUPPORT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GDB_SUPPORT)) +#define G_GDB_SUPPORT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_SUPPORT, GGdbSupportClass)) +#define G_IS_GDB_SUPPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_SUPPORT)) +#define G_GDB_SUPPORT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_SUPPORT, GGdbSupportClass)) + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +typedef struct _GGdbSupport GGdbSupport; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +typedef struct _GGdbSupportClass GGdbSupportClass; + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +GType g_gdb_support_get_type(void); + +/* Crée une définition des détails d'interfaçage GDB. */ +GGdbSupport *g_gdb_support_new(GGdbStream *); + + + + + + + +char *g_gdb_support_get_id(const GGdbSupport *support); + + + + + + + + + +#endif  /* _DEBUG_GDBRSP_SUPPORT_H */ diff --git a/src/debug/gdbrsp/target.c b/src/debug/gdbrsp/target.c new file mode 100644 index 0000000..d6edf6c --- /dev/null +++ b/src/debug/gdbrsp/target.c @@ -0,0 +1,950 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * target.c - gestion des éléments propres à l'architecture reconnue par GDB + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#include "target.h" + + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#include "utils.h" +#include "../../common/cpp.h" +#include "../../common/extstr.h" +#include "../../common/xml.h" + + + +/* Définitions de registres */ + +typedef struct _arch_register_t +{ +    char *name;                             /* Nom de registre             */ +    unsigned int size;                      /* Taille en bits              */ + +} arch_register_t; + +typedef struct _target_cpu_t +{ +    char *label;                            /* Désignation de l'ensemble   */ + +    arch_register_t *regs;                  /* Définition des registres    */ +    unsigned int count;                     /* Quantité de ces définitions */ + +} target_cpu_t; + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +struct _GGdbTarget +{ +    GObject parent;                         /* A laisser en premier        */ + +    target_cpu_t **defs;                    /* Liste de définitions        */ +    size_t count;                           /* Taille de cette même liste  */ + +    bool read_single_register;              /* Lecture spécifique permise ?*/ +    bool write_single_register;             /* Ecriture spécifique valide ?*/ + +}; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +struct _GGdbTargetClass +{ +    GObjectClass parent;                    /* A laisser en premier        */ + +}; + + +/* Initialise la classe des détails d'interfaçage GDB. */ +static void g_gdb_target_class_init(GGdbTargetClass *); + +/* Procède à l'initialisation des détails d'interfaçage GDB. */ +static void g_gdb_target_init(GGdbTarget *); + +/* Supprime toutes les références externes. */ +static void g_gdb_target_dispose(GGdbTarget *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_target_finalize(GGdbTarget *); + +/* Charge la définition d'un groupe de registres. */ +static bool g_gdb_target_load_register_definition(GGdbTarget *, GGdbStream *, const char *); + +/* Recherche l'indice correspondant à un registre donné. */ +static bool g_gdb_target_find_register_index(const GGdbTarget *, const char *, unsigned int *); + +/* Recherche la position correspondant à un registre donné. */ +static bool g_gdb_target_find_register_offset(const GGdbTarget *, unsigned int, size_t *); + + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +G_DEFINE_TYPE(GGdbTarget, g_gdb_target, G_TYPE_OBJECT); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe de débogueur à initialiser.                   * +*                                                                             * +*  Description : Initialise la classe des détails d'interfaçage GDB.          * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_class_init(GGdbTargetClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_target_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_target_finalize; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = instance de débogueur à préparer.                   * +*                                                                             * +*  Description : Procède à l'initialisation des détails d'interfaçage GDB.    * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_init(GGdbTarget *target) +{ +    target->defs = NULL; +    target->count = 0; + +    target->read_single_register = true; +    target->write_single_register = true; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_dispose(GGdbTarget *target) +{ +    G_OBJECT_CLASS(g_gdb_target_parent_class)->dispose(G_OBJECT(target)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_finalize(GGdbTarget *target) +{ +    G_OBJECT_CLASS(g_gdb_target_parent_class)->finalize(G_OBJECT(target)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication ouvert avec le débogueur.     * +*                                                                             * +*  Description : Crée une définition des détails d'interfaçage GDB.           * +*                                                                             * +*  Retour      : Instance de détails mise en place ou NULL.                   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbTarget *g_gdb_target_new(GGdbStream *stream) +{ +    GGdbTarget *result;                     /* Débogueur à retourner       */ +    GGdbPacket *packet;                     /* Paquet de communication GDB */ +    bool status;                            /* Bilan d'une communication   */ + +    const char *data;                       /* Données reçues du serveur   */ +    size_t len;                             /* Quantité de ces données     */ +    char *xmldata;                          /* Données modifiables         */ +    xmlDocPtr xdoc;                         /* Document XML récupéré       */ +    xmlXPathContextPtr context;             /* COntexte d'analyse associé  */ +    xmlXPathObjectPtr xobject;              /* Cible d'une recherche       */ +    unsigned int i;                         /* Boucle de parcours          */ +    char *access;                           /* Chemin d'accès à un élément */ +    char *xmlref;                           /* Référence de définitions    */ + + + + +    result = NULL; + + +    //goto end; + +    //goto skip; + + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qTargeted:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, "qXfer:features:read:target.xml:0,3fff"); + +    //g_gdb_packet_append(packet, "qTargeted:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContTargeted+;QThreadEvents+;no-resumed+"); + + + +    status = g_gdb_stream_send_packet(stream, packet); +    if (!status) goto ggtn_failed; + + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << Réception de '%s'\n", data); + +    /* Marqueur de fin placé au début ?! */ +    if (data[0] != 'l') +        goto ggtn_failed; + +    xmldata = strdup(data + 1); + +    /** +     * On cherche à éviter la déconvenue suivante avec la libxml2 : +     * +     *    noname.xml:12: namespace error : Namespace prefix xi on include is not defined +     *      <xi:include href="aarch64-core.xml"/> +     */ + +    xmldata = strrpl(xmldata, "xi:include", "include"); + +    if (!load_xml_from_memory(xmldata, len - 1, &xdoc, &context)) +        goto ggtn_failed; + + +    result = g_object_new(G_TYPE_GDB_TARGET, NULL); + + +    xobject = get_node_xpath_object(context, "/target/include"); + +    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) +    { +        asprintf(&access, "/target/include[position()=%u]", i + 1); + +        xmlref = get_node_prop_value(context, access, "href"); + +        free(access); + +        if (xmlref != NULL) +        { +            printf("REF>> %s\n", xmlref); +            /*static bool */g_gdb_target_load_register_definition(result, stream, xmlref); + +            free(xmlref); + +        } + +    } + +    if(xobject != NULL) +        xmlXPathFreeObject(xobject); + +    close_xml_file(xdoc, context); + +    free(xmldata); + + + + + + + + + + +    //result = g_object_new(G_TYPE_GDB_TARGET, NULL); + + + ggtn_failed: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                stream = flux de communication ouvert avec le débogueur.     * +*                name   = désignation des définitions de registres à charger. * +*                                                                             * +*  Description : Charge la définition d'un groupe de registres.               * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_target_load_register_definition(GGdbTarget *target, GGdbStream *stream, const char *name) +{ +    bool result;                            /* Bilan à retourner           */ +    GGdbPacket *packet;                     /* Paquet de communication GDB */ +    bool status;                            /* Bilan d'une communication   */ +    const char *data;                       /* Données reçues du serveur   */ +    size_t len;                             /* Quantité de ces données     */ +    xmlDocPtr xdoc;                         /* Document XML récupéré       */ +    xmlXPathContextPtr context;             /* COntexte d'analyse associé  */ +    xmlXPathObjectPtr xobject;              /* Cible d'une recherche       */ +    target_cpu_t *def;                      /* Nouvelle définition à lire  */ +    unsigned int i;                         /* Boucle de parcours          */ +    char *access;                           /* Chemin d'accès à un élément */ +    char *type;                             /* Espèce de définition        */ + +    result = false; + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); + +    g_gdb_packet_append(packet, "qXfer:features:read:"); +    g_gdb_packet_append(packet, name); +    g_gdb_packet_append(packet, ":0,3fff"); + +    status = g_gdb_stream_send_packet(stream, packet); +    if (!status) goto ggtlrd_failed; + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    //printf(">>>> '%s'\n", data); + +    /* Marqueur de fin placé au début ?! */ +    if (data[0] != 'l') +        goto ggtlrd_failed; + +    if (!load_xml_from_memory(data + 1, len - 1, &xdoc, &context)) +        goto ggtlrd_failed; + +    /* Chargement des définitions */ + +    xobject = get_node_xpath_object(context, "/feature/*"); + +    def = (target_cpu_t *)calloc(1, sizeof(target_cpu_t)); + +    def->count = XPATH_OBJ_NODES_COUNT(xobject); +    def->regs = (arch_register_t *)calloc(def->count, sizeof(arch_register_t)); + +    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) +    { +        asprintf(&access, "/feature/*[position()=%u]", i + 1); + +        type = get_node_name(context, access); + +        if (strcmp(type, "reg") == 0) +        { +            def->regs[i].name = get_node_prop_value(context, access, "name"); +            def->regs[i].size = atoi(get_node_prop_value(context, access, "bitsize")); + +            //printf("load reg '%s' (%u)\n", def->regs[i].name, def->regs[i].size); + +        } + +        free(type); + +        free(access); + +    } + +    if(xobject != NULL) +        xmlXPathFreeObject(xobject); + +    close_xml_file(xdoc, context); + +    /* Intégration finale */ + +    target->defs = (target_cpu_t **)realloc(target->defs, ++target->count * sizeof(target_cpu_t *)); + +    target->defs[target->count - 1] = def; + + ggtlrd_failed: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                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_gdb_target_get_register_names(const GGdbTarget *target, const char *group, size_t *count) +{ +    char **result;                          /* Désignations à retourner    */ +    unsigned int i;                         /* Boucle de parcours #1       */ +    const target_cpu_t *rgrp;               /* Groupe de registres         */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    result = NULL; + +    for (i = 0; i < target->count && result == NULL; i++) +    { +        rgrp = target->defs[i]; + +        if (group != NULL) +        { +            if (strcmp(rgrp->label, group) != 0) +                continue; +        } + +        *count = rgrp->count; + +        result = (char **)calloc(*count, sizeof(char *)); + +        for (j = 0; j < *count; j++) +            result[j] = strdup(rgrp->regs[j].name); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                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_gdb_target_get_register_size(const GGdbTarget *target, const char *name) +{ +    unsigned int result;                    /* Taille en bits  à retourner */ +    unsigned int i;                         /* Boucle de parcours #1       */ +    const target_cpu_t *rgrp;               /* Groupe de registres         */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    result = 0; + +    for (i = 0; i < target->count && result == 0; i++) +    { +        rgrp = target->defs[i]; + +        for (j = 0; j < rgrp->count; j++) +            if (strcmp(rgrp->regs[j].name, name) == 0) +                result = rgrp->regs[j].size; + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                reg    = désignation humaine du register à consulter.        * +*                index  = indice correspondant au registre pour GDB. [OUT]    * +*                                                                             * +*  Description : Recherche l'indice correspondant à un registre donné.        * +*                                                                             * +*  Retour      : Bilan de l'opération : trouvaille ou échec ?                 * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_target_find_register_index(const GGdbTarget *target, const char *reg, unsigned int *index) +{ +    bool result;                            /* Bilan à retourner           */ +    unsigned int i;                         /* Boucle de parcours #1       */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    result = false; + +    *index = 0; + +    for (i = 0; i < target->count && !result; i++) +        for (j = 0; j < target->defs[i]->count && !result; j++) +        { +            if (strcmp(target->defs[i]->regs[j].name, reg) == 0) +                result = true; +            else +                (*index)++; +        } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                index  = indice correspondant au registre pour GDB.          * +*                offset = position de valeur du registre dans du texte. [OUT] * +*                                                                             * +*  Description : Recherche la position correspondant à un registre donné.     * +*                                                                             * +*  Retour      : Bilan de l'opération : trouvaille ou échec ?                 * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_target_find_register_offset(const GGdbTarget *target, unsigned int index, size_t *offset) +{ +    unsigned int i;                         /* Boucle de parcours #1       */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    *offset = 0; + +    for (i = 0; i < target->count && index > 0; i++) +        for (j = 0; j < target->defs[i]->count && index > 0; j++) +        { +            assert(target->defs[i]->regs[j].size % 4 == 0); + +            *offset += target->defs[i]->regs[j].size / 4; + +            index--; + +        } + +    return (index == 0); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                stream = flux de communication ouvert avec le débogueur.     * +*                endian = boutisme de la cible.                               * +*                reg    = désignation humaine du register à consulter.        * +*                size   = taille des données mises en jeu.                    * +*                ...    = emplacement de la valeur lue à conserver. [OUT]     * +*                                                                             * +*  Description : Effectue la lecture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_target_read_register(GGdbTarget *target, GGdbStream *stream, SourceEndian endian, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan à retourner           */ +    unsigned int index;                     /* Indice du registre ciblé    */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    char cmd[sizeof(XSTR(UINT_MAX)) + 1];   /* Elément de requête          */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ +    const char *raw;                        /* Début de zone à relire      */ +    size_t offset;                          /* Position dans la masse      */ +    va_list ap;                             /* Liste variable d'arguments  */ +    uint8_t *val8;                          /* Valeur sur 8 bits           */ +    uint16_t *val16;                        /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    uint32_t *val32;                        /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    uint64_t *val64;                        /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ + +    result = g_gdb_target_find_register_index(target, reg, &index); +    if (!result) goto ggtrr_error; + +    /** +     * Essai avec la méthode précise. +     */ + +    if (!target->read_single_register) +        goto read_all_register_fallback; + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "p"); + +    snprintf(cmd, sizeof(cmd), "%x", index); +    g_gdb_packet_append(packet, cmd); + +    result = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!result) +        goto ggtrr_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (len != 0 && !is_error_code(data, len)) +        raw = data; + +    else +    { +        target->read_single_register = false; + +        g_gdb_stream_mark_packet_as_free(stream, packet); + + read_all_register_fallback: + +        /** +         * Utilisation de la méthode de masse au besoin... +         */ + +        packet = g_gdb_stream_get_free_packet(stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, "g"); + +        result = g_gdb_stream_send_packet(stream, packet); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        if (!result) +            goto ggtrr_error; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        result = g_gdb_target_find_register_offset(target, index, &offset); + +        if (!result || offset > len) +            goto ggtrr_exit; + +        raw = data + offset; +        len -= offset; + +    } + +    /* Lecture finale de la valeur recherchée */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = hex_to_u8(raw, val8); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            result = hex_to_u16(raw, &conv16); +            *val16 = from_u16(&conv16, endian); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            result = hex_to_u32(raw, &conv32); +            *val32 = from_u32(&conv32, endian); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            result = hex_to_u64(raw, &conv64); +            *val64 = from_u64(&conv64, endian); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + + ggtrr_exit: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + + ggtrr_error: + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                stream = flux de communication ouvert avec le débogueur.     * +*                endian = boutisme de la cible.                               * +*                reg    = désignation humaine du register à consulter.        * +*                size   = taille des données mises en jeu.                    * +*                ...    = emplacement de la valeur à écrire.                  * +*                                                                             * +*  Description : Effectue l'écriture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_target_write_register(GGdbTarget *target, GGdbStream *stream, SourceEndian endian, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    va_list ap;                             /* Liste variable d'arguments  */ +    const uint8_t *val8;                    /* Valeur sur 8 bits           */ +    const uint16_t *val16;                  /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    const uint32_t *val32;                  /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    const uint64_t *val64;                  /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ +    char hexval[17];                        /* Valeur sous forme hexa      */ +    unsigned int index;                     /* Indice du registre ciblé    */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    char cmd[sizeof(XSTR(UINT_MAX)) + 1];   /* Elément de requête          */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ +    char *new;                              /* Nouvelles valeurs générales */ +    size_t offset;                          /* Position dans la masse      */ + +    /* Tronc commun : récupération de la valeur */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = u8_to_hex(val8, hexval); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            conv16 = to_u16(val16, endian); +            result = u16_to_hex(&conv16, hexval); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            conv32 = to_u32(val32, endian); +            result = u32_to_hex(&conv32, hexval); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            conv64 = to_u64(val64, endian); +            result = u16_to_hex(&conv64, hexval); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + +    if (!result) +        goto ggtwr_error; + +    /* Préparation de la suite */ + +    result = g_gdb_target_find_register_index(target, reg, &index); +    if (!result) goto ggtwr_error; + +    /** +     * Essai avec la méthode précise. +     */ + +    if (!target->write_single_register) +        goto write_all_register_fallback; + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "P"); + +    snprintf(cmd, sizeof(cmd), "%x", index); +    g_gdb_packet_append(packet, cmd); + +    g_gdb_packet_append(packet, "="); + +    g_gdb_packet_append(packet, hexval); + +    result = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!result) +        goto ggtwr_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (is_error_code(data, len) || strcmp(data, "OK") != 0) +    { +        target->write_single_register = false; + +        g_gdb_stream_mark_packet_as_free(stream, packet); + + write_all_register_fallback: + +        /** +         * Utilisation de la méthode de masse au besoin... +         */ + +        /* Lecture de l'ensemble des registres */ + +        packet = g_gdb_stream_get_free_packet(stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, "g"); + +        result = g_gdb_stream_send_packet(stream, packet); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        if (!result) +            goto ggtwr_error; + +        /* Réception de la réponse et mise à jour */ + +        packet = g_gdb_stream_recv_packet(stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        result = g_gdb_target_find_register_offset(target, index, &offset); + +        if (!result || offset > len) +            goto ggtwr_exit; + +        new = (char *)malloc(len); + +        memcpy(new, data, len); +        memcpy(new + offset, hexval, strlen(hexval)); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        /* Ecrasement de tous les registres */ + +        packet = g_gdb_stream_get_free_packet(stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, "G"); + +        g_gdb_packet_append(packet, new); +        free(new); + +        result = g_gdb_stream_send_packet(stream, packet); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        if (!result) +            goto ggtwr_error; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        result = (!is_error_code(data, len) && strcmp(data, "OK") == 0); + +    } + + ggtwr_exit: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + + ggtwr_error: + +    return result; + +} diff --git a/src/debug/gdbrsp/target.h b/src/debug/gdbrsp/target.h new file mode 100644 index 0000000..805ff49 --- /dev/null +++ b/src/debug/gdbrsp/target.h @@ -0,0 +1,72 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * target.h - prototypes pour la gestion des éléments propres à l'architecture reconnue par GDB + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_TARGET_H +#define _DEBUG_GDBRSP_TARGET_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include "stream.h" +#include "../../common/endianness.h" + + + +#define G_TYPE_GDB_TARGET            (g_gdb_target_get_type()) +#define G_GDB_TARGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GDB_TARGET, GGdbTarget)) +#define G_IS_GDB_TARGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GDB_TARGET)) +#define G_GDB_TARGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_TARGET, GGdbTargetClass)) +#define G_IS_GDB_TARGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_TARGET)) +#define G_GDB_TARGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_TARGET, GGdbTargetClass)) + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +typedef struct _GGdbTarget GGdbTarget; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +typedef struct _GGdbTargetClass GGdbTargetClass; + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +GType g_gdb_target_get_type(void); + +/* Crée une définition des détails d'interfaçage GDB. */ +GGdbTarget *g_gdb_target_new(GGdbStream *); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +char **g_gdb_target_get_register_names(const GGdbTarget *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +unsigned int g_gdb_target_get_register_size(const GGdbTarget *, const char *); + +/* Effectue la lecture d'un registre donné. */ +bool g_gdb_target_read_register(GGdbTarget *, GGdbStream *, SourceEndian, const char *, size_t, ...); + +/* Effectue l'écriture d'un registre donné. */ +bool g_gdb_target_write_register(GGdbTarget *, GGdbStream *, SourceEndian, const char *, size_t, ...); + + + +#endif  /* _DEBUG_GDBRSP_TARGET_H */ diff --git a/src/debug/gdbrsp/tcp.c b/src/debug/gdbrsp/tcp.c new file mode 100644 index 0000000..d6a1ef8 --- /dev/null +++ b/src/debug/gdbrsp/tcp.c @@ -0,0 +1,280 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tcp.c - gestion des connexions TCP aux serveurs GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#include "tcp.h" + + +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/types.h> + + +#include "stream-int.h" + + +#include "../../common/net.h" + + +/* Flux de communication TCP avec un serveur GDB (instance) */ +struct _GGdbTcpClient +{ +    GGdbStream parent;                      /* A laisser en premier        */ + +}; + + +/* Flux de communication TCP avec un serveur GDB (classe) */ +struct _GGdbTcpClientClass +{ +    GGdbStreamClass parent;                 /* A laisser en premier        */ + +}; + + +/* Initialise la classe des flux de communication TCP avec GDB. */ +static void g_gdb_tcp_client_class_init(GGdbTcpClientClass *); + +/* Initialise une instance de flux de communication avec GDB. */ +static void g_gdb_tcp_client_init(GGdbTcpClient *); + +/* Ouvre une connexion TCP à un serveur GDB. */ +//static int connect_via_tcp(const char *, const char *); + +/* Envoie des données à un serveur GDB. */ +static bool g_gdb_tcp_client_send_data(GGdbTcpClient *, const char *, size_t); + +/* Réceptionne un octet de donnée d'un serveur GDB. */ +static bool g_gdb_tcp_client_recv_byte(GGdbTcpClient *, char *); + + + +/* Indique le type défini pour un flux de communication TCP avec un serveur GDB. */ +G_DEFINE_TYPE(GGdbTcpClient, g_gdb_tcp_client, G_TYPE_GDB_STREAM); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe à initialiser.                                * +*                                                                             * +*  Description : Initialise la classe des flux de communication TCP avec GDB. * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_tcp_client_class_init(GGdbTcpClientClass *klass) +{ + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : client = instance à initialiser.                             * +*                                                                             * +*  Description : Initialise une instance de flux de communication avec GDB.   * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_tcp_client_init(GGdbTcpClient *client) +{ +    GGdbStream *stream;                     /* Version parente             */ + +    stream = G_GDB_STREAM(client); + +    stream->send_data = (send_gdb_data_fc)g_gdb_tcp_client_send_data; +    stream->recv_byte = (recv_gdb_byte_fc)g_gdb_tcp_client_recv_byte; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : server = nom ou adresse du serveur à contacter.              * +*                port   = port de connexion.                                  * +*                                                                             * +*  Description : Ouvre une connexion TCP à un serveur GDB.                    * +*                                                                             * +*  Retour      : Flux ouvert en lecture/écriture ou -1 en cas d'échec.        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ +#if 0 +static int connect_via_tcp(const char *server, const char *port) +{ +    int result;                             /* Bilan à retourner           */ +    struct addrinfo hints;                  /* Type de connexion souhaitée */ +    struct addrinfo *infos;                 /* Informations disponibles    */ +    int ret;                                /* Bilan d'un appel            */ +    struct addrinfo *iter;                  /* Boucle de parcours          */ +    struct sockaddr_in addr;                /* Infos de connexion distante */ + +    memset(&hints, 0, sizeof(struct addrinfo)); + +    hints.ai_family = AF_UNSPEC;        /* IPv4 ou IPv6 */ +    hints.ai_socktype = SOCK_STREAM; +    hints.ai_flags = 0; +    hints.ai_protocol = 0;              /* N'importe quel protocole */ + +    ret = getaddrinfo(server, port, &hints, &infos); +    if (ret != 0) +    { +        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); +        return -1; +    } + +    for (iter = infos; iter != NULL; iter = iter->ai_next) +    { +        result = socket(iter->ai_family, iter->ai_socktype, iter->ai_protocol); +        if (result == -1) continue; + +        ret = connect(result, iter->ai_addr, iter->ai_addrlen); +        if (ret == 0) break; + +        perror("connect"); +        close(result); + +    } + +    freeaddrinfo(infos); + +    if (iter == NULL) return -1; + +    ret = getpeername(result, (struct sockaddr *)&addr, (socklen_t []){ sizeof(struct sockaddr_in) }); +    if (ret == -1) +    { +        perror("getpeername"); +        close(result); +        return -1; +    } + +    printf("Connecté à %s:%hd\n", server, ntohs(addr.sin_port)); + +    return result; + +} +#endif + +/****************************************************************************** +*                                                                             * +*  Paramètres  : server = nom ou adresse du serveur à contacter.              * +*                port   = port de connexion.                                  * +*                owner  = débogueur tributaire du canal de communication.     * +*                                                                             * +*  Description : Crée une nouvelle connexion TCP à un serveur GDB.            * +*                                                                             * +*  Retour      : Adresse de la structure mise en place.                       * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbStream *g_gdb_tcp_client_new(const char *server, const char *port, GGdbDebugger *owner) +{ +    GGdbTcpClient *result;                  /* Structure à retourner       */ +    int sock;                               /* Flux ouvert à construire    */ + +    sock = connect_via_tcp(server, port, NULL); +    if (sock == -1) return NULL; + +    result = g_object_new(G_TYPE_GDB_TCP_CLIENT, NULL); + +    G_GDB_STREAM(result)->fd = sock; + +    G_GDB_STREAM(result)->owner = owner; +    g_object_ref(G_OBJECT(owner)); + +    if (!g_gdb_stream_listen(G_GDB_STREAM(result))) +        goto ggtcn_error; + +    return G_GDB_STREAM(result); + + ggtcn_error: + +    return NULL; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : client = flux ouvert en écriture à utiliser.                 * +*                data   = données à envoyer.                                  * +*                len    = quantité de ces données.                            * +*                                                                             * +*  Description : Envoie des données à un serveur GDB.                         * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_tcp_client_send_data(GGdbTcpClient *client, const char *data, size_t len) +{ +    ssize_t sent;                           /* Quantité de données envoyée */ + +    sent = send(G_GDB_STREAM(client)->fd, data, len, 0); + +    //printf("  sent '%s'\n", data); +    //printf("  sent ? %d vs %d\n", (int)sent, (int)len); + +    return (sent == len); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : client = flux ouvert en lecture à utiliser.                  * +*                data   = donnée à recevoir.                                  * +*                                                                             * +*  Description : Réceptionne un octet de donnée d'un serveur GDB.             * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_tcp_client_recv_byte(GGdbTcpClient *client, char *data) +{ +    ssize_t got;                            /* Quantité de données reçue   */ + +    got = recv(G_GDB_STREAM(client)->fd, data, 1, 0); + +    //printf("  got ? %d vs %d -> %c (0x%02hhx\n", (int)got, (int)1, *data, *data); + +    return (got == 1); + +} diff --git a/src/debug/gdbrsp/tcp.h b/src/debug/gdbrsp/tcp.h new file mode 100644 index 0000000..18d6c33 --- /dev/null +++ b/src/debug/gdbrsp/tcp.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tcp.h - prototypes pour la gestion des connexions TCP aux serveurs GDB. + * + * Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_TCP_H +#define _DEBUG_GDBRSP_TCP_H + + +#include "gdb.h" +#include "stream.h" + + + +#define G_TYPE_GDB_TCP_CLIENT               g_gdb_tcp_client_get_type() +#define G_GDB_TCP_CLIENT(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_gdb_tcp_client_get_type(), GGdbTcpClient)) +#define G_IS_GDB_TCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_gdb_tcp_client_get_type())) +#define G_GDB_TCP_CLIENT_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_TCP_CLIENT, GGdbTcpClientClass)) +#define G_IS_GDB_TCP_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_TCP_CLIENT)) +#define G_GDB_TCP_CLIENT_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_TCP_CLIENT, GGdbTcpClientClass)) + + +/* Flux de communication TCP avec un serveur GDB (instance) */ +typedef struct _GGdbTcpClient GGdbTcpClient; + +/* Flux de communication TCP avec un serveur GDB (classe) */ +typedef struct _GGdbTcpClientClass GGdbTcpClientClass; + + + +/* Indique le type défini pour un flux de communication TCP avec un serveur GDB. */ +GType g_gdb_tcp_client_get_type(void); + +/* Crée une nouvelle connexion TCP à un serveur GDB. */ +GGdbStream *g_gdb_tcp_client_new(const char *, const char *, GGdbDebugger *); + + + +#endif  /* _DEBUG_GDBRSP_TCP_H */ diff --git a/src/debug/gdbrsp/utils.c b/src/debug/gdbrsp/utils.c new file mode 100644 index 0000000..8c4cb8a --- /dev/null +++ b/src/debug/gdbrsp/utils.c @@ -0,0 +1,351 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * utils.h - fonctions qui simplifient la vie dans les interactions avec un serveur GDB + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#include "utils.h" + + +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdbool.h> +#include <sys/param.h> +#include <sys/types.h> + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : data = données à inspecter.                                  * +*                len  = quantité de ces données.                              * +*                                                                             * +*  Description : Indique si les données correspondent à un code d'erreur.     * +*                                                                             * +*  Retour      : Bilan de l'analyse.                                          * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool is_error_code(const char *data, size_t len) +{ +    bool result;                            /* Bilan à retourner           */ + +    result = (len == 3); + +    if (result) +        result = (data[0] == 'E' && isdigit(data[1]) && isdigit(data[2])); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : data = données à analyser.                                   * +*                size = taille de ces données.                                * +*                byte = statut de sortie d'un programme. [OUT]                * +*                                                                             * +*  Description : Relit une valeur sur 8 bits et deux lettres.                 * +*                                                                             * +*  Retour      : Bilan de la lecture.                                         * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool read_fixed_byte(const char *data, size_t len, uint8_t *byte) +{ +    bool result;                            /* Bilan à retourner           */ +    const char *iter;                       /* Boucle de parcours #1       */ +    size_t i;                               /* Boucle de parcours #2       */ +    uint8_t nibble;                         /* Valeur affichée             */ + +    result = true; + +    len = MIN(2, len); + +    for (i = 0, iter = data; i < len && result; i++, iter++) +    { +        switch (*iter) +        { +            case '0' ... '9': +                nibble = *iter - '0'; +                break; + +            case 'a' ... 'f': +                nibble = *iter - 'a' + 10; +                break; + +            case 'A' ... 'F': +                nibble = *iter - 'A' + 10; +                break; + +            default: +                result = false; +                break; + +        } + +        if (i == 0) +            *byte = (nibble << 4); +        else +            *byte |= nibble; + +    } + +    if (result) +        result = (i == 2); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : hex   = tampon d'origine assez grand.                        * +*                size  = taille de la valeur à considérer pour les travaux.   * +*                value = valeur sur XX bits à transcrire. [OUT]               * +*                                                                             * +*  Description : Traduit en valeur sur XX bits une forme textuelle.           * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool hex_to_any_u(const char *hex, size_t size, ...) +{ +    bool result;                            /* Bilan à retourner           */ +    va_list ap;                             /* Gestion de l'inconnue       */ +    uint8_t *value8;                        /* Valeur sur 8 bits           */ +    uint16_t *value16;                      /* Valeur sur 16 bits          */ +    uint32_t *value32;                      /* Valeur sur 32 bits          */ +    uint64_t *value64;                      /* Valeur sur 64 bits          */ +    uint8_t *iter;                          /* Boucle de parcours #1       */ +    size_t i;                               /* Boucle de parcours #2       */ +    char nibble;                            /* Valeur à afficher           */ + +    result = false; + +    /* Récupération de la destination */ + +    va_start(ap, size); + +    switch (size) +    { +        case 1: +            value8 = va_arg(ap, uint8_t *); +            iter = value8; +            break; + +        case 2: +            value16 = va_arg(ap, uint16_t *); +            iter = (uint8_t *)value16; +            break; + +        case 4: +            value32 = va_arg(ap, uint32_t *); +            iter = (uint8_t *)value32; +            break; + +        case 8: +            value64 = va_arg(ap, uint64_t *); +            iter = (uint8_t *)value64; +            break; + +        default: +            goto done; +            break; + +    } + +    /* Lecture de la valeur */ + +    for (i = 0; i < size; i++, iter++) +    { +        *iter = 0; + +        nibble = hex[i * 2]; + +        switch (nibble) +        { +            case '0' ... '9': +                *iter = (nibble - '0') << 4; +                break; + +            case 'a' ... 'f': +                *iter = (nibble - 'a' + 0xa) << 4; +                break; + +            case 'A' ... 'F': +                *iter = (nibble - 'A' + 0xa) << 4; +                break; + +            default: +                goto done; +                break; + +        } + +        nibble = hex[i * 2 + 1]; + +        switch (nibble) +        { +            case '0' ... '9': +                *iter |= (nibble - '0'); +                break; + +            case 'a' ... 'f': +                *iter |= (nibble - 'a' + 0xa); +                break; + +            case 'A' ... 'F': +                *iter |= (nibble - 'A' + 0xa); +                break; + +            default: +                goto done; +                break; + +        } + +    } + +    result = (i == size); + + done: + +    va_end(ap); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : size  = taille de la valeur à considérer pour les travaux.   * +*                hex   = tampon de destination assez grand.                   * +*                value = valeur sur XX bits à transcrire. [OUT]               * +*                                                                             * +*  Description : Traduit une valeur sur XX bits en forme textuelle.           * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool any_u_to_hex(size_t size, char hex[17], ...) +{ +    bool result;                            /* Bilan à retourner           */ +    va_list ap;                             /* Gestion de l'inconnue       */ +    uint8_t *value8;                        /* Valeur sur 8 bits           */ +    uint16_t *value16;                      /* Valeur sur 16 bits          */ +    uint32_t *value32;                      /* Valeur sur 32 bits          */ +    uint64_t *value64;                      /* Valeur sur 64 bits          */ +    size_t i;                               /* Boucle de parcours #1       */ +    const uint8_t *iter;                    /* Boucle de parcours #2       */ +    uint8_t nibble;                         /* Valeur à retenir            */ + +    result = true; + +    /* Récupération de la destination */ + +    va_start(ap, hex); + +    switch (size) +    { +        case 1: +            value8 = va_arg(ap, uint8_t *); +            iter = (const uint8_t *)value8; +            break; + +        case 2: +            value16 = va_arg(ap, uint16_t *); +            iter = (const uint8_t *)value16; +            break; + +        case 4: +            value32 = va_arg(ap, uint32_t *); +            iter = (const uint8_t *)value32; +            break; + +        case 8: +            value64 = va_arg(ap, uint64_t *); +            iter = (const uint8_t *)value64; +            break; + +        default: +            result = false; +            goto done; +            break; + +    } + +    /* Lecture de la valeur */ + +    for (i = 0; i < size; i++, iter++) +    { +        nibble = (*iter & 0xf0) >> 4; + +        switch (nibble) +        { +            case 0x0 ... 0x9: +                hex[i * 2] = '0' + nibble; +                break; + +            case 0xa ... 0xf: +                hex[i * 2] = 'A' + nibble - 0xa; +                break; + +        } + +        nibble = (*iter & 0x0f); + +        switch (nibble) +        { +            case 0x0 ... 0x9: +                hex[i * 2 + 1] = '0' + nibble; +                break; + +            case 0xa ... 0xf: +                hex[i * 2 + 1] = 'A' + nibble - 0xa; +                break; + +        } + +    } + +    hex[size * 2] = '\0'; + + done: + +    va_end(ap); + +    return result; + +} diff --git a/src/debug/gdbrsp/utils.h b/src/debug/gdbrsp/utils.h new file mode 100644 index 0000000..a67df46 --- /dev/null +++ b/src/debug/gdbrsp/utils.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * utils.h - prototypes pour les fonctions qui simplifient la vie dans les interactions avec un serveur GDB + * + * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_UTILS_H +#define _DEBUG_GDBRSP_UTILS_H + + +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> + + + +/* Indique si les données correspondent à un code d'erreur. */ +bool is_error_code(const char *, size_t); + +/* Relit une valeur sur 8 bits et deux lettres. */ +bool read_fixed_byte(const char *, size_t, uint8_t *); + +/* Traduit en valeur sur XX bits une forme textuelle. */ +bool hex_to_any_u(const char *, size_t, ...); + +#define hex_to_u8(h, v)  hex_to_any_u(h, 1, v) +#define hex_to_u16(h, v) hex_to_any_u(h, 2, v) +#define hex_to_u32(h, v) hex_to_any_u(h, 4, v) +#define hex_to_u64(h, v) hex_to_any_u(h, 8, v) + +/* Traduit une valeur sur XX bits en forme textuelle. */ +bool any_u_to_hex(size_t, char [17], ...); + +#define u8_to_hex(v, h)  any_u_to_hex(1, h, v) +#define u16_to_hex(v, h) any_u_to_hex(2, h, v) +#define u32_to_hex(v, h) any_u_to_hex(4, h, v) +#define u64_to_hex(v, h) any_u_to_hex(8, h, v) + + + +#endif  /* _DEBUG_GDBRSP_UTILS_H */ | 
