From 94fd405bb0c2e6dfa43324b04a336ffb611c58ce Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Sun, 19 Mar 2017 14:02:54 +0100 Subject: Provided initial features for debugging using GDB. --- ChangeLog | 124 +++ configure.ac | 9 +- plugins/pychrysa/Makefile.am | 4 +- plugins/pychrysa/arch/vmpa.c | 62 ++ plugins/pychrysa/arch/vmpa.h | 3 + plugins/pychrysa/debug/Makefile.am | 4 + plugins/pychrysa/debug/debugger.c | 1111 +++++++++++++++++++-- plugins/pychrysa/debug/debugger.h | 17 +- plugins/pychrysa/debug/gdbrsp/Makefile.am | 15 + plugins/pychrysa/debug/gdbrsp/gdb.c | 165 +++ plugins/pychrysa/debug/gdbrsp/gdb.h | 42 + plugins/pychrysa/debug/gdbrsp/module.c | 86 ++ plugins/pychrysa/debug/gdbrsp/module.h | 39 + plugins/pychrysa/debug/module.c | 39 +- plugins/pychrysa/debug/module.h | 6 +- plugins/pychrysa/format/Makefile.am | 1 - plugins/pychrysa/format/elf/elf.c | 2 +- plugins/pychrysa/format/symbol.c | 9 +- plugins/pychrysa/pychrysa.c | 2 + src/Makefile.am | 1 - src/analysis/binary.c | 1 - src/arch/vmpa.c | 4 +- src/common/endianness.c | 220 ++++ src/common/endianness.h | 27 + src/common/extstr.c | 4 +- src/common/xml.c | 99 +- src/common/xml.h | 9 + src/debug/Makefile.am | 6 +- src/debug/break-int.h | 64 ++ src/debug/break.c | 489 ++++----- src/debug/break.h | 90 +- src/debug/debugger-int.h | 128 ++- src/debug/debugger.c | 1555 +++++++++++++++++++++++++++-- src/debug/debugger.h | 193 +++- src/debug/gdbrsp/Makefile.am | 24 + src/debug/gdbrsp/aops.h | 57 ++ src/debug/gdbrsp/gdb-int.h | 116 +++ src/debug/gdbrsp/gdb.c | 1524 ++++++++++++++++++++++++++++ src/debug/gdbrsp/gdb.h | 61 ++ src/debug/gdbrsp/helpers.c | 224 +++++ src/debug/gdbrsp/helpers.h | 62 ++ src/debug/gdbrsp/helpers_arm.c | 252 +++++ src/debug/gdbrsp/helpers_arm.h | 37 + src/debug/gdbrsp/helpers_arm64.c | 97 ++ src/debug/gdbrsp/helpers_arm64.h | 40 + src/debug/gdbrsp/packet.c | 389 ++++++++ src/debug/gdbrsp/packet.h | 82 ++ src/debug/gdbrsp/stream-int.h | 89 ++ src/debug/gdbrsp/stream.c | 696 +++++++++++++ src/debug/gdbrsp/stream.h | 68 ++ src/debug/gdbrsp/support.c | 598 +++++++++++ src/debug/gdbrsp/support.h | 73 ++ src/debug/gdbrsp/target.c | 950 ++++++++++++++++++ src/debug/gdbrsp/target.h | 72 ++ src/debug/gdbrsp/tcp.c | 280 ++++++ src/debug/gdbrsp/tcp.h | 57 ++ src/debug/gdbrsp/utils.c | 351 +++++++ src/debug/gdbrsp/utils.h | 58 ++ src/debug/misc.h | 37 + src/debug/remgdb/Makefile.am | 17 - src/debug/remgdb/gdb.c | 363 ------- src/debug/remgdb/gdb.h | 61 -- src/debug/remgdb/helpers.c | 139 --- src/debug/remgdb/helpers.h | 61 -- src/debug/remgdb/packet.c | 383 ------- src/debug/remgdb/packet.h | 82 -- src/debug/remgdb/stream-int.h | 74 -- src/debug/remgdb/stream.c | 402 -------- src/debug/remgdb/stream.h | 65 -- src/debug/remgdb/tcp.c | 263 ----- src/debug/remgdb/tcp.h | 56 -- src/gtkext/gtkstatusstack.c | 10 + 72 files changed, 10391 insertions(+), 2509 deletions(-) create mode 100644 plugins/pychrysa/debug/gdbrsp/Makefile.am create mode 100644 plugins/pychrysa/debug/gdbrsp/gdb.c create mode 100644 plugins/pychrysa/debug/gdbrsp/gdb.h create mode 100644 plugins/pychrysa/debug/gdbrsp/module.c create mode 100644 plugins/pychrysa/debug/gdbrsp/module.h create mode 100644 src/debug/break-int.h create mode 100644 src/debug/gdbrsp/Makefile.am create mode 100644 src/debug/gdbrsp/aops.h create mode 100644 src/debug/gdbrsp/gdb-int.h create mode 100644 src/debug/gdbrsp/gdb.c create mode 100644 src/debug/gdbrsp/gdb.h create mode 100644 src/debug/gdbrsp/helpers.c create mode 100644 src/debug/gdbrsp/helpers.h create mode 100644 src/debug/gdbrsp/helpers_arm.c create mode 100644 src/debug/gdbrsp/helpers_arm.h create mode 100644 src/debug/gdbrsp/helpers_arm64.c create mode 100644 src/debug/gdbrsp/helpers_arm64.h create mode 100644 src/debug/gdbrsp/packet.c create mode 100644 src/debug/gdbrsp/packet.h create mode 100644 src/debug/gdbrsp/stream-int.h create mode 100644 src/debug/gdbrsp/stream.c create mode 100644 src/debug/gdbrsp/stream.h create mode 100644 src/debug/gdbrsp/support.c create mode 100644 src/debug/gdbrsp/support.h create mode 100644 src/debug/gdbrsp/target.c create mode 100644 src/debug/gdbrsp/target.h create mode 100644 src/debug/gdbrsp/tcp.c create mode 100644 src/debug/gdbrsp/tcp.h create mode 100644 src/debug/gdbrsp/utils.c create mode 100644 src/debug/gdbrsp/utils.h create mode 100644 src/debug/misc.h delete mode 100644 src/debug/remgdb/Makefile.am delete mode 100644 src/debug/remgdb/gdb.c delete mode 100644 src/debug/remgdb/gdb.h delete mode 100644 src/debug/remgdb/helpers.c delete mode 100644 src/debug/remgdb/helpers.h delete mode 100644 src/debug/remgdb/packet.c delete mode 100644 src/debug/remgdb/packet.h delete mode 100644 src/debug/remgdb/stream-int.h delete mode 100644 src/debug/remgdb/stream.c delete mode 100644 src/debug/remgdb/stream.h delete mode 100644 src/debug/remgdb/tcp.c delete mode 100644 src/debug/remgdb/tcp.h diff --git a/ChangeLog b/ChangeLog index b309cc1..155f8e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,127 @@ +17-03-19 Cyrille Bagard + + * configure.ac: + Extend AC_CONFIG_COMMANDS. Add the Makefiles from the 'plugins/pychrysa/debug', + 'plugins/pychrysa/debug/gdbrsp' and 'src/debug/gdbrsp' directories. Remove + the Makefiles linked to the JDWP. + + * plugins/pychrysa/Makefile.am: + Add debug/libpychrysadebug.la to pychrysalide_la_LIBADD. Add debug to + SUBDIRS. + + * plugins/pychrysa/arch/vmpa.c: + * plugins/pychrysa/arch/vmpa.h: + Convert Python objects to vmpa structures. + + * plugins/pychrysa/debug/Makefile.am: + Add gdbrsp/libpychrysadebuggdbrsp.la to libpychrysadebug_la_LIBADD and gdbrsp + to SUBDIRS. + + * plugins/pychrysa/debug/debugger.c: + * plugins/pychrysa/debug/debugger.h: + Define Python bindings for debugging. + + * plugins/pychrysa/debug/gdbrsp/Makefile.am: + * plugins/pychrysa/debug/gdbrsp/gdb.c: + * plugins/pychrysa/debug/gdbrsp/gdb.h: + * plugins/pychrysa/debug/gdbrsp/module.c: + * plugins/pychrysa/debug/gdbrsp/module.h: + New entries: implement bindings for the GDB remote serial protocol. + + * plugins/pychrysa/debug/module.c: + * plugins/pychrysa/debug/module.h: + Update code. + + * plugins/pychrysa/format/Makefile.am: + * plugins/pychrysa/format/elf/elf.c: + Typo. + + * plugins/pychrysa/format/symbol.c: + Handle symbols without label. + + * plugins/pychrysa/pychrysa.c: + Handle the new debug module. + + * src/Makefile.am: + Fix libchrysadisass_la_LIBADD. + + * src/analysis/binary.c: + * src/arch/vmpa.c: + Typo. + + * src/common/endianness.c: + * src/common/endianness.h: + Deal with endianness in memory. + + * src/common/extstr.c: + Fix another bug in strrpl(). + + * src/common/xml.c: + * src/common/xml.h: + Load XML data from memory and get the name of nodes. + + * src/debug/Makefile.am: + Add the 'break-int.h' and 'misc.h' files to libdebug_la_SOURCES. Remove + jdwp/libdebugjdwp.la from libdebug_la_LIBADD and add gdbrsp/libdebuggdbrsp.la. + + * src/debug/break-int.h: + New entry: define raw breakpoints properties. + + * src/debug/break.c: + * src/debug/break.h: + Update raw breakpoints properties. + + * src/debug/debugger-int.h: + * src/debug/debugger.c: + * src/debug/debugger.h: + Provide initial features for debugging using GDB. + + * src/debug/gdbrsp/Makefile.am: + * src/debug/gdbrsp/aops.h: + * src/debug/gdbrsp/gdb-int.h: + * src/debug/gdbrsp/gdb.c: + * src/debug/gdbrsp/gdb.h: + * src/debug/gdbrsp/helpers.c: + * src/debug/gdbrsp/helpers.h: + * src/debug/gdbrsp/helpers_arm.c: + * src/debug/gdbrsp/helpers_arm.h: + * src/debug/gdbrsp/helpers_arm64.c: + * src/debug/gdbrsp/helpers_arm64.h: + * src/debug/gdbrsp/packet.c: + * src/debug/gdbrsp/packet.h: + * src/debug/gdbrsp/stream-int.h: + * src/debug/gdbrsp/stream.c: + * src/debug/gdbrsp/stream.h: + * src/debug/gdbrsp/support.c: + * src/debug/gdbrsp/support.h: + * src/debug/gdbrsp/target.c: + * src/debug/gdbrsp/target.h: + * src/debug/gdbrsp/tcp.c: + * src/debug/gdbrsp/tcp.h: + * src/debug/gdbrsp/utils.c: + * src/debug/gdbrsp/utils.h: + New entries: provide initial features for debugging using GDB. + + * src/debug/misc.h: + Provide initial features for debugging using GDB. + + * src/debug/remgdb/Makefile.am: + * src/debug/remgdb/gdb.c: + * src/debug/remgdb/gdb.h: + * src/debug/remgdb/helpers.c: + * src/debug/remgdb/helpers.h: + * src/debug/remgdb/packet.c: + * src/debug/remgdb/packet.h: + * src/debug/remgdb/stream-int.h: + * src/debug/remgdb/stream.c: + * src/debug/remgdb/stream.h: + * src/debug/remgdb/tcp.c: + * src/debug/remgdb/tcp.h: + Replaced entries. + + * src/gtkext/gtkstatusstack.c: + Allow the status bar to be used from Python bindings. + 17-03-15 Cyrille Bagard * src/analysis/disass/area.c: diff --git a/configure.ac b/configure.ac index b646361..60cf9c5 100644 --- a/configure.ac +++ b/configure.ac @@ -309,7 +309,7 @@ AC_SUBST(LIBPYGOBJECT_LIBS) AC_CONFIG_FILES([stamp-h po/Makefile.in], [echo timestamp > stamp-h]) -AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:UINT64,UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,ULONG,ULONG" > src/glibext/chrysamarshal.list]) +AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,UINT64\nVOID:BOOLEAN,ULONG,ULONG\nVOID:INT,INT" > src/glibext/chrysamarshal.list]) AC_CONFIG_FILES([Makefile pixmaps/Makefile @@ -327,6 +327,8 @@ AC_CONFIG_FILES([Makefile plugins/pychrysa/arch/arm/v7/Makefile plugins/pychrysa/common/Makefile plugins/pychrysa/core/Makefile + plugins/pychrysa/debug/Makefile + plugins/pychrysa/debug/gdbrsp/Makefile plugins/pychrysa/format/Makefile plugins/pychrysa/format/dex/Makefile plugins/pychrysa/format/elf/Makefile @@ -365,10 +367,7 @@ AC_CONFIG_FILES([Makefile src/common/Makefile src/core/Makefile src/debug/Makefile - src/debug/jdwp/Makefile - src/debug/jdwp/misc/Makefile - src/debug/jdwp/sets/Makefile - src/debug/remgdb/Makefile + src/debug/gdbrsp/Makefile src/format/Makefile src/format/dex/Makefile src/format/dwarf/Makefile diff --git a/plugins/pychrysa/Makefile.am b/plugins/pychrysa/Makefile.am index 7ab98fa..8bc5e0c 100644 --- a/plugins/pychrysa/Makefile.am +++ b/plugins/pychrysa/Makefile.am @@ -13,6 +13,7 @@ pychrysalide_la_LIBADD = \ arch/libpychrysaarch.la \ common/libpychrysacommon.la \ core/libpychrysacore.la \ + debug/libpychrysadebug.la \ format/libpychrysaformat.la \ glibext/libpychrysaglibext.la \ gtkext/libpychrysagtkext.la \ @@ -29,5 +30,4 @@ AM_CPPFLAGS = $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) $(LIBGTK_CFLAGS) $(LIBX AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) -SUBDIRS = analysis arch common core format glibext gtkext gui -#SUBDIRS = analysis arch common core debug format glibext gtkext gui +SUBDIRS = analysis arch common core debug format glibext gtkext gui diff --git a/plugins/pychrysa/arch/vmpa.c b/plugins/pychrysa/arch/vmpa.c index d747f8d..10acf35 100644 --- a/plugins/pychrysa/arch/vmpa.c +++ b/plugins/pychrysa/arch/vmpa.c @@ -729,6 +729,68 @@ PyObject *build_from_internal_vmpa(const vmpa2t *addr) } +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en adresse n'importe quoi. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_any_to_vmpa(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + int ret; /* Test intermédiaire */ + vmpa2t *src; /* Modèle de données à copier */ + PY_LONG_LONG value; /* Valeur de type générique */ + int overflow; /* Détection d'une grosse val. */ + + result = 0; + + /* Si l'objet est au bon format, rien à faire ! */ + + ret = PyObject_IsInstance(arg, (PyObject *)get_python_vmpa_type()); + + if (ret == 1) + { + src = get_internal_vmpa(arg); + copy_vmpa((vmpa2t *)dst, src); + + result = 1; + goto catv_done; + + } + + /* Sinon on demande à Python... */ + + value = PyLong_AsLongLongAndOverflow(arg, &overflow); + + if (value == -1 && (overflow == 1 || PyErr_Occurred())) + PyErr_Clear(); + + else + { + init_vmpa((vmpa2t *)dst, VMPA_NO_PHYSICAL, value); + + result = 1; + goto catv_done; + + } + + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to vmpa"); + + catv_done: + + return result; + +} + + /* ---------------------------------------------------------------------------------- */ /* DEFINITION D'UNE ZONE EN MEMOIRE */ diff --git a/plugins/pychrysa/arch/vmpa.h b/plugins/pychrysa/arch/vmpa.h index 793f24a..46828f5 100644 --- a/plugins/pychrysa/arch/vmpa.h +++ b/plugins/pychrysa/arch/vmpa.h @@ -46,6 +46,9 @@ vmpa2t *get_internal_vmpa(PyObject *); /* Convertit une structure de type 'vmpa2t' en objet Python. */ PyObject *build_from_internal_vmpa(const vmpa2t *); +/* Tente de convertir en adresse n'importe quoi. */ +int convert_any_to_vmpa(PyObject *, void *); + /* ------------------------ DEFINITION D'UNE ZONE EN MEMOIRE ------------------------ */ diff --git a/plugins/pychrysa/debug/Makefile.am b/plugins/pychrysa/debug/Makefile.am index 503d228..8011c49 100644 --- a/plugins/pychrysa/debug/Makefile.am +++ b/plugins/pychrysa/debug/Makefile.am @@ -5,6 +5,8 @@ libpychrysadebug_la_SOURCES = \ debugger.h debugger.c \ module.h module.c +libpychrysadebug_la_LIBADD = \ + gdbrsp/libpychrysadebuggdbrsp.la libpychrysadebug_la_LDFLAGS = @@ -13,3 +15,5 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJE -I../../../src AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) + +SUBDIRS = gdbrsp diff --git a/plugins/pychrysa/debug/debugger.c b/plugins/pychrysa/debug/debugger.c index fb55f65..6f930a4 100644 --- a/plugins/pychrysa/debug/debugger.c +++ b/plugins/pychrysa/debug/debugger.c @@ -25,85 +25,741 @@ #include "debugger.h" -#if 0 +#include #include #include -#include "../quirks.h" +#include +#include "../helpers.h" +#include "../arch/vmpa.h" -/* Crée un nouvel objet Python de type 'BinaryDebugger'. */ -static PyObject *py_binary_debugger_new(PyTypeObject *, PyObject *, PyObject *); -/* Fournit les identifiants de tous les threads actifs. */ + + + +/* Fournit les identifiants de tous les threads actifs. */ static PyObject *py_binary_debugger_list_all_threads(PyObject *, PyObject *); -/* Fournit la pile d'exécution courante via un débogueur. */ -static PyObject *py_binary_debugger_get_frames_stack(PyObject *, PyObject *); +/* Lit une valeur de 8 bits à une adresse arbitraire. */ +static PyObject *py_binary_debugger_read_memory_u8(PyObject *, PyObject *); + +/* Lit une valeur de 16 bits à une adresse arbitraire. */ +static PyObject *py_binary_debugger_read_memory_u16(PyObject *, PyObject *); + +/* Lit une valeur de 32 bits à une adresse arbitraire. */ +static PyObject *py_binary_debugger_read_memory_u32(PyObject *, PyObject *); + +/* Lit une valeur de 64 bits à une adresse arbitraire. */ +static PyObject *py_binary_debugger_read_memory_u64(PyObject *, PyObject *); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +static PyObject *py_binary_debugger_get_register_names(PyObject *, PyObject *); + +/* Indique la taille associée à un registre donné. */ +static PyObject *py_binary_debugger_get_register_size(PyObject *, PyObject *); + +/* Lit une valeur de 8 bits à partir d'un registre. */ +static PyObject *py_binary_debugger_read_register_u8(PyObject *, PyObject *); + +/* Lit une valeur de 16 bits à partir d'un registre. */ +static PyObject *py_binary_debugger_read_register_u16(PyObject *, PyObject *); + +/* Lit une valeur de 32 bits à partir d'un registre. */ +static PyObject *py_binary_debugger_read_register_u32(PyObject *, PyObject *); + +/* Lit une valeur de 64 bits à partir d'un registre. */ +static PyObject *py_binary_debugger_read_register_u64(PyObject *, PyObject *); + +/* Ecrit une valeur de 8 bits dans un registre. */ +static PyObject *py_binary_debugger_write_register_u8(PyObject *, PyObject *); + +/* Ecrit une valeur de 16 bits dans un registre. */ +static PyObject *py_binary_debugger_write_register_u16(PyObject *, PyObject *); + +/* Ecrit une valeur de 32 bits dans un registre. */ +static PyObject *py_binary_debugger_write_register_u32(PyObject *, PyObject *); + +/* Ecrit une valeur de 64 bits dans un registre. */ +static PyObject *py_binary_debugger_write_register_u64(PyObject *, PyObject *); + + + +/* Remonte la pile d'appels jusqu'au point courant. */ +static PyObject *py_binary_debugger_get_call_stack(PyObject *, PyObject *); + + + +/* Ajoute un point d'arrêt basique en mémoire. */ +static PyObject *py_binary_debugger_add_mem_bp(PyObject *, PyObject *); + +/* Retire un point d'arrêt basique en mémoire. */ +static PyObject *py_binary_debugger_delete_mem_bp(PyObject *, PyObject *); + + + +/* Redémarre le processus de débogage. */ +static PyObject *py_binary_debugger_restart(PyObject *, PyObject *); + +/* Remet en marche le débogueur courant. */ +static PyObject *py_binary_debugger_resume(PyObject *, PyObject *); + +/* Relance l'exécution pour une seule instruction. */ +static PyObject *py_binary_debugger_stepi(PyObject *, PyObject *); + + + + + + + + + + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments non utilisés ici. * +* * +* Description : Fournit les identifiants de tous les threads actifs. * +* * +* Retour : Liste contenant identifiants et désignations de threads. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_list_all_threads(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + size_t count; /* Quantité de threads actifs */ + dbg_thread_desc *threads; /* Liste des threads actifs */ + size_t i; /* Boucle de parcours */ + PyObject *thread; /* Détails sur un thread donné */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + threads = g_binary_debugger_list_all_threads(debugger, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + thread = PyTuple_New(2); + PyTuple_SetItem(result, i, thread); + + PyTuple_SetItem(thread, 0, PyLong_FromLong(threads[i].id)); + PyTuple_SetItem(thread, 1, PyUnicode_FromString(threads[i].name)); + + } + + delete_dbg_thread_desc(threads, count); + + return result; + +} + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 8 bits à une adresse arbitraire. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_memory_u8(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + int ret; /* Bilan de lecture des args. */ + vmpa2t addr; /* Position interne associée */ + uint8_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; + + status = g_binary_debugger_read_memory_u8(debugger, get_virt_addr(&addr), &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 16 bits à une adresse arbitraire. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_memory_u16(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + int ret; /* Bilan de lecture des args. */ + vmpa2t addr; /* Position interne associée */ + uint16_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; + + status = g_binary_debugger_read_memory_u16(debugger, get_virt_addr(&addr), &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 32 bits à une adresse arbitraire. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_memory_u32(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + int ret; /* Bilan de lecture des args. */ + vmpa2t addr; /* Position interne associée */ + uint32_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; + + status = g_binary_debugger_read_memory_u32(debugger, get_virt_addr(&addr), &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 64 bits à une adresse arbitraire. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_memory_u64(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + int ret; /* Bilan de lecture des args. */ + vmpa2t addr; /* Position interne associée */ + uint64_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; + + status = g_binary_debugger_read_memory_u64(debugger, get_virt_addr(&addr), &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance de débogueur à consulter. * +* args = arguments accompagnant l'appel. * +* * +* Description : Liste l'ensemble des registres appartenant à un groupe. * +* * +* Retour : Liste de noms à libérer de la mémoire après utilisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_get_register_names(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + const char *group; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + char **list; /* Liste de noms de registre */ + size_t count; /* Taille de cette liste */ + size_t i; /* Boucle de parcours */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "|s", &group); + if (!ret) return NULL; + + list = g_binary_debugger_get_register_names(debugger, group, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + PyTuple_SetItem(result, i, PyUnicode_FromString(list[i])); + free(list[i]); + } + + if (list != NULL) + free(list); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance de débogueur à consulter. * +* args = arguments accompagnant l'appel. * +* * +* 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 PyObject *py_binary_debugger_get_register_size(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + unsigned int size; /* Taille associée au registre */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "s", ®); + if (!ret) return NULL; + + size = g_binary_debugger_get_register_size(debugger, reg); + + result = PyLong_FromUnsignedLong(size); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 8 bits à partir d'un registre. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_register_u8(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint8_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "s", ®); + if (!ret) return NULL; + + status = g_binary_debugger_read_register_u8(debugger, reg, &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 16 bits à partir d'un registre. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_register_u16(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint16_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "s", ®); + if (!ret) return NULL; + + status = g_binary_debugger_read_register_u16(debugger, reg, &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 32 bits à partir d'un registre. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_register_u32(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint32_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "s", ®); + if (!ret) return NULL; + + status = g_binary_debugger_read_register_u32(debugger, reg, &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Lit une valeur de 64 bits à partir d'un registre. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_read_register_u64(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint64_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "s", ®); + if (!ret) return NULL; + + status = g_binary_debugger_read_register_u64(debugger, reg, &value); + + if (status) + result = PyLong_FromUnsignedLongLong(value); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Ecrit une valeur de 8 bits dans un registre. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_write_register_u8(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint8_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "sB", ®, &value); + if (!ret) return NULL; + + status = g_binary_debugger_write_register_u8(debugger, reg, &value); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} /****************************************************************************** * * -* Paramètres : type = type de l'objet à instancier. * -* args = arguments fournis à l'appel. * -* kwds = arguments de type key=val fournis. * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * * * -* Description : Crée un nouvel objet Python de type 'BinaryDebugger'. * +* Description : Ecrit une valeur de 16 bits dans un registre. * * * -* Retour : Instance Python mise en place. * +* Retour : Valeur lue ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_binary_debugger_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static PyObject *py_binary_debugger_write_register_u16(PyObject *self, PyObject *args) { PyObject *result; /* Instance à retourner */ - DebuggerType dtype; /* Type de débogueur à créer */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ int ret; /* Bilan de lecture des args. */ + uint16_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "sH", ®, &value); + if (!ret) return NULL; + + status = g_binary_debugger_write_register_u16(debugger, reg, &value); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Ecrit une valeur de 32 bits dans un registre. * +* * +* Retour : Valeur lue ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_write_register_u32(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint32_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); - ret = PyArg_ParseTuple(args, "l", &dtype); - if (!ret) Py_RETURN_NONE; + ret = PyArg_ParseTuple(args, "sI", ®, &value); + if (!ret) return NULL; - debugger = g_new_binary_debugger(dtype, NULL/* FIXME */); + status = g_binary_debugger_write_register_u32(debugger, reg, &value); - result = py_binary_debugger_from_c(debugger); - g_object_unref(debugger); + result = status ? Py_True : Py_False; + Py_INCREF(result); - return (PyObject *)result; + return result; } /****************************************************************************** * * -* Paramètres : debugger = instance existante GLib. * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * * * -* Description : Crée un nouvel objet Python de type 'BinaryDebugger'. * +* Description : Ecrit une valeur de 64 bits dans un registre. * * * -* Retour : Instance Python mise en place. * +* Retour : Valeur lue ou None. * * * * Remarques : - * * * ******************************************************************************/ -PyObject *py_binary_debugger_from_c(GBinaryDebugger *debugger) +static PyObject *py_binary_debugger_write_register_u64(PyObject *self, PyObject *args) { - PyObject *module; /* Module d'appartenance */ - PyTypeObject *type; /* Type Python correspondant */ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + char *reg; /* Nom du registre à manipuler */ + int ret; /* Bilan de lecture des args. */ + uint64_t value; /* Valeur lue en mémoire */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "sK", ®, &value); + if (!ret) return NULL; - module = PyImport_ImportModule("pychrysalide.debug"); - type = (PyTypeObject*)PyObject_GetAttrString(module, "BinaryDebugger"); - Py_DECREF(module); + status = g_binary_debugger_write_register_u64(debugger, reg, &value); - pychrysalide_set_instance_data(G_OBJECT(debugger), type); + result = status ? Py_True : Py_False; + Py_INCREF(result); - return pygobject_new(G_OBJECT(debugger)); + return result; } @@ -111,52 +767,96 @@ PyObject *py_binary_debugger_from_c(GBinaryDebugger *debugger) + + + /****************************************************************************** * * -* Paramètres : self = classe représentant un débogueur. * -* args = arguments fournis à l'appel. * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * * * -* Description : Fournit les identifiants de tous les threads actifs. * +* Description : Remonte la pile d'appels jusqu'au point courant. * * * -* Retour : Object Python représentant le résultat de l'opération. * +* Retour : Pile d'appels sous forme de liste ou None en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_binary_debugger_list_all_threads(PyObject *self, PyObject *args) +static PyObject *py_binary_debugger_get_call_stack(PyObject *self, PyObject *args) { - PyObject *result; /* Trouvailles à retourner */ - GBinaryDebugger *debugger; /* Version native */ - char **names; /* Noms associés aux threads */ - size_t count; /* Taille de cette liste */ - pid_t *threads; /* Liste des threads actifs */ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + virt_t *callstack; /* Pile d'appels obtenue */ + size_t size; /* Hauteur de cette pile */ + bool status; /* Bilan de l'opération */ size_t i; /* Boucle de parcours */ - PyObject *thread; /* Détails sur un thread donné */ debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); - threads = g_binary_debugger_list_all_threads(debugger, &names, &count); + status = g_binary_debugger_get_call_stack(debugger, &callstack, &size); - result = PyTuple_New(count); + if (!status) + { + result = Py_None; + Py_INCREF(result); + } - for (i = 0; i < count; i++) + else { - thread = PyTuple_New(2); - PyTuple_SetItem(result, i, thread); + result = PyTuple_New(size); - PyTuple_SetItem(thread, 0, PyLong_FromLong(threads[i])); - PyTuple_SetItem(thread, 1, PyString_FromString(names[i])); + for (i = 0; i < size; i++) + PyTuple_SetItem(result, i, PyLong_FromUnsignedLongLong(callstack[i])); - free(names[i]); + if (callstack != NULL) + free(callstack); } - if (names != NULL) - free(names); + return result; + +} + + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * +* * +* Description : Ajoute un point d'arrêt basique en mémoire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_add_mem_bp(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + vmpa2t addr; /* Position interne associée */ + int ret; /* Bilan de lecture des args. */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; + + status = g_binary_debugger_add_memory_breakpoint(debugger, get_virt_addr(&addr)); - if (threads != NULL) - free(threads); + result = status ? Py_True : Py_False; + Py_INCREF(result); return result; @@ -165,116 +865,321 @@ static PyObject *py_binary_debugger_list_all_threads(PyObject *self, PyObject *a /****************************************************************************** * * -* Paramètres : self = classe représentant un débogueur. * -* args = arguments fournis à l'appel. * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments accompagnant l'appel. * * * -* Description : Fournit la pile d'exécution courante via un débogueur. * +* Description : Retire un point d'arrêt basique en mémoire. * * * -* Retour : Object Python représentant le résultat de l'opération. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_binary_debugger_get_frames_stack(PyObject *self, PyObject *args) +static PyObject *py_binary_debugger_delete_mem_bp(PyObject *self, PyObject *args) { - PyObject *result; /* Trouvailles à retourner */ - GBinaryDebugger *debugger; /* Version native */ - unsigned long thread; /* Identifiant du thread visé */ - size_t count; /* Taille de cette liste */ - dbg_frame_t *frames; /* Frames courantes trouvées */ - size_t i; /* Boucle de parcours */ - PyObject *frame; /* Détails sur une frame */ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + vmpa2t addr; /* Position interne associée */ + int ret; /* Bilan de lecture des args. */ + bool status; /* Bilan de l'opération */ debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); - if (!PyArg_ParseTuple(args, "k", &thread)) - Py_RETURN_NONE; + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; - frames = g_binary_debugger_get_frames_stack(debugger, thread, &count); + status = g_binary_debugger_delete_memory_breakpoint(debugger, get_virt_addr(&addr)); - result = PyTuple_New(count); + result = status ? Py_True : Py_False; + Py_INCREF(result); - for (i = 0; i < count; i++) - { - frame = PyTuple_New(1); - PyTuple_SetItem(result, i, frame); + return result; + +} - PyTuple_SetItem(frame, 0, PyLong_FromUnsignedLongLong(frames[i].addr)); - } - if (frames != NULL) - free(frames); + + + + + +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments non utilisés ici. * +* * +* Description : Redémarre le processus de débogage. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_restart(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + status = g_binary_debugger_restart(debugger); + + result = status ? Py_True : Py_False; + Py_INCREF(result); return result; } +/****************************************************************************** +* * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments non utilisés ici. * +* * +* Description : Remet en marche le débogueur courant. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_binary_debugger_resume(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + status = g_binary_debugger_resume(debugger); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; +} /****************************************************************************** * * -* Paramètres : module = module dont la définition est à compléter. * +* Paramètres : self = contenu binaire à manipuler. * +* args = arguments non utilisés ici. * * * -* Description : Ajoute l'objet 'pychrysalide.debug.BinaryDebugger' au module.* +* Description : Relance l'exécution pour une seule instruction. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -bool register_python_binary_debugger(PyObject *module) +static PyObject *py_binary_debugger_stepi(PyObject *self, PyObject *args) { - PyObject *pygobj_mod; /* Module Python-GObject */ - int ret; /* Bilan d'un appel */ + PyObject *result; /* Instance à retourner */ + GBinaryDebugger *debugger; /* Version GLib du format */ + bool status; /* Bilan de l'opération */ + + debugger = G_BINARY_DEBUGGER(pygobject_get(self)); + assert(debugger != NULL); + + status = g_binary_debugger_stepi(debugger, false); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + + + + + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_binary_debugger_type(void) +{ static PyMethodDef py_binary_debugger_methods[] = { { - "list_all_threads", (PyCFunction)py_binary_debugger_list_all_threads, + "list_all_threads", py_binary_debugger_list_all_threads, + METH_NOARGS, + "list_all_threads($self, /)\n--\n\nList all current active threads." + }, + { + "read_mem_u8", py_binary_debugger_read_memory_u8, + METH_VARARGS, + "read_mem_u8($self, addr, /)\n--\n\nRead a 8-bit value from a given address." + }, + { + "read_mem_u16", py_binary_debugger_read_memory_u16, + METH_VARARGS, + "read_mem_u16($self, addr, /)\n--\n\nRead a 16-bit value from a given address." + }, + { + "read_mem_u32", py_binary_debugger_read_memory_u32, + METH_VARARGS, + "read_mem_u32($self, addr, /)\n--\n\nRead a 32-bit value from a given address." + }, + { + "read_mem_u64", py_binary_debugger_read_memory_u64, + METH_VARARGS, + "read_mem_u64($self, addr, /)\n--\n\nRead a 64-bit value from a given address." + }, + { + "get_reg_names", py_binary_debugger_get_register_names, + METH_VARARGS, + "get_reg_names($self, [grp]/)\n--\n\nGet the names of all registers belonging to an optional group." + }, + { + "get_reg_size", py_binary_debugger_get_register_size, + METH_VARARGS, + "get_reg_size($self, name, /)\n--\n\nGet the size of a given register." + }, + { + "read_reg_u8", py_binary_debugger_read_register_u8, + METH_VARARGS, + "read_reg_u8($self, reg, /)\n--\n\nRead a 8-bit value from a named register." + }, + { + "read_reg_u16", py_binary_debugger_read_register_u16, + METH_VARARGS, + "read_reg_u16($self, reg, /)\n--\n\nRead a 16-bit value from a named register." + }, + { + "read_reg_u32", py_binary_debugger_read_register_u32, + METH_VARARGS, + "read_reg_u32($self, reg, /)\n--\n\nRead a 32-bit value from a named register." + }, + { + "read_reg_u64", py_binary_debugger_read_register_u64, + METH_VARARGS, + "read_reg_u64($self, reg, /)\n--\n\nRead a 64-bit value from a named register." + }, + { + "write_reg_u8", py_binary_debugger_write_register_u8, + METH_VARARGS, + "write_reg_u8($self, reg, val, /)\n--\n\nWrite a 8-bit value into a named register." + }, + { + "write_reg_u16", py_binary_debugger_write_register_u16, + METH_VARARGS, + "write_reg_u16($self, reg, val, /)\n--\n\nWrite a 16-bit value into a named register." + }, + { + "write_reg_u32", py_binary_debugger_write_register_u32, + METH_VARARGS, + "write_reg_u32($self, reg, val, /)\n--\n\nWrite a 32-bit value into a named register." + }, + { + "write_reg_u64", py_binary_debugger_write_register_u64, + METH_VARARGS, + "write_reg_u64($self, reg, val, /)\n--\n\nWrite a 64-bit value into a named register." + }, + { + "get_call_stack", py_binary_debugger_get_call_stack, METH_NOARGS, - "List all current active threads." + "get_call_stack($self, /)\n--\n\nGet the current call stack." + }, + { + "add_mem_bp", py_binary_debugger_add_mem_bp, + METH_VARARGS, + "add_mem_bp($self, addr, /)\n--\n\nInsert a memory breakpoint at a given address." }, { - "get_frames_stack", (PyCFunction)py_binary_debugger_get_frames_stack, + "delete_mem_bp", py_binary_debugger_delete_mem_bp, METH_VARARGS, - "Provide the current callstack using a debugger." + "delete_mem_bp($self, addr, /)\n--\n\nRemove a memory breakpoint at a given address." + }, + { + "restart", py_binary_debugger_restart, + METH_NOARGS, + "restart($self, /)\n--\n\nRestart the current debugging session." + }, + { + "resume", py_binary_debugger_resume, + METH_NOARGS, + "resume($self, /)\n--\n\nResume the current debugging session." }, + { + "stepi", py_binary_debugger_stepi, + METH_NOARGS, + "stepi($self, /)\n--\n\nExecute one machine instruction, then stop and return to the debugger." + }, + { NULL } + }; + + static PyGetSetDef py_binary_debugger_getseters[] = { { NULL } }; static PyTypeObject py_binary_debugger_type = { - PyObject_HEAD_INIT(NULL) + PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.debug.BinaryDebugger", - .tp_basicsize = sizeof(PyGObject), + .tp_name = "pychrysalide.analysis.BinaryDebugger", .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = "PyChrysalide binary debugger", + .tp_doc = "PyChrysalide loaded binary", .tp_methods = py_binary_debugger_methods, - .tp_new = (newfunc)py_binary_debugger_new + .tp_getset = py_binary_debugger_getseters }; - pygobj_mod = PyImport_ImportModule("gobject"); - if (pygobj_mod == NULL) return false; + return &py_binary_debugger_type; - py_binary_debugger_type.tp_base = (PyTypeObject *)PyObject_GetAttrString(pygobj_mod, "GObject"); - Py_DECREF(pygobj_mod); +} - if (PyType_Ready(&py_binary_debugger_type) < 0) - return false; - Py_INCREF(&py_binary_debugger_type); - ret = PyModule_AddObject(module, "BinaryDebugger", (PyObject *)&py_binary_debugger_type); +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.....BinaryDebugger'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool register_python_binary_debugger(PyObject *module) +{ + PyTypeObject *py_binary_debugger_type; /* Type Python 'BinaryDebugger'*/ + PyObject *dict; /* Dictionnaire du module */ + + py_binary_debugger_type = get_python_binary_debugger_type(); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_BINARY_DEBUGGER, py_binary_debugger_type, &PyGObject_Type)) + return false; - return (ret == 0); + return true; } -#endif diff --git a/plugins/pychrysa/debug/debugger.h b/plugins/pychrysa/debug/debugger.h index f2e4e46..97f91ac 100644 --- a/plugins/pychrysa/debug/debugger.h +++ b/plugins/pychrysa/debug/debugger.h @@ -22,24 +22,21 @@ */ -#ifndef _PLUGINS_PYOIDA_DEBUG_DEBUGGER_H -#define _PLUGINS_PYOIDA_DEBUG_DEBUGGER_H +#ifndef _PLUGINS_PYCHRYSALIDE_DEBUG_DEBUGGER_H +#define _PLUGINS_PYCHRYSALIDE_DEBUG_DEBUGGER_H #include #include -#if 0 -#include +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_binary_debugger_type(void); -/* Crée un nouvel objet Python de type 'BinaryDebugger'. */ -PyObject *py_binary_debugger_from_c(GBinaryDebugger *debugger); - -/* Ajoute l'objet 'pychrysalide.debug.BinaryDebugger' au module. */ +/* Prend en charge l'objet 'pychrysalide.debug.BinaryDebugger'. */ bool register_python_binary_debugger(PyObject *); -#endif -#endif /* _PLUGINS_PYOIDA_DEBUG_DEBUGGER_H */ + +#endif /* _PLUGINS_PYCHRYSALIDE_DEBUG_DEBUGGER_H */ diff --git a/plugins/pychrysa/debug/gdbrsp/Makefile.am b/plugins/pychrysa/debug/gdbrsp/Makefile.am new file mode 100644 index 0000000..cf7b78a --- /dev/null +++ b/plugins/pychrysa/debug/gdbrsp/Makefile.am @@ -0,0 +1,15 @@ + +noinst_LTLIBRARIES = libpychrysadebuggdbrsp.la + +libpychrysadebuggdbrsp_la_SOURCES = \ + gdb.h gdb.c \ + module.h module.c + + +libpychrysadebuggdbrsp_la_LDFLAGS = + + +AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I../../../../src + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) diff --git a/plugins/pychrysa/debug/gdbrsp/gdb.c b/plugins/pychrysa/debug/gdbrsp/gdb.c new file mode 100644 index 0000000..cbbf66b --- /dev/null +++ b/plugins/pychrysa/debug/gdbrsp/gdb.c @@ -0,0 +1,165 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.c - équivalent Python du fichier "debug/gdbrsp/gdb.c" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "gdb.h" + + +#include + + +#include + + +#include + + +#include "../debugger.h" +#include "../../helpers.h" +#include "../../analysis/binary.h" + + +/* Crée un nouvel objet Python de type 'GdbDebugger'. */ +static PyObject *py_gdb_debugger_new(PyTypeObject *, PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : type = type de l'objet à instancier. * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Crée un nouvel objet Python de type 'GdbDebugger'. * +* * +* Retour : Instance Python mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_gdb_debugger_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Instance à retourner */ + PyObject *binary_obj; /* Objet pour le binaire lié */ + const char *server; /* Nom du serveur à contacter */ + unsigned short port; /* Port de connexion */ + int ret; /* Bilan de lecture des args. */ + GLoadedBinary *binary; /* Binaire chargé en mémoire */ + GBinaryDebugger *debugger; /* Création GLib à transmettre */ + + ret = PyArg_ParseTuple(args, "OsH", &binary_obj, &server, &port); + if (!ret) return NULL; + + ret = PyObject_IsInstance(binary_obj, (PyObject *)get_python_loaded_binary_type()); + if (!ret) + { + PyErr_SetString(PyExc_TypeError, _("The first argument must be an instance of LoadedBinary.")); + return NULL; + } + + binary = G_LOADED_BINARY(pygobject_get(binary_obj)); + + debugger = g_gdb_debugger_new(binary, server, port); + + result = pygobject_new(G_OBJECT(debugger)); + + g_object_unref(debugger); + + return (PyObject *)result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_gdb_debugger_type(void) +{ + static PyMethodDef py_gdb_debugger_methods[] = { + { NULL } + }; + + static PyGetSetDef py_gdb_debugger_getseters[] = { + { NULL } + }; + + static PyTypeObject py_gdb_debugger_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.debug.gdbrsp.GdbDebugger", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = "PyChrysalide GDB debugger", + + .tp_methods = py_gdb_debugger_methods, + .tp_getset = py_gdb_debugger_getseters, + .tp_new = (newfunc)py_gdb_debugger_new + + }; + + return &py_gdb_debugger_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide....gdbrsp.GdbDebugger'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool register_python_gdb_debugger(PyObject *module) +{ + PyTypeObject *py_gdb_debugger_type; /* Type Python 'GdbDebugger' */ + PyObject *dict; /* Dictionnaire du module */ + + py_gdb_debugger_type = get_python_gdb_debugger_type(); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_GDB_DEBUGGER, + py_gdb_debugger_type, get_python_binary_debugger_type())) + return false; + + return true; + +} diff --git a/plugins/pychrysa/debug/gdbrsp/gdb.h b/plugins/pychrysa/debug/gdbrsp/gdb.h new file mode 100644 index 0000000..c3d1330 --- /dev/null +++ b/plugins/pychrysa/debug/gdbrsp/gdb.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.h - prototypes pour l'équivalent Python du fichier "debug/gdbrsp/gdb.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSA_DEBUG_GDBRSP_DEBUGGER_H +#define _PLUGINS_PYCHRYSA_DEBUG_GDBRSP_DEBUGGER_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_gdb_debugger_type(void); + +/* Prend en charge l'objet 'pychrysalide.debug.gdbrsp.GdbDebugger'. */ +bool register_python_gdb_debugger(PyObject *); + + + +#endif /* _PLUGINS_PYCHRYSA_DEBUG_GDBRSP_DEBUGGER_H */ diff --git a/plugins/pychrysa/debug/gdbrsp/module.c b/plugins/pychrysa/debug/gdbrsp/module.c new file mode 100644 index 0000000..6e7896c --- /dev/null +++ b/plugins/pychrysa/debug/gdbrsp/module.c @@ -0,0 +1,86 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire gdbrsp en tant que module + * + * Copyright (C) 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module.h" + + +#include "gdb.h" + + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Ajoute le module 'debug.gdbrsp' au module Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_debug_gdbrsp_module_to_python_module(PyObject *super) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Sous-module mis en place */ + int ret; /* Bilan d'un appel */ + + static PyModuleDef py_chrysalide_gdbrsp_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.debug.gdbrsp", + .m_doc = "Python module for Chrysalide.debug.gdbrsp", + + .m_size = -1, + + }; + + result = false; + + module = PyModule_Create(&py_chrysalide_gdbrsp_module); + if (module == NULL) return false; + + ret = PyState_AddModule(super, &py_chrysalide_gdbrsp_module); + if (ret != 0) goto loading_failed; + + ret = _PyImport_FixupBuiltin(module, "pychrysalide.debug.gdbrsp"); + if (ret != 0) goto loading_failed; + + Py_INCREF(module); + ret = PyModule_AddObject(super, "gdbrsp", module); + if (ret != 0) goto loading_failed; + + result = true; + + result &= register_python_gdb_debugger(module); + + loading_failed: + + assert(result); + + return result; + +} diff --git a/plugins/pychrysa/debug/gdbrsp/module.h b/plugins/pychrysa/debug/gdbrsp/module.h new file mode 100644 index 0000000..78e62b0 --- /dev/null +++ b/plugins/pychrysa/debug/gdbrsp/module.h @@ -0,0 +1,39 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire gdbrsp en tant que module + * + * Copyright (C) 2012-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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSA_DEBUG_GDBRSP_MODULE_H +#define _PLUGINS_PYCHRYSA_DEBUG_GDBRSP_MODULE_H + + +#include +#include + + + +/* Ajoute le module 'debug.gdbrsp' au module Python. */ +bool add_debug_gdbrsp_module_to_python_module(PyObject *); + + + +#endif /* _PLUGINS_PYCHRYSA_DEBUG_GDBRSP_MODULE_H */ diff --git a/plugins/pychrysa/debug/module.c b/plugins/pychrysa/debug/module.c index f4b1ff3..db33a13 100644 --- a/plugins/pychrysa/debug/module.c +++ b/plugins/pychrysa/debug/module.c @@ -26,6 +26,7 @@ #include "debugger.h" +#include "gdbrsp/module.h" @@ -43,23 +44,45 @@ bool add_debug_module_to_python_module(PyObject *super) { - bool result; - PyObject *module; + bool result; /* Bilan à retourner */ + PyObject *module; /* Sous-module mis en place */ int ret; /* Bilan d'un appel */ - static PyMethodDef py_debug_methods[] = { - { NULL } + static PyModuleDef py_chrysalide_debug_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.debug", + .m_doc = "Python module for Chrysalide.debug", + + .m_size = -1, + }; - module = Py_InitModule("pyoida.debug", py_debug_methods); + result = false; + + module = PyModule_Create(&py_chrysalide_debug_module); if (module == NULL) return false; + ret = PyState_AddModule(super, &py_chrysalide_debug_module); + if (ret != 0) goto loading_failed; + + ret = _PyImport_FixupBuiltin(module, "pychrysalide.debug"); + if (ret != 0) goto loading_failed; + Py_INCREF(module); - ret = PyModule_AddObject(super, "pyoida.debug", module); + ret = PyModule_AddObject(super, "debug", module); + if (ret != 0) goto loading_failed; + + result = true; + + result &= register_python_binary_debugger(module); + + result &= add_debug_gdbrsp_module_to_python_module(module); - result = (ret == 0); + loading_failed: - //result &= register_python_binary_debugger(module); + assert(result); return result; diff --git a/plugins/pychrysa/debug/module.h b/plugins/pychrysa/debug/module.h index 9c17b75..bbd1971 100644 --- a/plugins/pychrysa/debug/module.h +++ b/plugins/pychrysa/debug/module.h @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYOIDA_DEBUG_MODULE_H -#define _PLUGINS_PYOIDA_DEBUG_MODULE_H +#ifndef _PLUGINS_PYCHRYSA_DEBUG_MODULE_H +#define _PLUGINS_PYCHRYSA_DEBUG_MODULE_H #include @@ -36,4 +36,4 @@ bool add_debug_module_to_python_module(PyObject *); -#endif /* _PLUGINS_PYOIDA_DEBUG_MODULE_H */ +#endif /* _PLUGINS_PYCHRYSA_DEBUG_MODULE_H */ diff --git a/plugins/pychrysa/format/Makefile.am b/plugins/pychrysa/format/Makefile.am index 5733e39..bef87ca 100644 --- a/plugins/pychrysa/format/Makefile.am +++ b/plugins/pychrysa/format/Makefile.am @@ -11,7 +11,6 @@ libpychrysaformat_la_LIBADD = \ dex/libpychrysaformatdex.la \ elf/libpychrysaformatelf.la - libpychrysaformat_la_LDFLAGS = diff --git a/plugins/pychrysa/format/elf/elf.c b/plugins/pychrysa/format/elf/elf.c index f8c067f..102c60d 100644 --- a/plugins/pychrysa/format/elf/elf.c +++ b/plugins/pychrysa/format/elf/elf.c @@ -198,7 +198,7 @@ bool register_python_elf_format(PyObject *module) dict = PyModule_GetDict(module); if (!register_class_for_pygobject(dict, G_TYPE_ELF_FORMAT, - py_elf_format_type, get_python_executable_format_type())) + py_elf_format_type, get_python_executable_format_type())) return false; return true; diff --git a/plugins/pychrysa/format/symbol.c b/plugins/pychrysa/format/symbol.c index 44707d1..101b694 100644 --- a/plugins/pychrysa/format/symbol.c +++ b/plugins/pychrysa/format/symbol.c @@ -384,7 +384,14 @@ static PyObject *py_binary_symbol_get_label(PyObject *self, void *closure) symbol = G_BIN_SYMBOL(pygobject_get(self)); label = g_binary_symbol_get_label(symbol); - result = PyUnicode_FromString(label); + if (label != NULL) + result = PyUnicode_FromString(label); + + else + { + result = Py_None; + Py_INCREF(result); + } return result; diff --git a/plugins/pychrysa/pychrysa.c b/plugins/pychrysa/pychrysa.c index 60d200d..45e32b4 100644 --- a/plugins/pychrysa/pychrysa.c +++ b/plugins/pychrysa/pychrysa.c @@ -46,6 +46,7 @@ #include "arch/module.h" #include "common/module.h" #include "core/module.h" +#include "debug/module.h" #include "format/module.h" #include "glibext/module.h" #include "gtkext/module.h" @@ -391,6 +392,7 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) status &= add_arch_module_to_python_module(result); status &= add_common_module_to_python_module(result); status &= add_core_module_to_python_module(result); + status &= add_debug_module_to_python_module(result); status &= add_format_module_to_python_module(result); status &= add_glibext_module_to_python_module(result); status &= add_gtkext_module_to_python_module(result); diff --git a/src/Makefile.am b/src/Makefile.am index ad9f22d..4b66cfb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,7 +33,6 @@ libchrysadisass_la_LIBADD = \ analysis/libanalysis.la \ arch/libarch.la \ debug/libdebug.la \ - debug/remgdb/libdebugremgdb.la \ format/libformat.la diff --git a/src/analysis/binary.c b/src/analysis/binary.c index a6f0a31..79c82f5 100644 --- a/src/analysis/binary.c +++ b/src/analysis/binary.c @@ -394,7 +394,6 @@ GLoadedBinary *g_loaded_binary_new_from_xml(xmlXPathContextPtr context, const ch char *access; /* Chemin d'accès à un élément */ char *hash; /* Empreinte à retrouver */ GBinContent *content; /* Contenu à référencer */ - xmlXPathObjectPtr xobject; /* Cible d'une recherche */ unsigned int i; /* Boucle de parcours */ diff --git a/src/arch/vmpa.c b/src/arch/vmpa.c index 27262d1..f344101 100644 --- a/src/arch/vmpa.c +++ b/src/arch/vmpa.c @@ -422,7 +422,7 @@ static char *_phys_t_to_string(phys_t phys, MemoryDataSize msize, char buffer[VM switch (msize) { case MDS_8_BITS: - ret = snprintf(buffer, VMPA_MAX_LEN,"0x%02" PRIx64, phys); + ret = snprintf(buffer, VMPA_MAX_LEN, "0x%02" PRIx64, phys); break; case MDS_16_BITS: @@ -499,7 +499,7 @@ char *vmpa2_virt_to_string(const vmpa2t *addr, MemoryDataSize msize, char buffer switch (msize) { case MDS_8_BITS: - ret = snprintf(buffer, VMPA_MAX_LEN,"0x%02" PRIx64, addr->virtual); + ret = snprintf(buffer, VMPA_MAX_LEN, "0x%02" PRIx64, addr->virtual); break; case MDS_16_BITS: diff --git a/src/common/endianness.c b/src/common/endianness.c index eecb397..ba72f3d 100755 --- a/src/common/endianness.c +++ b/src/common/endianness.c @@ -29,6 +29,226 @@ +/* ---------------------------------------------------------------------------------- */ +/* CONVERSION ENTRE BOUTISMES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : value = valeur d'origine à manipuler. * +* endian = ordre des bits dans la source. * +* * +* Description : Adapte un nombre sur 16 bits à un boutisme donné. * +* * +* Retour : Valeur transformée au besoin. * +* * +* Remarques : - * +* * +******************************************************************************/ + +uint16_t swap_u16(const uint16_t *value, SourceEndian endian) +{ + uint16_t result; /* Valeur à retourner */ + + switch (endian) + { + case SRE_LITTLE: + +#if __BYTE_ORDER == __LITTLE_ENDIAN + + result = *value; + +#elif __BYTE_ORDER == __BIG_ENDIAN + + result = ((*value >> 0) & 0xff) << 8 | ((*value >> 8) & 0xff) << 0; + +#else + +# error "TODO : PDP !" + +#endif + + break; + + case SRE_MIDDLE: + /* TODO */ + break; + + case SRE_BIG: + +#if __BYTE_ORDER == __LITTLE_ENDIAN + + result = ((*value >> 0) & 0xff) << 8 | ((*value >> 8) & 0xff) << 0; + +#elif __BYTE_ORDER == __BIG_ENDIAN + + result = *value; + +#else + +# error "TODO : PDP !" + +#endif + + break; + + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = valeur d'origine à manipuler. * +* endian = ordre des bits dans la source. * +* * +* Description : Adapte un nombre sur 16 bits à un boutisme donné. * +* * +* Retour : Valeur transformée au besoin. * +* * +* Remarques : - * +* * +******************************************************************************/ + +uint32_t swap_u32(const uint32_t *value, SourceEndian endian) +{ + uint32_t result; /* Valeur à retourner */ + + switch (endian) + { + case SRE_LITTLE: + +#if __BYTE_ORDER == __LITTLE_ENDIAN + + result = *value; + +#elif __BYTE_ORDER == __BIG_ENDIAN + + result = ((*value >> 0) & 0xff) << 24 | ((*value >> 8) & 0xff) << 16 + | ((*value >> 16) & 0xff) << 8 | ((*value >> 24) & 0xff) << 0; + +#else + +# error "TODO : PDP !" + +#endif + + break; + + case SRE_MIDDLE: + /* TODO */ + break; + + case SRE_BIG: + +#if __BYTE_ORDER == __LITTLE_ENDIAN + + result = ((*value >> 0) & 0xff) << 24 | ((*value >> 8) & 0xff) << 16 + | ((*value >> 16) & 0xff) << 8 | ((*value >> 24) & 0xff) << 0; + +#elif __BYTE_ORDER == __BIG_ENDIAN + + result = *value; + +#else + +# error "TODO : PDP !" + +#endif + + break; + + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = valeur d'origine à manipuler. * +* endian = ordre des bits dans la source. * +* * +* Description : Adapte un nombre sur 16 bits à un boutisme donné. * +* * +* Retour : Valeur transformée au besoin. * +* * +* Remarques : - * +* * +******************************************************************************/ + +uint64_t swap_u64(const uint64_t *value, SourceEndian endian) +{ + uint64_t result; /* Valeur à retourner */ + + switch (endian) + { + case SRE_LITTLE: + +#if __BYTE_ORDER == __LITTLE_ENDIAN + + result = *value; + +#elif __BYTE_ORDER == __BIG_ENDIAN + + result = ((*value >> 0) & 0xff) << 56 | ((*value >> 8) & 0xff) << 48 + | ((*value >> 16) & 0xff) << 40 | ((*value >> 24) & 0xff) << 32 + | ((*value >> 32) & 0xff) << 24 | ((*value >> 40) & 0xff) << 16 + | ((*value >> 48) & 0xff) << 8 | ((*value >> 56) & 0xff) << 0; + +#else + +# error "TODO : PDP !" + +#endif + + break; + + case SRE_MIDDLE: + /* TODO */ + break; + + case SRE_BIG: + +#if __BYTE_ORDER == __LITTLE_ENDIAN + + result = ((*value >> 0) & 0xff) << 56 | ((*value >> 8) & 0xff) << 48 + | ((*value >> 16) & 0xff) << 40 | ((*value >> 24) & 0xff) << 32 + | ((*value >> 32) & 0xff) << 24 | ((*value >> 40) & 0xff) << 16 + | ((*value >> 48) & 0xff) << 8 | ((*value >> 56) & 0xff) << 0; + +#elif __BYTE_ORDER == __BIG_ENDIAN + + result = *value; + +#else + +# error "TODO : PDP !" + +#endif + + break; + + + } + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* BOUTISME DES ENTREES / SORTIES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * diff --git a/src/common/endianness.h b/src/common/endianness.h index 2af6493..5ceb2ee 100755 --- a/src/common/endianness.h +++ b/src/common/endianness.h @@ -43,6 +43,33 @@ typedef enum _SourceEndian } SourceEndian; +/* --------------------------- CONVERSION ENTRE BOUTISMES --------------------------- */ + + +/* Adapte un nombre sur 16 bits à un boutisme donné. */ +uint16_t swap_u16(const uint16_t *, SourceEndian); + +/* Adapte un nombre sur 16 bits à un boutisme donné. */ +uint32_t swap_u32(const uint32_t *, SourceEndian); + +/* Adapte un nombre sur 16 bits à un boutisme donné. */ +uint64_t swap_u64(const uint64_t *, SourceEndian); + + +#define from_u16(v, e) swap_u16(v, e) +#define from_u32(v, e) swap_u32(v, e) +#define from_u64(v, e) swap_u64(v, e) + + +#define to_u16(v, e) swap_u16(v, e) +#define to_u32(v, e) swap_u32(v, e) +#define to_u64(v, e) swap_u64(v, e) + + + +/* ------------------------- BOUTISME DES ENTREES / SORTIES ------------------------- */ + + /* Lit un nombre non signé sur 4 bits. */ bool read_u4(uint8_t *, const bin_t *, phys_t *, phys_t, bool *); diff --git a/src/common/extstr.c b/src/common/extstr.c index 087505a..bd561ba 100644 --- a/src/common/extstr.c +++ b/src/common/extstr.c @@ -196,13 +196,13 @@ char *strrpl(char *haystack, const char *needle1, const char *needle2) haystack = (char *)realloc(haystack, inlen * sizeof(char *)); found = haystack + index; - memmove(found + len2, found + len1, inlen + len2 - index); + memmove(found + len2, found + len1, inlen - len2 - index); } else if (len2 < len1) { - memmove(found + len2, found + len1, inlen + len2 - index); + memmove(found + len2, found + len1, inlen - index - len1); inlen -= (len1 - len2); diff --git a/src/common/xml.c b/src/common/xml.c index cbb7a41..0bd4d86 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -45,9 +45,8 @@ /****************************************************************************** * * -* Paramètres : filename = nom du fichier à ouvrir. * -* xdoc = structure XML chargée. [OUT] * -* context = contexte à utiliser pour les recherches. [OUT] * +* Paramètres : xdoc = structure XML chargée. [OUT] * +* context = contexte à utiliser pour les recherches. [OUT] * * * * Description : Crée un nouveau fichier XML. * * * @@ -79,6 +78,41 @@ bool create_new_xml_file(xmlDocPtr *xdoc, xmlXPathContextPtr *context) /****************************************************************************** * * +* Paramètres : content = données XML présentes en mémoire et à charge. * +* length = quantité de ces données. * +* xdoc = structure XML chargée. [OUT] * +* context = contexte à utiliser pour les recherches. [OUT] * +* * +* Description : Charge un document XML entièrement en mémoire. * +* * +* Retour : true si l'opération a pu s'effectuer, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool load_xml_from_memory(const char *content, size_t length, xmlDocPtr *xdoc, xmlXPathContextPtr *context) +{ + *xdoc = xmlReadMemory(content, length, "noname.xml", NULL, 0); + + if (*xdoc == NULL) + return false; + + *context = xmlXPathNewContext(*xdoc); + + if (*context == NULL) + { + xmlFreeDoc(*xdoc); + return false; + } + + return true; + +} + + +/****************************************************************************** +* * * Paramètres : xdoc = structure XML chargée. * * filename = nom du fichier à remplir. * * * @@ -217,6 +251,65 @@ xmlXPathObjectPtr get_node_xpath_object(xmlXPathContextPtr xpathCtx, const char * * * Paramètres : node = noeud dont une propriété est à lire. * * * +* Description : Obtient le nom de balise d'un noeud donné. * +* * +* Retour : Valeur sous forme de chaîne de caractères ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *qck_get_node_name(xmlNodePtr node) +{ + char *result; /* Valeur en question renvoyée */ + + result = NULL; + + if (node != NULL) + result = strdup((const char *)node->name); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : xpathCtx = contexte à utiliser pour les recherches. * +* path = chemin d'accès au noeud visé. * +* * +* Description : Obtient le nom de balise d'un noeud donné. * +* * +* Retour : Valeur sous forme de chaîne de caractères ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *get_node_name(xmlXPathContextPtr xpathCtx, const char *path) +{ + char *result; /* Valeur en question renvoyée */ + xmlXPathObjectPtr xpathObj; /* Point de départ XML */ + + result = NULL; + + xpathObj = get_node_xpath_object(xpathCtx, path); + if (xpathObj == NULL) return NULL; + + if (xpathObj->nodesetval->nodeNr > 0) + result = qck_get_node_name(xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject(xpathObj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = noeud dont une propriété est à lire. * +* * * Description : Obtient une valeur placée entre <...> et . * * * * Retour : Valeur sous forme de chaîne de caractères ou NULL. * diff --git a/src/common/xml.h b/src/common/xml.h index c3e4b56..ed009c6 100644 --- a/src/common/xml.h +++ b/src/common/xml.h @@ -37,6 +37,9 @@ /* Crée un nouveau fichier XML. */ bool create_new_xml_file(xmlDocPtr *, xmlXPathContextPtr *); +/* Charge un document XML entièrement en mémoire. */ +bool load_xml_from_memory(const char *, size_t, xmlDocPtr *, xmlXPathContextPtr *); + /* Sauvegarde une structure XML dans un fichier. */ bool save_xml_file(xmlDocPtr, const char *); @@ -58,6 +61,12 @@ gboolean open_xml_file(const char *filename, xmlDoc **, xmlXPathContextPtr *); /* Obtient de façon encadrée l'accès à un noeud défini. */ xmlXPathObjectPtr get_node_xpath_object(xmlXPathContextPtr, const char *); +/* Obtient le nom de balise d'un noeud donné. */ +char *qck_get_node_name(xmlNodePtr); + +/* Obtient le nom de balise d'un noeud donné. */ +char *get_node_name(xmlXPathContextPtr, const char *); + /* Obtient une valeur placée entre <...> et . */ char *qck_get_node_text_value(xmlNodePtr); diff --git a/src/debug/Makefile.am b/src/debug/Makefile.am index b2f4685..f955a5a 100755 --- a/src/debug/Makefile.am +++ b/src/debug/Makefile.am @@ -2,16 +2,18 @@ noinst_LTLIBRARIES = libdebug.la libdebug_la_SOURCES = \ + break-int.h \ break.h break.c \ debugger-int.h \ debugger.h debugger.c \ + misc.h \ packet-int.h \ packet.h packet.c \ stream-int.h \ stream.h stream.c libdebug_la_LIBADD = \ - jdwp/libdebugjdwp.la + gdbrsp/libdebuggdbrsp.la libdebug_la_CFLAGS = $(AM_CFLAGS) @@ -20,4 +22,4 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) -SUBDIRS = jdwp remgdb +SUBDIRS = gdbrsp diff --git a/src/debug/break-int.h b/src/debug/break-int.h new file mode 100644 index 0000000..44b3664 --- /dev/null +++ b/src/debug/break-int.h @@ -0,0 +1,64 @@ +/* Chrysalide - Outil d'analyse de fichiers binaires + * break-int.h - prototypes pour la définition générique interne des points d'arrêt + * + * 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 . + */ + + +#ifndef _DEBUG_BREAK_INT_H +#define _DEBUG_BREAK_INT_H + + +#include "break.h" + + + +/* Traçabilité des poses */ +typedef struct _bp_source +{ + RawBpOrigin origin; /* Source de la définition */ + + /* Si origin != RBO_USER : */ + + dbg_thread_id_t tid; /* Identifiant du thread lié */ + virt_t previous; /* Arrêt officiel d'origine */ + +} bp_source; + + +/* Définition d'un point d'arrêt appliqué */ +struct _raw_breakpoint +{ + virt_t addr; /* Adresse d'application */ + + union + { + bp_source source; /* Origine du point d'arrêt */ + bp_source *sources; /* Origines du point d'arrêt */ + }; + size_t count; /* Nombre de ces origines */ + +}; + + +/* Initialise le coeur d'un point d'arrêt. */ +void init_raw_breakpoint(raw_breakpoint *, virt_t); + + + +#endif /* _DEBUG_BREAK_INT_H */ diff --git a/src/debug/break.c b/src/debug/break.c index 19eb787..1d4c2d3 100644 --- a/src/debug/break.c +++ b/src/debug/break.c @@ -24,114 +24,20 @@ #include "break.h" +#include #include -#include -#include "../common/dllist.h" +#include "break-int.h" - -/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ - - -/* Propriétés d'un point d'arrêt (instance) */ -struct _GBreakPoint -{ - GObject parent; /* A laisser en premier */ - - DL_LIST_ITEM(link); /* Maillon de liste chaînée */ - - vmpa_t address; /* Adresse où s'arrêter */ - - bool is_enabled; /* Statut d'activité */ - -}; - -/* Propriétés d'un point d'arrêt (classe) */ -struct _GBreakPointClass -{ - GObjectClass parent; /* A laisser en premier */ - - /* Signaux */ - - void (* changed) (GBreakPoint *); - -}; - - -#define bp_list_add_tail(new, head) dl_list_add_tail(new, head, GBreakPoint, link) -#define bp_list_del(item, head) dl_list_del(item, head, GBreakPoint, link) -#define bp_list_for_each(pos, head) dl_list_for_each(pos, head, GBreakPoint, link) -#define bp_list_for_each_safe(pos, head, next) dl_list_for_each_safe(pos, head, next, GBreakPoint, link) - - -/* Initialise la classe des propriétés d'un point d'arrêt. */ -static void g_break_point_class_init(GBreakPointClass *); - -/* Initialise des propriétés d'un point d'arrêt. */ -static void g_break_point_init(GBreakPoint *); - - - - - - -/* ---------------------------- GROUPE DE POINTS D'ARRET ---------------------------- */ - - -/* Propriétés d'un groupe de points d'arrêt (instance) */ -struct _GBreakGroup -{ - GObject parent; /* A laisser en premier */ - - char *name; /* Désignation humaine */ - - GBreakPoint *points; /* Liste de points d'arrêt */ - -}; - -/* Propriétés d'un groupe de points d'arrêt (classe) */ -struct _GBreakGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - - /* Signaux */ - - void (* added) (GBreakGroup *, GBreakPoint *); - void (* removed) (GBreakGroup *, GBreakPoint *); - -}; - - -/* Initialise la classe des groupes de points d'arrêt. */ -static void g_break_group_class_init(GBreakGroupClass *); - -/* Initialise un groupe de points d'arrêt. */ -static void g_break_group_init(GBreakGroup *); - - - - - - - - -/* ---------------------------------------------------------------------------------- */ -/* GESTION DES POINTS D'ARRET */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour des propriétés d'un point d'arrêt. */ -G_DEFINE_TYPE(GBreakPoint, g_break_point, G_TYPE_OBJECT); - - /****************************************************************************** * * -* Paramètres : klass = classe à initialiser. * +* Paramètres : bp = point d'arrêt à initialiser. * +* addr = adresse d'action du point d'arrêt. * * * -* Description : Initialise la classe des propriétés d'un point d'arrêt. * +* Description : Initialise le coeur d'un point d'arrêt. * * * * Retour : - * * * @@ -139,24 +45,20 @@ G_DEFINE_TYPE(GBreakPoint, g_break_point, G_TYPE_OBJECT); * * ******************************************************************************/ -static void g_break_point_class_init(GBreakPointClass *klass) +void init_raw_breakpoint(raw_breakpoint *bp, virt_t addr) { - g_signal_new("changed", - G_TYPE_BREAK_POINT, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBreakPointClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + bp->addr = addr; + + bp->count = 0; } /****************************************************************************** * * -* Paramètres : point = instance à initialiser. * +* Paramètres : bp = point d'arrêt à traiter. * * * -* Description : Initialise des propriétés d'un point d'arrêt. * +* Description : Libère le coeur d'un point d'arrêt. * * * * Retour : - * * * @@ -164,135 +66,133 @@ static void g_break_point_class_init(GBreakPointClass *klass) * * ******************************************************************************/ -static void g_break_point_init(GBreakPoint *point) +void fini_raw_breakpoint(raw_breakpoint *bp) { - DL_LIST_ITEM_INIT(&point->link); + if (bp->count > 1) + free(bp->sources); + + free(bp); } /****************************************************************************** * * -* Paramètres : address = adresse à laquelle s'arrêter. * +* Paramètres : bp = point d'arrêt à consulter. * * * -* Description : Construit un nouveau point d'arrêt. * +* Description : Indique l'adresse du point d'arrêt dans la mémoire ciblée. * * * -* Retour : Point d'arrêt mis en place ou NULL en cas d'échec. * +* Retour : Adresse associée au point d'arrêt. * * * * Remarques : - * * * ******************************************************************************/ -GBreakPoint *g_break_point_new(vmpa_t address) +virt_t get_raw_breakpoint_addr(const raw_breakpoint *bp) { - GBreakPoint *result; /* Adresse à retourner */ - - result = g_object_new(G_TYPE_BREAK_POINT, NULL); - - result->address = address; - - return result; + return bp->addr; } + /****************************************************************************** * * -* Paramètres : point = instance à consulter. * +* Paramètres : bp = point d'arrêt à consulter. * * * -* Description : Fournit l'adresse associée à un point d'arrêt. * +* Description : Fournit l'adresse d'origine d'un point d'arrêt de pas à pas. * * * -* Retour : Adresse associée. * +* Retour : - * * * -* Remarques : - * +* Remarques : Un appel à cette fonction n'est valide que pour un point * +* d'arrêt de type RBO_STEP. * * * ******************************************************************************/ -vmpa_t g_break_point_get_address(const GBreakPoint *point) +virt_t get_raw_breakpoint_prev_addr(const raw_breakpoint *bp) { - return point->address; + virt_t result; /* Localisation à retourner */ + bool found; /* Valide une obtention */ + size_t i; /* Boucle de parcours */ -} + switch (bp->count) + { + case 1: + assert(bp->source.origin == RBO_INTERNAL || bp->source.origin == RBO_STEP); + result = bp->source.previous; + break; + default: + found = false; -/* ---------------------------------------------------------------------------------- */ -/* GROUPE DE POINTS D'ARRET */ -/* ---------------------------------------------------------------------------------- */ + for (i = 0; i < bp->count && !found; i++) + if (bp->sources[i].origin == RBO_INTERNAL || bp->sources[i].origin == RBO_STEP) + { + result = bp->sources[i].previous; + found = true; + } + assert(found); -/* Indique le type défini pour les groupes de points d'errêt. */ -G_DEFINE_TYPE(GBreakGroup, g_break_group, G_TYPE_OBJECT); + break; + } -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des groupes de points d'arrêt. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_break_group_class_init(GBreakGroupClass *klass) -{ - g_signal_new("added", - G_TYPE_BREAK_GROUP, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBreakGroupClass, added), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, G_TYPE_BREAK_POINT); - - g_signal_new("removed", - G_TYPE_BREAK_GROUP, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBreakGroupClass, removed), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, G_TYPE_BREAK_POINT); + return result; } /****************************************************************************** * * -* Paramètres : group = instance à initialiser. * +* Paramètres : addr = adresse à consulter. * +* bp = point d'arrêt à consulter. * * * -* Description : Initialise un groupe de point d'arrêt. * +* Description : Effectue une comparaison entre adresse et point d'arrêt. * * * -* Retour : - * +* Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ -static void g_break_group_init(GBreakGroup *group) +int compare_raw_breakpoint_with_addr(const virt_t *addr, const raw_breakpoint **bp) { + int result; /* Bilan à retourner */ + + if (*addr < (*bp)->addr) + result = -1; + + else if (*addr == (*bp)->addr) + result = 0; + + else + result = 1; + + return result; } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : a = premier point d'arrêt à consulter. * +* b = second point d'arrêt à consulter. * * * -* Description : Construit un nouveau groupe de points d'arrêt. * +* Description : Effectue une comparaison entre deux points d'arrêt. * * * -* Retour : Groupe de points d'arrêt mis en place ou NULL en cas d'échec.* +* Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ -GBreakGroup *g_break_group_new(void) +int compare_raw_breakpoints(const raw_breakpoint **a, const raw_breakpoint **b) { - GBreakGroup *result; /* Adresse à retourner */ + int result; /* Bilan à retourner */ - result = g_object_new(G_TYPE_BREAK_GROUP, NULL); + result = compare_raw_breakpoint_with_addr(&(*a)->addr, b); return result; @@ -301,140 +201,263 @@ GBreakGroup *g_break_group_new(void) /****************************************************************************** * * -* Paramètres : group = groupe de points d'arrêt à consulter. * +* Paramètres : bp = point d'arrêt à manipuler. * +* origin = origine de la création du point d'arrêt. * +* tid = identifiant du thread concerné. * +* previous = éventuelle adresse précédent celle du point. * * * -* Description : Fournit la désignation humaine associée à un groupe. * +* Description : Enregistre la source d'un point d'arrêt posé. * * * -* Retour : Désignation humaine associée, jamais NULL. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -const char *g_break_group_get_name(const GBreakGroup *group) +void set_raw_breakpoint_origin(raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid, virt_t previous) { - return group->name; +#ifndef NDEBUG + size_t i; /* Boucle de parcours */ +#endif + bp_source *src; /* Source à définir */ + bp_source tmp; /* Copie temporaire */ + +#ifndef NDEBUG + + if (bp->count == 1) + assert(bp->source.tid != tid || (bp->source.origin & origin) == 0); + + else + for (i = 0; i < bp->count; i++) + if (bp->source.tid == tid) + { + assert((bp->sources[i].origin & origin) == 0); + break; + } + +#endif + + bp->count++; + + switch (bp->count) + { + case 1: + src = &bp->source; + break; + + case 2: + tmp = bp->source; + bp->sources = (bp_source *)calloc(2, sizeof(bp_source)); + bp->sources[0] = tmp; + src = &bp->sources[1]; + break; + + default: + bp->sources = (bp_source *)realloc(bp->sources, bp->count * sizeof(bp_source)); + src = &bp->sources[bp->count - 1]; + break; + + } + + src->origin = origin; + src->tid = tid; + src->previous = previous; } /****************************************************************************** * * -* Paramètres : group = groupe de points d'arrêt à modifier. * -* name = nouveau nom de scène. * +* Paramètres : bp = point d'arrêt à manipuler. * +* origin = origine de la création du point d'arrêt. * +* tid = identifiant du thread concerné. * +* previous = éventuelle adresse précédent celle du point. * * * -* Description : Définit la désignation humaine à associer à un groupe. * +* Description : Oublie la source d'un point d'arrêt posé. * * * -* Retour : Désignation humaine associée, voire NULL. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -void g_break_group_set_name(GBreakGroup *group, const char *name) +void unset_raw_breakpoint_origin(raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid) { - if (group->name != NULL) - free(group->name); + size_t i; /* Boucle de parcours #1 */ + bp_source tmp; /* Copie temporaire */ +#ifndef NDEBUG + size_t j; /* Boucle de parcours #2 */ +#endif - if (name == NULL) - group->name = NULL; + bool has_same_origin(bp_source *src) + { + bool result; - else - group->name = strdup(name); + result = (src->origin == origin && src->tid == tid); + + return result; + + } + + switch (bp->count) + { + case 1: + + if (has_same_origin(&bp->source)) + bp->count = 0; + + break; + + case 2: + + if (has_same_origin(&bp->sources[0])) + { + assert(!has_same_origin(&bp->sources[1])); + + tmp = bp->sources[1]; + + bp->count = 1; + free(bp->sources); + + bp->source = tmp; + + } + + else if (has_same_origin(&bp->sources[1])) + { + assert(!has_same_origin(&bp->sources[0])); + + tmp = bp->sources[0]; + + bp->count = 1; + free(bp->sources); + + bp->source = tmp; + + } + + break; + + default: + + for (i = 0; i < bp->count; i++) + { + if (has_same_origin(&bp->sources[i])) + { + if ((i + 1) < bp->count) + memmove(&bp->sources[i], &bp->sources[i + 1], (bp->count - i - 1) * sizeof(bp_source)); + + bp->sources = (bp_source *)realloc(bp->sources, --bp->count * sizeof(bp_source)); + +#ifndef NDEBUG + for (j = i; j < bp->count; j++) + assert(!has_same_origin(&bp->sources[j])); +#endif + + break; + + } + + } + + break; + + } } /****************************************************************************** * * -* Paramètres : group = groupe de points d'arrêt à consulter. * -* addr = adresse recherchée dans les points d'arrêt. * +* Paramètres : bp = point d'arrêt à manipuler. * +* origin = origine de la création du point d'arrêt. * +* tid = identifiant du thread concerné. * * * -* Description : Indique si une adresse donnée est gérée dans un groupe. * +* Description : Indique si le point d'arrêt correspond à une source donnée. * * * -* Retour : true si l'adresse est gérée ici, false sinon. * +* Retour : Bilan de l'analyse. * * * * Remarques : - * * * ******************************************************************************/ -bool g_break_group_has_address(const GBreakGroup *group, vmpa_t addr) +bool has_raw_breakpoint_origin(const raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid) { - GBreakPoint *iter; /* Boucle de parcours */ + bool result; /* Conclusion à retourner */ + size_t i; /* Boucle de parcours */ + + if (bp->count == 1) + result = (bp->source.tid == tid && (bp->source.origin & origin) != 0); + + else + { + result = false; + + for (i = 0; i < bp->count && !result; i++) + result = (bp->sources[i].tid == tid && (bp->sources[i].origin & origin) != 0); - bp_list_for_each(iter, group->points) - if (g_break_point_get_address(iter) == addr) - return true; + } - return false; + return result; } /****************************************************************************** * * -* Paramètres : group = groupe de points d'arrêt à modifier. * -* addr = adresse mémoire à faire basculer. * +* Paramètres : bp = point d'arrêt à manipuler. * +* origin = origine de la création du point d'arrêt. * +* tid = identifiant du thread concerné. * +* prev = adresse d'instruction qui a conduit à des poses. * * * -* Description : Ajoute ou supprime un point d'arrêt dans un groupe. * +* Description : Indique si le point d'arrêt correspond à une origine donnée. * * * -* Retour : - * +* Retour : Bilan de l'analyse. * * * * Remarques : - * * * ******************************************************************************/ -void g_break_group_toggle_breakpoint(GBreakGroup *group, vmpa_t addr) +bool has_raw_breakpoint_previous_address(const raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid, virt_t prev) { - GBreakPoint *iter; /* Boucle de parcours */ - GBreakPoint *next; /* Prochain point de passage */ + bool result; /* Conclusion à retourner */ + size_t i; /* Boucle de parcours */ - /* Suppression d'un éventuel existant */ + if (bp->count == 1) + result = (bp->source.tid == tid && (bp->source.origin & origin) != 0 && bp->source.previous == prev); - bp_list_for_each_safe(iter, &group->points, next) - if (g_break_point_get_address(iter) == addr) - { - g_signal_emit_by_name(group, "removed", iter); - - bp_list_del(iter, &group->points); - g_object_unref(G_OBJECT(iter)); - - return; - - } - - /* Si non trouvé, ajout du nouveau point */ + else + { + result = false; - iter = g_break_point_new(addr); + for (i = 0; i < bp->count && !result; i++) + result = (bp->sources[i].tid == tid && (bp->sources[i].origin & origin) != 0 && bp->sources[i].previous == prev); - bp_list_add_tail(iter, &group->points); + } - g_signal_emit_by_name(group, "added", iter); + return result; } /****************************************************************************** * * -* Paramètres : group = groupe de points d'arrêt à parcourir. * -* func = fonction à appeler à chaque point trouvé. * -* data = éventuelle donnée de l'utilisateur à joindre. * +* Paramètres : bp = point d'arrêt à consulter. * * * -* Description : Parcourt l'ensemble des points d'arrêt d'un groupe donné. * +* Description : Indique si un point d'arrêt a encore une utilité. * * * -* Retour : - * +* Retour : true si le point peut être retiré, false sinon. * * * * Remarques : - * * * ******************************************************************************/ -void g_break_group_for_each(GBreakGroup *group, GExtFunc func, gpointer data) +bool is_breakpoint_useless(const raw_breakpoint *bp) { - GBreakPoint *iter; /* Boucle de parcours */ + bool result; /* Conclusion à faire remonter */ - /* Suppression d'un éventuel existant */ + result = (bp->count == 0); - bp_list_for_each(iter, group->points) - func(group, iter, data); + return result; } diff --git a/src/debug/break.h b/src/debug/break.h index 5946ad2..77d959a 100644 --- a/src/debug/break.h +++ b/src/debug/break.h @@ -25,80 +25,58 @@ #define _DEBUG_BREAK_H -#include -#include +#include "misc.h" +#include "../arch/vmpa.h" -#include "../arch/archbase.h" -#include "../glibext/proto.h" +/* Origine des points d'arrêt en place */ +typedef enum _RawBpOrigin +{ + RBO_INVALID = (0 << 0), /* Existance illégale */ + RBO_USER = (1 << 0), /* Point d'arrêt utilisateur */ + //RBO_COMPUTED = (1 << 1), /* Arrêt sur un point spécial */ + RBO_INTERNAL = (1 << 2), /* Restauration transparente */ + RBO_STEP = (1 << 3), /* Mise en place éphémère */ + RBO_COUNT -/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ +} RawBpOrigin; -#define G_TYPE_BREAK_POINT g_break_point_get_type() -#define G_BREAK_POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_break_point_get_type(), GBreakPoint)) -#define G_IS_BREAK_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_break_point_get_type())) -#define G_BREAK_POINT_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), g_break_point_get_type(), GBreakPointIface)) -#define G_BREAK_POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BREAK_POINT, GBreakPointClass)) +/* Définition d'un point d'arrêt appliqué */ +typedef struct _raw_breakpoint raw_breakpoint; -/* Propriétés d'un point d'arrêt (instance) */ -typedef struct _GBreakPoint GBreakPoint; +/* Libère le coeur d'un point d'arrêt. */ +void fini_raw_breakpoint(raw_breakpoint *); -/* Propriétés d'un point d'arrêt (classe) */ -typedef struct _GBreakPointClass GBreakPointClass; +/* Indique l'adresse du point d'arrêt dans la mémoire ciblée. */ +virt_t get_raw_breakpoint_addr(const raw_breakpoint *); +/* Fournit l'adresse d'origine d'un point d'arrêt de pas à pas. */ +virt_t get_raw_breakpoint_prev_addr(const raw_breakpoint *); -/* Indique le type défini pour des propriétés d'un point d'arrêt. */ -GType g_break_point_get_type(void); +/* Effectue une comparaison entre adresse et point d'arrêt. */ +int compare_raw_breakpoint_with_addr(const virt_t *, const raw_breakpoint **); -/* Construit un nouveau point d'arrêt. */ -GBreakPoint *g_break_point_new(vmpa_t); +/* Effectue une comparaison entre deux points d'arrêt. */ +int compare_raw_breakpoints(const raw_breakpoint **, const raw_breakpoint **); -/* Fournit l'adresse associée à un point d'arrêt. */ -vmpa_t g_break_point_get_address(const GBreakPoint *); +/* Enregistre la source d'un point d'arrêt posé. */ +void set_raw_breakpoint_origin(raw_breakpoint *, RawBpOrigin, dbg_thread_id_t, virt_t); +/* Oublie la source d'un point d'arrêt posé. */ +void unset_raw_breakpoint_origin(raw_breakpoint *, RawBpOrigin, dbg_thread_id_t); +/* Indique si le point d'arrêt correspond à une source donnée. */ +bool has_raw_breakpoint_origin(const raw_breakpoint *, RawBpOrigin, dbg_thread_id_t); -/* ---------------------------- GROUPE DE POINTS D'ARRET ---------------------------- */ +/* Indique si le point d'arrêt correspond à une origine donnée. */ +bool has_raw_breakpoint_previous_address(const raw_breakpoint *, RawBpOrigin, dbg_thread_id_t, virt_t); - -#define G_TYPE_BREAK_GROUP g_break_group_get_type() -#define G_BREAK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_break_group_get_type(), GBreakGroup)) -#define G_IS_BREAK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_break_group_get_type())) -#define G_BREAK_GROUP_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), g_break_group_get_type(), GBreakGroupIface)) -#define G_BREAK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BREAK_GROUP, GBreakGroupClass)) - - -/* Propriétés d'un groupe de points d'arrêt (instance) */ -typedef struct _GBreakGroup GBreakGroup; - -/* Propriétés d'un groupe de points d'arrêt (classe) */ -typedef struct _GBreakGroupClass GBreakGroupClass; - - -/* Indique le type défini pour les groupes de points d'errêt. */ -GType g_break_group_get_type(void); - -/* Construit un nouveau groupe de points d'arrêt. */ -GBreakGroup *g_break_group_new(void); - -/* Fournit la désignation humaine associée à un groupe. */ -const char *g_break_group_get_name(const GBreakGroup *); - -/* Définit la désignation humaine à associer à un groupe. */ -void g_break_group_set_name(GBreakGroup *, const char *); - -/* Indique si une adresse donnée est gérée dans un groupe. */ -bool g_break_group_has_address(const GBreakGroup *, vmpa_t); - -/* Ajoute ou supprime un point d'arrêt dans un groupe. */ -void g_break_group_toggle_breakpoint(GBreakGroup *, vmpa_t); - -/* Parcourt l'ensemble des points d'arrêt d'un groupe donné. */ -void g_break_group_for_each(GBreakGroup *, GExtFunc, gpointer); +/* Indique si un point d'arrêt a encore une utilité. */ +bool is_breakpoint_useless(const raw_breakpoint *); diff --git a/src/debug/debugger-int.h b/src/debug/debugger-int.h index 06aa5d3..58153b8 100644 --- a/src/debug/debugger-int.h +++ b/src/debug/debugger-int.h @@ -28,7 +28,15 @@ #include "debugger.h" -#include +#include "break.h" + + + +//#include //////////////////////////////////////////// + + + + @@ -39,16 +47,71 @@ typedef bool (* attach_debugger_fc) (GBinaryDebugger *); typedef bool (* basic_debugger_fc) (GBinaryDebugger *); /* Reprend une procédure de débogage. */ -typedef bool (* resume_debugger_fc) (GBinaryDebugger *); +//typedef bool (* resume_debugger_fc) (GBinaryDebugger *); /* Fournit les identifiants de tous les threads actifs. */ -typedef pid_t * (* dbg_list_all_threads_fc) (GBinaryDebugger *, char ***, size_t *); +//typedef pid_t * (* dbg_list_all_threads_fc) (GBinaryDebugger *, char ***, size_t *); /* Fournit la liste des frames courantes d'un thread donné. */ -typedef dbg_frame_t * (* dbg_get_frames_stack_fc) (GBinaryDebugger *, pid_t, size_t *); +//typedef dbg_frame_t * (* dbg_get_frames_stack_fc) (GBinaryDebugger *, pid_t, size_t *); /* Fournit la valeur des registres de l'architecture. */ -typedef register_value * (* get_register_values_fc) (GBinaryDebugger *, size_t *); +//typedef register_value * (* get_register_values_fc) (GBinaryDebugger *, size_t *); + + + + + + +/* Fournit les identifiants de tous les threads actifs. */ +typedef dbg_thread_desc * (* list_all_threads_fc) (GBinaryDebugger *, size_t *); + + + + +/* Lit une valeur quelconque à une adresse arbitraire. */ +typedef bool (* read_mem_any_fc) (GBinaryDebugger *, virt_t, size_t, ...); + +/* Ecrit une valeur quelconque à une adresse arbitraire. */ +typedef bool (* write_mem_any_fc) (GBinaryDebugger *, virt_t, size_t, ...); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +typedef char ** (* get_reg_names_fc) (const GBinaryDebugger *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +typedef unsigned int (* get_reg_size_fc) (const GBinaryDebugger *, const char *); + +/* Lit une valeur quelconque à partir d'un registre. */ +typedef bool (* read_write_reg_any_fc) (GBinaryDebugger *, const char *, size_t, ...); + + + +/* Détermine le point d'exécution courant. */ +typedef bool (* get_current_pc_fc) (GBinaryDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +typedef bool (* get_call_stack_fc) (GBinaryDebugger *, virt_t **, size_t *); + + + +/* Ajoute un point d'arrêt basique en mémoire. */ +typedef raw_breakpoint * (* enable_mem_bp_fc) (GBinaryDebugger *, virt_t); + +/* Retire un point d'arrêt basique en mémoire. */ +typedef bool (* disable_mem_bp_fc) (GBinaryDebugger *, raw_breakpoint *); + +/* Redémarre le processus de débogage lié à un serveur GDB. */ +typedef bool (* restart_debugger_fc) (GBinaryDebugger *); + +/* Remet en marche le débogueur courant. */ +typedef bool (* resume_debugger_fc) (GBinaryDebugger *); + + + + + + + /* Définition des fonctionnalités d'un débogueur (instance) */ @@ -62,13 +125,13 @@ struct _GBinaryDebugger basic_debugger_fc run; /* Démarre le débogueur */ basic_debugger_fc pause; /* Met en pause le débogueur */ - resume_debugger_fc resume; /* Relance le débogueur */ basic_debugger_fc kill; /* Tue le débogueur */ - dbg_list_all_threads_fc all_threads; /* Liste des threads actifs */ - dbg_get_frames_stack_fc frames_stack; /* Pile des frames courantes */ - get_register_values_fc get_reg_values; /* Obtient les valeurs de reg. */ + + raw_breakpoint **bpoints; /* Points d'arrêt posés */ + size_t bp_count; /* Quantité de ces points posés*/ + GRWLock bp_lock; /* Verrou d'accès à la liste */ }; @@ -78,13 +141,58 @@ struct _GBinaryDebuggerClass { GObjectClass parent; /* A laisser en premier */ + list_all_threads_fc all_threads; /* Liste des threads actifs */ + + read_mem_any_fc read_mem; /* Lecture d'une valeur XX bits*/ + read_mem_any_fc write_mem; /* Ecriture d'une valeur X bits*/ + get_reg_names_fc get_reg_names; /* Liste des registres */ + get_reg_size_fc get_reg_size; /* Taille d'un registre donné */ + read_write_reg_any_fc read_reg; /* Lecture de registre XX bits */ + read_write_reg_any_fc write_reg; /* Ecriture de registre XX bits*/ + + get_current_pc_fc get_current_pc; /* Obtention du point d'exéc. */ + get_call_stack_fc get_call_stack; /* Obtention de pile d'appels */ + + enable_mem_bp_fc enable_bp; /* Mise en place d'un arrêt */ + disable_mem_bp_fc disable_bp; /* Retrait d'un point d'arrêt */ + + restart_debugger_fc restart; /* Redémarre le débogueur */ + resume_debugger_fc resume; /* Relance le débogueur */ + /* Signaux */ - void (* debugger_stopped) (GBinaryDebugger *, uint64_t, uint64_t); + void (* signaled) (GBinaryDebugger *, int); + void (* exited) (GBinaryDebugger *, int, pid_t); + void (* terminated) (GBinaryDebugger *, int, pid_t); + + void (* stopped) (GBinaryDebugger *, virt_t); + + + + + /* Signaux */ void (* debugger_halted) (GBinaryDebugger *, int, vmpa_t, pid_t); + + + + + void (* mem_bp_handled) (GBinaryDebugger *, bool, virt_t); + }; + +/* ------------------------- MANIPULATION DE L'ETAT COURANT ------------------------- */ + + +/* Réagit à un arrêt du flot d'exécution. */ +void on_binary_debugger_stopped(GBinaryDebugger *, virt_t); + +/* Réagit à la fin de l'opération de débogage. */ +void on_binary_debugger_finished(GBinaryDebugger *, pid_t); + + + #endif /* _DEBUG_DEBUGGER_INT_H */ diff --git a/src/debug/debugger.c b/src/debug/debugger.c index 331adc6..0dfe315 100644 --- a/src/debug/debugger.c +++ b/src/debug/debugger.c @@ -23,27 +23,70 @@ #include "debugger.h" + +#include +#include +#include +#include + + +#include + + #include "debugger-int.h" -#include "jdwp/debugger.h" -#include "remgdb/gdb.h" +#include "../common/sort.h" #include "../glibext/chrysamarshal.h" +#include "../gui/panels/log.h" #include "../plugins/pglist.h" +/* ---------------------------- TRONC COMMUN DE DEBOGAGE ---------------------------- */ + + /* Initialise la classe de base des débogueurs. */ static void g_binary_debugger_class_init(GBinaryDebuggerClass *); /* Initialise une instance de base d'un débogueur. */ static void g_binary_debugger_init(GBinaryDebugger *); +/* Supprime toutes les références externes. */ +static void g_binary_debugger_dispose(GBinaryDebugger *); + +/* Procède à la libération totale de la mémoire. */ +static void g_binary_debugger_finalize(GBinaryDebugger *); + + + +/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ + + +/* Active un point d'arrêt à un emplacement de mémoire donné. */ +static bool g_binary_debugger_insert_memory_breakpoint(GBinaryDebugger *, virt_t, RawBpOrigin, dbg_thread_id_t, virt_t); + +/* Désactive un point d'arrêt à un emplacement de mémoire donné. */ +static bool g_binary_debugger_remove_memory_breakpoint(GBinaryDebugger *, virt_t); + +/* Sème des points d'arrêt sur les instructions suivantes. */ +static bool g_binary_debugger_spread_breakpoints(GBinaryDebugger *, dbg_thread_id_t, virt_t, RawBpOrigin, bool); + +/* Retire tous les points d'arrêt issus d'un adresse. */ +static void g_binary_debugger_remove_same_breakpoints(GBinaryDebugger *, dbg_thread_id_t, virt_t, RawBpOrigin); + +/* Met à jour les points d'arrêt suite à un arrêt. */ +static bool g_binary_debugger_update_breakpoints_on_stop(GBinaryDebugger *, dbg_thread_id_t, virt_t); + + + +/* ---------------------------------------------------------------------------------- */ +/* TRONC COMMUN DE DEBOGAGE */ +/* ---------------------------------------------------------------------------------- */ /* Indique le type définit pour une ligne de représentation. */ G_DEFINE_TYPE(GBinaryDebugger, g_binary_debugger, G_TYPE_OBJECT); - /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * @@ -58,13 +101,46 @@ G_DEFINE_TYPE(GBinaryDebugger, g_binary_debugger, G_TYPE_OBJECT); static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass) { - g_signal_new("debugger-stopped", + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_debugger_dispose; + object->finalize = (GObjectFinalizeFunc)g_binary_debugger_finalize; + + g_signal_new("signaled", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, signaled), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + g_signal_new("exited", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, exited), + NULL, NULL, + g_cclosure_user_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + + g_signal_new("terminated", G_TYPE_BINARY_DEBUGGER, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBinaryDebuggerClass, debugger_stopped), + G_STRUCT_OFFSET(GBinaryDebuggerClass, terminated), NULL, NULL, - g_cclosure_user_marshal_VOID__UINT64_UINT64, - G_TYPE_NONE, 2, G_TYPE_UINT64, G_TYPE_UINT64); + g_cclosure_user_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + + g_signal_new("stopped", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, stopped), + NULL, NULL, + g_cclosure_user_marshal_VOID__UINT64, + G_TYPE_NONE, 1, G_TYPE_UINT64); + + g_signal_new("halted", G_TYPE_BINARY_DEBUGGER, @@ -74,6 +150,18 @@ static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass) g_cclosure_user_marshal_VOID__INT_UINT64_INT, G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT64, G_TYPE_INT); + + + + + g_signal_new("mem-bp-handled", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, mem_bp_handled), + NULL, NULL, + g_cclosure_user_marshal_VOID__INT_UINT64_INT, + G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT64); + } @@ -91,51 +179,64 @@ static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass) static void g_binary_debugger_init(GBinaryDebugger *debugger) { + g_rw_lock_init(&debugger->bp_lock); } /****************************************************************************** * * -* Paramètres : type = type de débigueur choisi pour l'opération. * -* binary = binaire devant être débogué. * +* Paramètres : debugger = instance d'objet GLib à traiter. * * * -* Description : Crée un nouveau débogueur. * +* Description : Supprime toutes les références externes. * * * -* Retour : Composant GObject mis en place ou NULL. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GBinaryDebugger *g_new_binary_debugger(DebuggerType type, GLoadedBinary *binary) +static void g_binary_debugger_dispose(GBinaryDebugger *debugger) { - GBinaryDebugger *result; + g_object_unref(G_OBJECT(debugger->binary)); - switch (type) - { - case DGT_JDWP: - result = g_java_debugger_new(binary, NULL); - break; + g_rw_lock_clear(&debugger->bp_lock); - case DGT_REMOTE_GDB: - result = g_gdb_debugger_new(binary, NULL); - break; + G_OBJECT_CLASS(g_binary_debugger_parent_class)->dispose(G_OBJECT(debugger)); - default: - result = NULL; - break; +} - } - if (result != NULL) - result->binary = binary; +/****************************************************************************** +* * +* Paramètres : debugger = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - return result; +static void g_binary_debugger_finalize(GBinaryDebugger *debugger) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < debugger->bp_count; i++) + fini_raw_breakpoint(debugger->bpoints[i]); + + if (debugger->bpoints != NULL) + free(debugger->bpoints); + + G_OBJECT_CLASS(g_binary_debugger_parent_class)->finalize(G_OBJECT(debugger)); } + + + /****************************************************************************** * * * Paramètres : debugger = débogueur à manipuler ici. * @@ -196,11 +297,13 @@ void g_binary_debugger_run(GBinaryDebugger *debugger) } + + /****************************************************************************** * * * Paramètres : debugger = débogueur à manipuler ici. * * * -* Description : Reprend une procédure de débogage. * +* Description : Tue une procédure de débogage. * * * * Retour : - * * * @@ -208,18 +311,37 @@ void g_binary_debugger_run(GBinaryDebugger *debugger) * * ******************************************************************************/ -void g_binary_debugger_resume(GBinaryDebugger *debugger) +void g_binary_debugger_kill(GBinaryDebugger *debugger) { - debugger->resume(debugger); + debugger->kill(debugger); } + + + + + + + + + +/* ------------------- MANIPULATION DES DIFFERENTS THREADS ACTIFS ------------------- */ + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES DIFFERENTS THREADS ACTIFS */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : debugger = débogueur à manipuler ici. * +* Paramètres : list = descriptions à supprimer de la mémoire. * +* count = taille de cette liste. * * * -* Description : Tue une procédure de débogage. * +* Description : Libère la mémoire d'une liste de threads actifs. * * * * Retour : - * * * @@ -227,9 +349,15 @@ void g_binary_debugger_resume(GBinaryDebugger *debugger) * * ******************************************************************************/ -void g_binary_debugger_kill(GBinaryDebugger *debugger) +void delete_dbg_thread_desc(dbg_thread_desc *list, size_t count) { - debugger->kill(debugger); + size_t i; /* Boucle de parcours */ + + for (i = 0; i < count; i++) + free(list[i].name); + + if (list != NULL) + free(list); } @@ -242,50 +370,212 @@ void g_binary_debugger_kill(GBinaryDebugger *debugger) * * * Description : Fournit les identifiants de tous les threads actifs. * * * -* Retour : Liste des threads décomptés. * +* Retour : Liste des threads décomptés, à libérer de la mémoire ensuite.* * * * Remarques : - * * * ******************************************************************************/ -pid_t *g_binary_debugger_list_all_threads(GBinaryDebugger *debugger, char ***names, size_t *count) +dbg_thread_desc *g_binary_debugger_list_all_threads(GBinaryDebugger *debugger, size_t *count) { - pid_t *result; /* Liste à retourner */ + dbg_thread_desc *result; /* Liste à retourner */ - if (debugger->all_threads != NULL) - result = debugger->all_threads(debugger, names, count); - else - result = NULL; + *count = 0; + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->all_threads(debugger, count); + + if (result != NULL) + { + int cmp_dbg_thread_desc(const dbg_thread_desc *a, const dbg_thread_desc *b) + { + int status; /* Bilan à retourner */ + + if (a->id < b->id) + status = -1; + + if (a->id > b->id) + status = 1; + + else + status = -1; + + return status; + + } + + qsort(result, *count, sizeof(dbg_thread_desc), (__compar_fn_t)cmp_dbg_thread_desc); + + } return result; } + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/* ENTREES / SORTIES BASIQUES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : debugger = instance du module de débogage chargé. * -* thread = thread concerné par l'analyse. * -* count = nombre de frames en place. [OUT] * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * * * -* Description : Fournit la liste des frames courantes d'un thread donné. * +* Description : Lit une valeur de 8 bits à une adresse arbitraire. * * * -* Retour : Liste des frames trouvées. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *debugger, pid_t thread, size_t *count) +bool g_binary_debugger_read_memory_u8(GBinaryDebugger *debugger, virt_t addr, uint8_t *value) { - dbg_frame_t *result; /* Liste à retourner */ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 8, value); - if (debugger->frames_stack != NULL) - result = debugger->frames_stack(debugger, thread, count); - else +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 16 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u16(GBinaryDebugger *debugger, virt_t addr, uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 32 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u32(GBinaryDebugger *debugger, virt_t addr, uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 64 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u64(GBinaryDebugger *debugger, virt_t addr, uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 64, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* len = taille attendue de la valeur en octets. * +* * +* Description : Lit une valeur de taille quelconque à une adresse arbitraire.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_data(GBinaryDebugger *debugger, virt_t addr, uint8_t *value, size_t len) +{ + bool result; /* Bilan à retourner */ + size_t iter; /* Tête de lecture / écriture */ + size_t remaining; /* Quantité restant à replacer */ + + result = true; + + iter = 0; + remaining = len; + + while (result && remaining > 0) { - *count = 0; - result = NULL; + if (remaining >= 8) + { + result = g_binary_debugger_read_memory_u64(debugger, addr + iter, (uint64_t *)&value[iter]); + + iter += 8; + remaining -= 8; + + } + + else if (remaining >= 4) + { + result = g_binary_debugger_read_memory_u32(debugger, addr + iter, (uint32_t *)(&value[iter])); + + iter += 4; + remaining -= 4; + + } + + else if (remaining >= 2) + { + result = g_binary_debugger_read_memory_u16(debugger, addr + iter, (uint16_t *)&value[iter]); + + iter += 2; + remaining -= 2; + + } + + else if (remaining >= 1) + { + result = g_binary_debugger_read_memory_u8(debugger, addr + iter, (uint8_t *)&value[iter]); + + iter += 1; + remaining -= 1; + + } + + else + assert(false); + } return result; @@ -295,19 +585,1164 @@ dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *debugger, pid_t /****************************************************************************** * * -* Paramètres : debugger = débogueur à manipuler ici. * -* count = nombre de transmissions effetuées. * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 8 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u8(GBinaryDebugger *debugger, virt_t addr, const uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 16 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u16(GBinaryDebugger *debugger, virt_t addr, const uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 32 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u32(GBinaryDebugger *debugger, virt_t addr, const uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * * * -* Description : Fournit la valeur des registres de l'architecture. * +* Description : Ecrit une valeur de 64 bits à une adresse arbitraire. * * * -* Retour : Tableau de valeurs transmises à libérer de la mémoire / NULL.* +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -register_value *g_binary_debugger_get_registers(GBinaryDebugger *debugger, size_t *count) +bool g_binary_debugger_write_memory_u64(GBinaryDebugger *debugger, virt_t addr, const uint64_t *value) { - return debugger->get_reg_values(debugger, count); + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 64, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à incrire. * +* len = taille de la valeur fournie en octets. * +* * +* Description : Ecrit une valeur de taille quelconque à une adresse donnée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_data(GBinaryDebugger *debugger, virt_t addr, const uint8_t *value, size_t len) +{ + bool result; /* Bilan à retourner */ + size_t iter; /* Tête de lecture / écriture */ + size_t remaining; /* Quantité restant à replacer */ + + result = true; + + iter = 0; + remaining = len; + + while (result && remaining > 0) + { + if (remaining >= 8) + { + result = g_binary_debugger_write_memory_u64(debugger, addr + iter, (uint64_t *)&value[iter]); + + iter += 8; + remaining -= 8; + + } + + else if (remaining >= 4) + { + result = g_binary_debugger_write_memory_u32(debugger, addr + iter, (uint32_t *)&value[iter]); + + iter += 4; + remaining -= 4; + + } + + else if (remaining >= 2) + { + result = g_binary_debugger_write_memory_u16(debugger, addr + iter, (uint16_t *)&value[iter]); + + iter += 2; + remaining -= 2; + + } + + else if (remaining >= 1) + { + result = g_binary_debugger_write_memory_u8(debugger, addr + iter, (uint8_t *)&value[iter]); + + iter += 1; + remaining -= 1; + + } + + else + assert(false); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* group = éventuel groupe de registres ciblé ou NULL. * +* count = nombre d'éléments dans la liste de noms. [OUT] * +* * +* Description : Liste l'ensemble des registres appartenant à un groupe. * +* * +* Retour : Liste de noms à libérer de la mémoire après utilisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char **g_binary_debugger_get_register_names(const GBinaryDebugger *debugger, const char *group, size_t *count) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_reg_names(debugger, group, count); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* name = désignation du registre visé. * +* * +* Description : Indique la taille associée à un registre donné. * +* * +* Retour : Taille en bits, ou 0 si le registre n'a pas été trouvé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +unsigned int g_binary_debugger_get_register_size(const GBinaryDebugger *debugger, const char *name) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_reg_size(debugger, name); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 8 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u8(GBinaryDebugger *debugger, const char *reg, uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 16 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u16(GBinaryDebugger *debugger, const char *reg, uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 32 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u32(GBinaryDebugger *debugger, const char *reg, uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 64 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u64(GBinaryDebugger *debugger, const char *reg, uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 64, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 8 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u8(GBinaryDebugger *debugger, const char *reg, const uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 16 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u16(GBinaryDebugger *debugger, const char *reg, const uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 32 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u32(GBinaryDebugger *debugger, const char *reg, const uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 64 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u64(GBinaryDebugger *debugger, const char *reg, const uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 64, value); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DE L'ETAT COURANT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* pc = adresse de l'instruction courante. [OUT] * +* * +* Description : Détermine le point d'exécution courant. * +* * +* Retour : Bilan de la récupération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_get_current_pc(GBinaryDebugger *debugger, virt_t *pc) +{ + bool result; /* Bilan à retourner */ + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_current_pc(debugger, pc); + + if (!result) + log_variadic_message(LMT_WARNING, "Unable to get the current PC!"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* pc = adresse de l'instruction de retour d'appel. [OUT] * +* * +* Description : Détermine l'adresse du premier retour d'appel. * +* * +* Retour : Bilan de la récupération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_get_return_pc(GBinaryDebugger *debugger, virt_t *pc) +{ + bool result; /* Bilan à retourner */ + virt_t *callstack; /* Pile d'appels courante */ + size_t size; /* Hauteur de cette pile */ + + result = g_binary_debugger_get_call_stack(debugger, &callstack, &size); + + if (result && size > 0) + *pc = callstack[size - 1]; + + if (callstack != NULL) + free(callstack); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* over = indique si les appels doivent être sautés ou non. * +* count = nombre d'adresses identifiées. * +* * +* Description : Détermine les prochaines probables instructions exécutées. * +* * +* Retour : Liste d'adresses à libérer de la mémoire après usage. * +* * +* Remarques : - * +* * +******************************************************************************/ + +virt_t *g_binary_debugger_get_next_pcs(GBinaryDebugger *debugger, virt_t pc, bool over, size_t *count) +{ + virt_t *result; /* Liste à retourner */ + GArchProcessor *proc; /* Processeur lié au binaire */ + vmpa2t addr; /* Localisation à cibler */ + instr_iter_t *iter; /* Parcours local d'adresses */ + GArchInstruction *instr; /* Instruction correspondante */ + virt_t ret; /* Adresse de retour d'appel */ + instr_link_t *dests; /* Instr. visées par une autre */ + size_t dcount; /* Nombre de liens de dest. */ + size_t i; /* Boucle de parcours */ + const mrange_t *range; /* Emplacement d'instruction */ + + result = NULL; + *count = 0; + + proc = g_loaded_binary_get_processor(debugger->binary); + + init_vmpa(&addr, VMPA_NO_PHYSICAL, pc); + iter = g_arch_processor_get_iter_from_address(proc, &addr); + + if (iter != NULL) + { + instr = get_instruction_iterator_current(iter); + + /* Si l'instruction est un retour à l'appelant */ + if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT) + { + if (g_binary_debugger_get_return_pc(debugger, &ret)) + { + *count = 1; + result = (virt_t *)malloc(sizeof(virt_t)); + + result[0] = ret; + + } + + } + + /* Sinon on se penche sur ses destinations */ + else + { + g_arch_instruction_rlock_dest(instr); + + dcount = g_arch_instruction_get_destinations(instr, &dests); + + for (i = 0; i < dcount; i++) + switch (dests[i].type) + { + case ILT_EXEC_FLOW: + case ILT_JUMP: + case ILT_CASE_JUMP: + case ILT_JUMP_IF_TRUE: + case ILT_JUMP_IF_FALSE: + case ILT_LOOP: + + (*count)++; + result = (virt_t *)realloc(result, *count * sizeof(virt_t)); + + range = g_arch_instruction_get_range(dests[i].linked); + + result[*count - 1] = get_virt_addr(get_mrange_addr(range)); + break; + + case ILT_CALL: + + if (!over) + { + (*count)++; + result = (virt_t *)realloc(result, *count * sizeof(virt_t)); + + range = g_arch_instruction_get_range(dests[i].linked); + + result[*count - 1] = get_virt_addr(get_mrange_addr(range)); + + } + + break; + + default: + break; + + } + + g_arch_instruction_runlock_dest(instr); + + /* Si tout ça ne donne rien, on se rabat sur l'instruction suivante par défaut */ + if (*count == 0) + { + g_object_unref(G_OBJECT(instr)); + + instr = get_instruction_iterator_next(iter); + + if (instr != NULL) + { + *count = 1; + result = (virt_t *)malloc(sizeof(virt_t)); + + range = g_arch_instruction_get_range(instr); + + result[0] = get_virt_addr(get_mrange_addr(range)); + + } + + } + + } + + if (instr != NULL) + g_object_unref(G_OBJECT(instr)); + + delete_instruction_iterator(iter); + + } + + g_object_unref(G_OBJECT(proc)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* callstack = pile d'appels reconstituée. [OUT] * +* size = taille de cette pile. [OUT] * +* * +* Description : Remonte la pile d'appels jusqu'au point courant. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_get_call_stack(GBinaryDebugger *debugger, virt_t **callstack, size_t *size) +{ + bool result; /* Bilan à retourner */ + + *callstack = NULL; + *size = 0; + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_call_stack(debugger, callstack, size); + + if (!result) + { + if (*callstack != NULL) + free(*callstack); + + *callstack = NULL; + *size = 0; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* pc = adresse de l'instruction courante. * +* * +* Description : Réagit à un arrêt du flot d'exécution. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void on_binary_debugger_stopped(GBinaryDebugger *debugger, virt_t pc) +{ + pid_t tid; /* Identifiant du thread */ + bool auto_resume; /* Poursuite automatique ? */ + + tid = 1; // FIXME + + auto_resume = g_binary_debugger_update_breakpoints_on_stop(debugger, tid, pc); + + if (!auto_resume) + g_signal_emit_by_name(debugger, "stopped", pc); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* pid = éventuel identifiant de processus concerné ou -1. * +* * +* Description : Réagit à la fin de l'opération de débogage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void on_binary_debugger_finished(GBinaryDebugger *debugger, pid_t pid) +{ + + /* TODO : libérer de la mémoire tous les BP */ + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION DES POINTS D'ARRET */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* addr = emplacement du point mémoire à traiter. * +* origin = origine de la création du point d'arrêt. * +* tid = identifiant du thread concerné. * +* previous = éventuelle adresse précédent celle du point. * +* * +* Description : Active un point d'arrêt à un emplacement de mémoire donné. * +* * +* Retour : true si la demande a été prise en compte, false sinon. * +* * +* Remarques : L'accès à la liste doit être placée sous la protection de * +* l'appelant. * +* * +******************************************************************************/ + +static bool g_binary_debugger_insert_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr, RawBpOrigin origin, dbg_thread_id_t tid, virt_t previous) +{ + bool result; /* Bilan à retourner */ + size_t index; /* Indice de ce point d'arrêt */ + bool found; /* Présence d'un point d'arrêt */ + raw_breakpoint *bp; /* Point d'arrêt à constituer */ + + result = false; + + found = bsearch_index(&addr, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoint_with_addr, &index); + + if (found) + { + bp = debugger->bpoints[index]; + result = true; + } + + else + { + bp = G_BINARY_DEBUGGER_GET_CLASS(debugger)->enable_bp(debugger, addr); + + if (bp != NULL) + { + debugger->bpoints = (raw_breakpoint **)qinsert(debugger->bpoints, &debugger->bp_count, + sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoints, &bp); + result = true; + } + + } + + if (result) + set_raw_breakpoint_origin(bp, origin, tid, previous); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* index = indice du point à supprimer. * +* * +* Description : Désactive un point d'arrêt à un emplacement de mémoire donné.* +* * +* Retour : true si la demande a été prise en compte, false sinon. * +* * +* Remarques : L'accès à la liste doit être placée sous la protection de * +* l'appelant. * +* * +******************************************************************************/ + +static bool g_binary_debugger_remove_memory_breakpoint(GBinaryDebugger *debugger, size_t index) +{ + bool result; /* Bilan à retourner */ + raw_breakpoint *bp; /* Point d'arrêt à manipuler */ + + result = false; + + assert(index < debugger->bp_count); + + bp = debugger->bpoints[index]; + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->disable_bp(debugger, bp); + + if (result) + { + debugger->bpoints = (raw_breakpoint **)_qdelete(debugger->bpoints, &debugger->bp_count, + sizeof(raw_breakpoint *), index); + + fini_raw_breakpoint(bp); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* tid = identifiant du thread concerné. * +* pc = adresse de l'instruction courante. * +* type = type de point d'arrêt à insérer. * +* over = indique si les appels doivent être sautés ou non. * +* * +* Description : Sème des points d'arrêt sur les instructions suivantes. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_debugger_spread_breakpoints(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t pc, RawBpOrigin type, bool over) +{ + bool result; /* Bilan à retourner */ + virt_t *next_list; /* Liste de points d'arrêts */ + size_t next_count; /* Taille de cette liste */ + GArchProcessor *proc; /* Processeur lié au binaire */ + size_t i; /* Boucle de parcours */ + vmpa2t addr; /* Format d'adresse complet */ + GArchInstruction *instr; /* Instruction ciblée */ + bool valid; /* Point d'arrêt pertinent ? */ + const char *encoding; /* Encodage de l'instruction */ + + result = true; + + next_list = g_binary_debugger_get_next_pcs(debugger, pc, over, &next_count); + + if (next_count == 0) + log_variadic_message(LMT_WARNING, "No instruction found to break after 0x%" PRIx64, pc); + + proc = g_loaded_binary_get_processor(debugger->binary); + + /** + * Le verrou sur la liste des points est normalement déjà posé. + */ + + for (i = 0; i < next_count && result; i++) + { + /** + * Des données peuvent suivre du code (typiquement en ARM). + * On réalise une validation minimale au préalable donc. + */ + + init_vmpa(&addr, VMPA_NO_PHYSICAL, next_list[i]); + instr = g_arch_processor_find_instr_by_address(proc, &addr); + + if (instr == NULL) + valid = false; + + else + { + encoding = g_arch_instruction_get_encoding(instr); + + valid = strcmp(encoding, _("String")) != 0 + && strcmp(encoding, _("Raw")) != 0 + && strcmp(encoding, _("Undefined")) != 0; + + g_object_unref(G_OBJECT(instr)); + + } + + if (valid) + result = g_binary_debugger_insert_memory_breakpoint(debugger, next_list[i], type, tid, pc); + + } + + if (next_list != NULL) + free(next_list); + + g_object_unref(G_OBJECT(proc)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* tid = identifiant du thread concerné. * +* prev = adresse d'instruction qui a conduit à des poses. * +* type = type de point d'arrêt à insérer. * +* * +* Description : Retire tous les points d'arrêt issus d'un adresse. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_debugger_remove_same_breakpoints(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t prev, RawBpOrigin type) +{ + size_t i; /* Boucle de parcours */ + raw_breakpoint *bp; /* Confort de l'accès rapide */ + + /** + * Le verrou sur la liste des points est normalement déjà posé. + */ + + for (i = 0; i < debugger->bp_count; i++) + { + bp = debugger->bpoints[i]; + + if (has_raw_breakpoint_previous_address(bp, type, tid, prev)) + unset_raw_breakpoint_origin(bp, type, tid); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* tid = identifiant du thread concerné. * +* pc = adresse de l'instruction courante. * +* * +* Description : Met à jour les points d'arrêt suite à un arrêt. * +* * +* Retour : true si l'exécution a été relancée automatiquement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_debugger_update_breakpoints_on_stop(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t pc) +{ + bool result; /* Indication à faire remonter */ + size_t index; /* Indice de ce point d'arrêt */ + bool found; /* Présence d'un point d'arrêt */ + raw_breakpoint *bp; /* Confort de l'accès rapide */ + virt_t previous; /* Adresse d'origine */ + bool status; /* Bilan d'un retrait */ + size_t i; /* Boucle de parcours */ + virt_t addr; /* Localisation d'un point */ + + result = false; + + g_rw_lock_writer_lock(&debugger->bp_lock); + + found = bsearch_index(&pc, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoint_with_addr, &index); + + if (found) + { + bp = debugger->bpoints[index]; + + /* S'il s'agissait d'un point d'arrêt à usage interne */ + if (has_raw_breakpoint_origin(bp, RBO_INTERNAL, tid)) + { + previous = get_raw_breakpoint_prev_addr(bp); + assert(previous != VMPA_NO_VIRTUAL); + + g_binary_debugger_remove_same_breakpoints(debugger, tid, previous, RBO_INTERNAL); + + assert(!has_raw_breakpoint_origin(bp, RBO_INTERNAL, tid)); + + result = true; + + } + + /* S'il s'agissait d'un arrêt demandé par l'utilisateur */ + if (has_raw_breakpoint_origin(bp, RBO_USER, tid)) + { + status = G_BINARY_DEBUGGER_GET_CLASS(debugger)->disable_bp(debugger, bp); + + if (!status) + log_variadic_message(LMT_ERROR, "Error while removing the breakpoint at 0x%" PRIx64, pc); + + g_binary_debugger_spread_breakpoints(debugger, tid, pc, RBO_INTERNAL, false); + + result = false; + + } + + /* S'il s'agissait d'une progression pas à pas */ + if (has_raw_breakpoint_origin(bp, RBO_STEP, tid)) + { + previous = get_raw_breakpoint_prev_addr(bp); + assert(previous != VMPA_NO_VIRTUAL); + + g_binary_debugger_remove_same_breakpoints(debugger, tid, previous, RBO_STEP); + + assert(!has_raw_breakpoint_origin(bp, RBO_STEP, tid)); + + result = false; + + } + + /* En conclusion, on supprime les points inutiles */ + for (i = 0; i < debugger->bp_count; ) + { + bp = debugger->bpoints[i]; + + if (is_breakpoint_useless(bp)) + { + status = g_binary_debugger_remove_memory_breakpoint(debugger, i); + + if (!status) + { + addr = get_raw_breakpoint_addr(bp); + log_variadic_message(LMT_ERROR, "Error while removing the breakpoint at 0x%" PRIx64, addr); + i++; + } + + } + + else i++; + + } + + } + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + if (result) + G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* addr = emplacement du point mémoire à traiter. * +* * +* Description : Ajoute un point d'arrêt basique en mémoire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_add_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr) +{ + bool result; /* Bilan à retourner */ + dbg_thread_id_t tid; /* Identifiant de thread */ + + tid = 1;/// FIXME + + g_rw_lock_writer_lock(&debugger->bp_lock); + + result = g_binary_debugger_insert_memory_breakpoint(debugger, addr, RBO_USER, tid, VMPA_NO_VIRTUAL); + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + if (result) + g_signal_emit_by_name(debugger, "mem-bp-handled", true, addr); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* addr = emplacement du point mémoire à traiter. * +* * +* Description : Retire un point d'arrêt basique en mémoire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_delete_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr) +{ + bool result; /* Bilan à retourner */ + size_t index; /* Indice de ce point d'arrêt */ + raw_breakpoint *bp; /* Confort de l'accès rapide */ + dbg_thread_id_t tid; /* Identifiant de thread */ + + g_rw_lock_writer_lock(&debugger->bp_lock); + + result = bsearch_index(&addr, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoint_with_addr, &index); + + if (result) + { + bp = debugger->bpoints[index]; + + tid = 1;/// FIXME + + result = has_raw_breakpoint_origin(bp, RBO_USER, tid); + + if (result) + { + unset_raw_breakpoint_origin(bp, RBO_USER, tid); + + if (is_breakpoint_useless(bp)) + result = g_binary_debugger_remove_memory_breakpoint(debugger, index); + + } + + } + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + if (result) + g_signal_emit_by_name(debugger, "mem-bp-handled", false, addr); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONTROLE DU FLOT D'EXECUTION */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à redémarrer. * +* * +* Description : Redémarre le processus de débogage. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_restart(GBinaryDebugger *debugger) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->restart(debugger); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* * +* Description : Remet en marche le débogueur courant. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_resume(GBinaryDebugger *debugger) +{ + bool result; /* Bilan à retourner */ + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* over = indique si les appels doivent être sautés ou non. * +* * +* Description : Relance l'exécution pour une seule instruction. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_stepi(GBinaryDebugger *debugger, bool over) +{ + bool result; /* Bilan à retourner */ + virt_t pc; /* Position courante */ + dbg_thread_id_t tid; /* Identifiant de thread */ + + result = g_binary_debugger_get_current_pc(debugger, &pc); + + if (result) + { + tid = 1;/// FIXME + + g_rw_lock_writer_lock(&debugger->bp_lock); + + result = g_binary_debugger_spread_breakpoints(debugger, tid, pc, RBO_STEP, over); + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + } + + if (result) + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger); + + return result; } diff --git a/src/debug/debugger.h b/src/debug/debugger.h index 6553b6d..1050e4e 100644 --- a/src/debug/debugger.h +++ b/src/debug/debugger.h @@ -27,51 +27,23 @@ #include #include -#include +#include "misc.h" #include "../analysis/binary.h" #include "../arch/archbase.h" -/* Liste de tous les débogueurs */ -typedef enum _DebuggerType -{ - DGT_JDWP, /* Utilisation du JDWP */ - DGT_REMOTE_GDB, /* Utilisation de GDB */ - DGT_PTRACE, /* Utilisation de ptrace() */ - - DGT_COUNT2/* FIXME */ - -} DebuggerType; - - -/* Définition d'une frame */ -typedef struct _dbg_frame_t -{ - - vmpa_t addr; /* Position dans le code */ - -} dbg_frame_t; - - - -/* Transmission des valeurs des registres */ -typedef struct _register_value -{ - const char *name; /* Nom à ne pas libérer */ - uint64_t value; /* Valeur (taille maximale) */ - -} register_value; +/* ---------------------------- TRONC COMMUN DE DEBOGAGE ---------------------------- */ #define G_TYPE_BINARY_DEBUGGER g_binary_debugger_get_type() #define G_BINARY_DEBUGGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_binary_debugger_get_type(), GBinaryDebugger)) #define G_IS_BINARY_DEBUGGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_binary_debugger_get_type())) -#define G_BINARY_DEBUGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BINARY_DEBUGGER, GGBinaryDebuggerClass)) +#define G_BINARY_DEBUGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BINARY_DEBUGGER, GBinaryDebuggerClass)) #define G_IS_BINARY_DEBUGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BINARY_DEBUGGER)) -#define G_BINARY_DEBUGGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BINARY_DEBUGGER, GGBinaryDebuggerClass)) +#define G_BINARY_DEBUGGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BINARY_DEBUGGER, GBinaryDebuggerClass)) /* Définition des fonctionnalités d'un débogueur (instance) */ @@ -84,9 +56,6 @@ typedef struct _GBinaryDebuggerClass GBinaryDebuggerClass; /* Indique le type définit par la GLib pour le débogueur ptrace(). */ GType g_binary_debugger_get_type(void); -/* Crée un nouveau débogueur. */ -GBinaryDebugger *g_new_binary_debugger(DebuggerType, GLoadedBinary *); - /* Démarre une procédure de débogage. */ bool g_binary_debugger_attach(GBinaryDebugger *); @@ -94,19 +63,165 @@ bool g_binary_debugger_attach(GBinaryDebugger *); void g_binary_debugger_run(GBinaryDebugger *); /* Reprend une procédure de débogage. */ -void g_binary_debugger_resume(GBinaryDebugger *); +//void g_binary_debugger_resume(GBinaryDebugger *); /* Tue une procédure de débogage. */ void g_binary_debugger_kill(GBinaryDebugger *); /* Fournit les identifiants de tous les threads actifs. */ -pid_t *g_binary_debugger_list_all_threads(GBinaryDebugger *, char ***, size_t *); +//pid_t *g_binary_debugger_list_all_threads(GBinaryDebugger *, char ***, size_t *); /* Fournit la liste des frames courantes d'un thread donné. */ -dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *, pid_t, size_t *); +//dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *, pid_t, size_t *); /* Fournit la valeur des registres de l'architecture. */ -register_value *g_binary_debugger_get_registers(GBinaryDebugger *, size_t *); +//register_value *g_binary_debugger_get_registers(GBinaryDebugger *, size_t *); + + + + + + + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES DIFFERENTS THREADS ACTIFS */ +/* ---------------------------------------------------------------------------------- */ + + +/* Description humaine à représenter */ +typedef struct _dbg_thread_desc +{ + dbg_thread_id_t id; /* Identifiant interne */ + char *name; /* Dénomination humaine */ + +} dbg_thread_desc; + + +/* Libère la mémoire d'une liste de threads actifs. */ +void delete_dbg_thread_desc(dbg_thread_desc *, size_t); + +/* Fournit les identifiants de tous les threads actifs. */ +dbg_thread_desc *g_binary_debugger_list_all_threads(GBinaryDebugger *, size_t *); + + + + + + + + + +/* --------------------------- ENTREES / SORTIES BASIQUES --------------------------- */ + + +/* Lit une valeur de 8 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u8(GBinaryDebugger *, virt_t, uint8_t *); + +/* Lit une valeur de 16 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u16(GBinaryDebugger *, virt_t, uint16_t *); + +/* Lit une valeur de 32 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u32(GBinaryDebugger *, virt_t, uint32_t *); + +/* Lit une valeur de 64 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u64(GBinaryDebugger *, virt_t, uint64_t *); + +/* Lit une valeur de taille quelconque à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_data(GBinaryDebugger *, virt_t, uint8_t *, size_t); + +/* Ecrit une valeur de 8 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u8(GBinaryDebugger *, virt_t, const uint8_t *); + +/* Ecrit une valeur de 16 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u16(GBinaryDebugger *, virt_t, const uint16_t *); + +/* Ecrit une valeur de 32 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u32(GBinaryDebugger *, virt_t, const uint32_t *); + +/* Ecrit une valeur de 64 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u64(GBinaryDebugger *, virt_t, const uint64_t *); + +/* Ecrit une valeur de taille quelconque à une adresse donnée. */ +bool g_binary_debugger_write_memory_data(GBinaryDebugger *, virt_t, const uint8_t *, size_t); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +char **g_binary_debugger_get_register_names(const GBinaryDebugger *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +unsigned int g_binary_debugger_get_register_size(const GBinaryDebugger *, const char *); + +/* Lit une valeur de 8 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u8(GBinaryDebugger *, const char *, uint8_t *); + +/* Lit une valeur de 16 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u16(GBinaryDebugger *, const char *, uint16_t *); + +/* Lit une valeur de 32 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u32(GBinaryDebugger *, const char *, uint32_t *); + +/* Lit une valeur de 64 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u64(GBinaryDebugger *, const char *, uint64_t *); + +/* Ecrit une valeur de 8 bits dans un registre. */ +bool g_binary_debugger_write_register_u8(GBinaryDebugger *, const char *, const uint8_t *); + +/* Ecrit une valeur de 16 bits dans un registre. */ +bool g_binary_debugger_write_register_u16(GBinaryDebugger *, const char *, const uint16_t *); + +/* Ecrit une valeur de 32 bits dans un registre. */ +bool g_binary_debugger_write_register_u32(GBinaryDebugger *, const char *, const uint32_t *); + +/* Ecrit une valeur de 64 bits dans un registre. */ +bool g_binary_debugger_write_register_u64(GBinaryDebugger *, const char *, const uint64_t *); + + + +/* ------------------------- MANIPULATION DE L'ETAT COURANT ------------------------- */ + + +/* Détermine le point d'exécution courant. */ +bool g_binary_debugger_get_current_pc(GBinaryDebugger *, virt_t *); + +/* Détermine l'adresse du premier retour d'appel. */ +bool g_binary_debugger_get_return_pc(GBinaryDebugger *, virt_t *); + +/* Détermine les prochaines probables instructions exécutées. */ +virt_t *g_binary_debugger_get_next_pcs(GBinaryDebugger *, virt_t, bool, size_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +bool g_binary_debugger_get_call_stack(GBinaryDebugger *, virt_t **, size_t *); + + + +/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ + + +/* Ajoute un point d'arrêt basique en mémoire. */ +bool g_binary_debugger_add_memory_breakpoint(GBinaryDebugger *, virt_t); + +/* Retire un point d'arrêt basique en mémoire. */ +bool g_binary_debugger_delete_memory_breakpoint(GBinaryDebugger *, virt_t); + + + +/* -------------------------- CONTROLE DU FLOT D'EXECUTION -------------------------- */ + + +/* Redémarre le processus de débogage. */ +bool g_binary_debugger_restart(GBinaryDebugger *); + +/* Remet en marche le débogueur courant. */ +bool g_binary_debugger_resume(GBinaryDebugger *); + +/* Relance l'exécution pour une seule instruction. */ +bool g_binary_debugger_stepi(GBinaryDebugger *, bool); 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 . + */ + + +#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 . + */ + + +#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 . + */ + + +#include "gdb.h" + + + +#include +#include +#include +#include +#include + + +#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 . + */ + + +#ifndef _DEBUG_GDBRSP_GDB_H +#define _DEBUG_GDBRSP_GDB_H + + +#include + + +#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 . + */ + + +#include "helpers.h" + + +#include +#include + + +#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 . + */ + + +#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 . + */ + + +#include "helpers_arm.h" + + +#include +#include + + +#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 . + */ + + +#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 . + */ + + +#include "helpers_arm64.h" + + +#include + + +#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 . + */ + + +#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 . + */ + + +#include "packet.h" + + +#include +#include + + +#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 . + */ + + +#ifndef _DEBUG_GDBRSP_PACKET_H +#define _DEBUG_GDBRSP_PACKET_H + + +#include +#include +#include + + + +#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 . + */ + + +#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 . + */ + + +#include "stream.h" + + +#include +#include +#include +#include +#include +#include + + +#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 +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 . + */ + + +#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 . + */ + + +#include "support.h" + + +#include +#include + + + +/* 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 + +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... 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 . + */ + + +#ifndef _DEBUG_GDBRSP_SUPPORT_H +#define _DEBUG_GDBRSP_SUPPORT_H + + +#include +#include + + +#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 . + */ + + +#include "target.h" + + +#include +#include +#include +#include +#include + + +#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 + * + */ + + 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 . + */ + + +#ifndef _DEBUG_GDBRSP_TARGET_H +#define _DEBUG_GDBRSP_TARGET_H + + +#include +#include + + +#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 . + */ + + +#include "tcp.h" + + +#include +#include +#include +#include +#include +#include + + +#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 . + */ + + +#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 . + */ + + +#include "utils.h" + + +#include +#include +#include +#include +#include +#include + + + +/****************************************************************************** +* * +* 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 . + */ + + +#ifndef _DEBUG_GDBRSP_UTILS_H +#define _DEBUG_GDBRSP_UTILS_H + + +#include +#include +#include + + + +/* 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 */ diff --git a/src/debug/misc.h b/src/debug/misc.h new file mode 100644 index 0000000..dd9492a --- /dev/null +++ b/src/debug/misc.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * misc.h - prototypes pour les définitions communes et génériques liées au débogage + * + * 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 . + */ + + +#ifndef _DEBUG_MISC_H +#define _DEBUG_MISC_H + + +#include + + + +/* Couverture maximale des identifiants */ +typedef uint64_t dbg_thread_id_t; + + + +#endif /* _DEBUG_MISC_H */ diff --git a/src/debug/remgdb/Makefile.am b/src/debug/remgdb/Makefile.am deleted file mode 100644 index 47e5fdc..0000000 --- a/src/debug/remgdb/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ - -noinst_LTLIBRARIES = libdebugremgdb.la - -libdebugremgdb_la_SOURCES = \ - gdb.h gdb.c \ - helpers.h helpers.c \ - packet.h packet.c \ - stream-int.h \ - stream.h stream.c \ - tcp.h tcp.c - -libdebugremgdb_la_CFLAGS = $(AM_CFLAGS) - - -AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) - -AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) diff --git a/src/debug/remgdb/gdb.c b/src/debug/remgdb/gdb.c deleted file mode 100644 index 728eee6..0000000 --- a/src/debug/remgdb/gdb.c +++ /dev/null @@ -1,363 +0,0 @@ - -/* 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 . - */ - - -#include "gdb.h" - - -#include "../debugger-int.h" - - - -#include "helpers.h" -#include "tcp.h" - - - - -/* Débogueur utilisant un serveur GDB (instance) */ -struct _GGdbDebugger -{ - GBinaryDebugger parent; /* A laisser en premier */ - - GGdbStream *stream; /* Flux de communication */ - - -#if 0 - GCond *cond; /* Poursuite du déroulement */ - GMutex *mutex; /* Accès à la condition */ - - ptrace_options *options; /* Configuration du débogage */ - - pid_t child; /* Processus suivi lancé */ - - gboolean run_again; /* Reprise du débogage */ -#endif -}; - -/* Débogueur utilisant un serveur GDB (classe) */ -struct _GGdbDebuggerClass -{ - GBinaryDebuggerClass parent; /* A laisser en premier */ - -}; - - - - - -/* 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 *); - - -/* 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 *); - - - -/* 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) -{ - -} - - -/****************************************************************************** -* * -* 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(); - -} - - -/****************************************************************************** -* * -* Paramètres : binary = binaire représenter à déboguer. * -* options = paramètres destinés au débogage. * -* * -* 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, void *options) -{ - GBinaryDebugger *result; /* Débogueur à retourner */ - - result = g_object_new(G_TYPE_GDB_DEBUGGER, NULL); - - return result; - -} - - - - - - - - -/****************************************************************************** -* * -* 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"); - 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 : 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) -{ - - - /* - g_mutex_lock(debugger->mutex); - debugger->run_again = TRUE; - g_cond_signal(debugger->cond); - g_mutex_unlock(debugger->mutex); - */ - 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; - -} - - - - - - - - - - - - -void test_gdb(void) -{ - - GGdbStream *stream; - - GGdbPacket *packet; - - bool test; - - const char *data; - size_t len; - - printf("GDB !!!!\n"); - - - stream = g_gdb_tcp_client_new("192.168.100.141", "6666"); - - - - packet = g_gdb_stream_get_free_packet(stream); - - g_gdb_packet_start_new_command(packet); - g_gdb_packet_append(packet, "g"); - - - test = g_gdb_stream_send_packet(stream, packet); - - - - printf(" >> Paquet '%s' bien envoyé ? %s\n", "g", 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); - - - - -} diff --git a/src/debug/remgdb/gdb.h b/src/debug/remgdb/gdb.h deleted file mode 100644 index 38aa8fe..0000000 --- a/src/debug/remgdb/gdb.h +++ /dev/null @@ -1,61 +0,0 @@ - -/* 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 . - */ - - -#ifndef _DEBUG_REMGDB_GDB_H -#define _DEBUG_REMGDB_GDB_H - - -#include - - -#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 *, void *); - - -void test_gdb(void); - - - -#endif /* _DEBUG_REMGDB_GDB_H */ diff --git a/src/debug/remgdb/helpers.c b/src/debug/remgdb/helpers.c deleted file mode 100644 index bae7f0b..0000000 --- a/src/debug/remgdb/helpers.c +++ /dev/null @@ -1,139 +0,0 @@ - -/* 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 . - */ - - -#include "helpers.h" - - -#include -#include - - - - - - - - - - - - - - - - -/* -------------------------- 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/remgdb/helpers.h b/src/debug/remgdb/helpers.h deleted file mode 100644 index d68fc11..0000000 --- a/src/debug/remgdb/helpers.h +++ /dev/null @@ -1,61 +0,0 @@ - -/* 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 . - */ - - -#ifndef _DEBUG_REMGDB_HELPERS_H -#define _DEBUG_REMGDB_HELPERS_H - - -#include "packet.h" - - -#include "../../arch/archbase.h" -#include "../../common/endianness.h" - - - - - - - -/* -------------------------- 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_REMGDB_HELPERS_H */ diff --git a/src/debug/remgdb/packet.c b/src/debug/remgdb/packet.c deleted file mode 100644 index 8d798c6..0000000 --- a/src/debug/remgdb/packet.c +++ /dev/null @@ -1,383 +0,0 @@ - -/* 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 . - */ - - -#include "packet.h" - - -#include -#include - - -#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; - } - - 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); - - 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. [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; - *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/remgdb/packet.h b/src/debug/remgdb/packet.h deleted file mode 100644 index 9c44108..0000000 --- a/src/debug/remgdb/packet.h +++ /dev/null @@ -1,82 +0,0 @@ - -/* 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 . - */ - - -#ifndef _DEBUG_REMGDB_PACKET_H -#define _DEBUG_REMGDB_PACKET_H - - -#include -#include -#include - - - -#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_REMGDB_PACKET_H */ diff --git a/src/debug/remgdb/stream-int.h b/src/debug/remgdb/stream-int.h deleted file mode 100644 index 8ac422d..0000000 --- a/src/debug/remgdb/stream-int.h +++ /dev/null @@ -1,74 +0,0 @@ - -/* 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 . - */ - - -#ifndef _DEBUG_REMGDB_STREAM_INT_H -#define _DEBUG_REMGDB_STREAM_INT_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. */ - - 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 */ - -}; - - -/* 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_REMGDB_STREAM_INT_H */ diff --git a/src/debug/remgdb/stream.c b/src/debug/remgdb/stream.c deleted file mode 100644 index cd303c3..0000000 --- a/src/debug/remgdb/stream.c +++ /dev/null @@ -1,402 +0,0 @@ - -/* 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 . - */ - - -#include "stream.h" - - -#include -#include -#include -#include - - -#include "stream-int.h" -#include "../../common/dllist.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 *); - -/* 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 *); - -/* 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) -{ - -} - - -/****************************************************************************** -* * -* 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); - -} - - -/****************************************************************************** -* * -* 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; - - 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) -{ - 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 */ - - while (1) - { - FD_ZERO(&rfds); - FD_SET(stream->fd, &rfds); - - ret = select(stream->fd + 1, &rfds, NULL, NULL, NULL); - - printf("ret :: %d\n", ret); - - 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 (!gdb_stream_ack(stream)) goto bad_recv; - - /* On conserve le résultat */ - - 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); - - } - - bad_recv: - - printf("bad things happend...\n"); - - g_gdb_stream_mark_packet_as_free(stream, packet); - - break; - - } - - } - - 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); - - 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) -{ - 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é */ - - result = stream->recv_byte(stream, tmp); - - if (tmp[0] != '$') return false; - - tmp[1] = '\0'; - - while ((result = stream->recv_byte(stream, tmp))) - { - 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 : - * -* * -******************************************************************************/ - -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 */ - - result = stream->send_data(stream, "$", 1); - - g_gdb_packet_compute_checksum(packet); - g_gdb_packet_get_data(packet, &data, &len, &checksum); - - result &= stream->send_data(stream, data, len); - - result = stream->send_data(stream, "#", 1); - - snprintf(tmp, 3, "%hhx", 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/remgdb/stream.h b/src/debug/remgdb/stream.h deleted file mode 100644 index f93ff60..0000000 --- a/src/debug/remgdb/stream.h +++ /dev/null @@ -1,65 +0,0 @@ - -/* 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 . - */ - - -#ifndef _DEBUG_REMGDB_STREAM_H -#define _DEBUG_REMGDB_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); - -/* 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_REMGDB_STREAM_H */ diff --git a/src/debug/remgdb/tcp.c b/src/debug/remgdb/tcp.c deleted file mode 100644 index aa10c54..0000000 --- a/src/debug/remgdb/tcp.c +++ /dev/null @@ -1,263 +0,0 @@ - -/* 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 . - */ - - -#include "tcp.h" - - -#include -#include -#include -#include -#include -#include - - -#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. * -* * -* 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) -{ - 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; - - 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) -{ - return (send(G_GDB_STREAM(client)->fd, data, len, 0) == 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) -{ - return (recv(G_GDB_STREAM(client)->fd, data, 1, 0) == 1); - -} diff --git a/src/debug/remgdb/tcp.h b/src/debug/remgdb/tcp.h deleted file mode 100644 index 7d40f16..0000000 --- a/src/debug/remgdb/tcp.h +++ /dev/null @@ -1,56 +0,0 @@ - -/* 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 . - */ - - -#ifndef _DEBUG_REMGDB_TCP_H -#define _DEBUG_REMGDB_TCP_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 *); - - - -#endif /* _DEBUG_REMGDB_TCP_H */ diff --git a/src/gtkext/gtkstatusstack.c b/src/gtkext/gtkstatusstack.c index 51107c5..789661e 100644 --- a/src/gtkext/gtkstatusstack.c +++ b/src/gtkext/gtkstatusstack.c @@ -845,6 +845,8 @@ activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *m progress_info *info; /* Informations à consulter */ size_t new; /* Indice de l'activité créée */ + if (stack == NULL) return 0; + info = stack->prog_info; g_mutex_lock(&info->access); @@ -904,6 +906,8 @@ void gtk_status_stack_extend_activity(GtkStatusStack *stack, activity_id_t id, u progress_info *info; /* Informations à consulter */ size_t i; /* Boucle de parcours */ + if (stack == NULL) return; + info = stack->prog_info; g_mutex_lock(&info->access); @@ -941,6 +945,8 @@ void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, c size_t i; /* Boucle de parcours */ bool msg_changed; /* Changement d'intitulé */ + if (stack == NULL) return; + info = stack->prog_info; g_mutex_lock(&info->access); @@ -1008,6 +1014,8 @@ void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t progress_status *status; /* Raccourci de confort */ double new; /* Nouvelle progression */ + if (stack == NULL) return; + info = stack->prog_info; g_mutex_lock(&info->access); @@ -1061,6 +1069,8 @@ void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id) progress_info *info; /* Informations à consulter */ size_t i; /* Boucle de parcours */ + if (stack == NULL) return; + info = stack->prog_info; g_mutex_lock(&info->access); -- cgit v0.11.2-87-g4458