From 92a73bf3e57e77c877da09ba38c958e61663912a Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Tue, 2 Jun 2020 22:25:13 +0200 Subject: Extended the Python bindings for disassembling contexts. --- plugins/pychrysalide/arch/context.c | 417 +++++++++++++++++++++++++++++++++++- src/arch/context.c | 7 +- src/arch/context.h | 12 +- 3 files changed, 422 insertions(+), 14 deletions(-) diff --git a/plugins/pychrysalide/arch/context.c b/plugins/pychrysalide/arch/context.c index f7c6549..ce1ef01 100644 --- a/plugins/pychrysalide/arch/context.c +++ b/plugins/pychrysalide/arch/context.c @@ -29,15 +29,419 @@ #include -#include +#include +#include +#include #include "constants.h" #include "../access.h" #include "../helpers.h" +#include "../analysis/db/item.h" +#include "../arch/vmpa.h" +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Accompagne la création d'une instance dérivée en Python. */ +static PyObject *py_proc_context_new(PyTypeObject *, PyObject *, PyObject *); + +/* Initialise la classe des contextes de processeur. */ +static void py_proc_context_init_gclass(GProcContextClass *, gpointer); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_proc_context_init(PyObject *, PyObject *, PyObject *); + +/* Ajoute une adresse virtuelle comme point de départ de code. */ +static void py_proc_context_push_drop_point_wrapper(GProcContext *, DisassPriorityLevel, virt_t, va_list); + + + +/* ----------------------------- DEFINITION DE CONTEXTE ----------------------------- */ + + +/* Ajoute une adresse virtuelle comme point de départ de code. */ +static PyObject *py_proc_context_push_drop_point(PyObject *, PyObject *); + +/* Empile une adresse de nouveau symbole à prendre en compte. */ +static PyObject *py_proc_context_push_new_symbol_at(PyObject *, PyObject *); + +/* Note la mise en place d'un élément pendant le désassemblage. */ +static PyObject *py_proc_context_add_db_item(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : type = type du nouvel objet à mettre en place. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* * +* Description : Accompagne la création d'une instance dérivée en Python. * +* * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_proc_context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Objet à retourner */ + PyTypeObject *base; /* Type de base à dériver */ + bool first_time; /* Evite les multiples passages*/ + GType gtype; /* Nouveau type de processeur */ + bool status; /* Bilan d'un enregistrement */ + + /* Validations diverses */ + + base = get_python_proc_context_type(); + + if (type == base) + { + result = NULL; + PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); + goto exit; + } + + /* Mise en place d'un type dédié */ + + first_time = (g_type_from_name(type->tp_name) == 0); + + gtype = build_dynamic_type(G_TYPE_PROC_CONTEXT, type->tp_name, + (GClassInitFunc)py_proc_context_init_gclass, NULL, NULL); + + if (first_time) + { + status = register_class_for_dynamic_pygobject(gtype, type, base); + + if (!status) + { + result = NULL; + goto exit; + } + + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* unused = données non utilisées ici. * +* * +* Description : Initialise la classe des contextes de processeur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_proc_context_init_gclass(GProcContextClass *class, gpointer unused) +{ + class->push_point = py_proc_context_push_drop_point_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_proc_context_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan d'initialisation */ + +#define PROC_CONTEXT_DOC \ + "The ProcContext object is a disassembling companion for" \ + " architecture processors and is usually provided by the" \ + " pychrysalide.arch.ArchProcessor.get_context() method.\n" \ + "\n" \ + "So each kind of processor should have its dedicated context.\n" \ + "\n" \ + "The role of a ProcContext instance is to collect on demand next" \ + " points to process during a disassembling operation.\n" \ + "\n" \ + "The following method may be defined for new classes:\n" \ + "* pychrysalide.arch.ProcContext._push_drop_point();\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " no particular argument." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : ctx = contexte de désassemblage à compléter. * +* level = indication de priorité et d'origine de l'adresse. * +* addr = adresse d'un nouveau point de départ à traiter. * +* ap = éventuelles informations complémentaires. * +* * +* Description : Ajoute une adresse virtuelle comme point de départ de code. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_proc_context_push_drop_point_wrapper(GProcContext *ctx, DisassPriorityLevel level, virt_t addr, va_list ap) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pylevel; /* Priorité en objet Python */ + PyObject *pyextra; /* Argument complémentaire ? */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + GProcContextClass *class; /* Classe parente de l'instance*/ + +#define PROC_CONTEXT_PUSH_DROP_POINT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _push_drop_point, "$self, level, addr, /, extra=None", \ + METH_VARARGS, \ + "Abstract method used to inject a new virtual address to" \ + " disassemble during the disassembling process.\n" \ + "\n" \ + "The priority of this point is given by the" \ + " pychrysalide.arch.ProcContext.DisassPriorityLevel value." \ + " Extra information may also be provided, as a Python object." \ + "\n" \ + "If this method is not defined, the default behavior is to" \ + " inject the point without any further treatment, as if" \ + " *extra* does not carry any valuable information." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(ctx)); + + if (has_python_method(pyobj, "_push_drop_point")) + { + pylevel = cast_with_constants_group_from_type(get_python_proc_context_type(), "DisassPriorityLevel", level); + pyextra = va_arg(ap, PyObject *); + + if (pyextra == NULL) + pyextra = Py_None; + + Py_INCREF(pyextra); + + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, pylevel); + PyTuple_SetItem(args, 1, PyLong_FromUnsignedLongLong(addr)); + PyTuple_SetItem(args, 2, pyextra); + + pyret = run_python_method(pyobj, "_push_drop_point", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + else + { + class = G_PROC_CONTEXT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(G_OBJECT(ctx)))); + + assert(class->push_point != NULL); + class->push_point(ctx, level, addr, ap); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION DE CONTEXTE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet représentant un contexte de désasssemblage. * +* args = arguments fournis pour l'opération. * +* * +* Description : Ajoute une adresse virtuelle comme point de départ de code. * +* * +* Retour : - * +* * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_proc_context_push_drop_point(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + DisassPriorityLevel level; /* Niveau de priorité */ + unsigned long long addr; /* Adresse virtuelle à traiter */ + PyObject *extra; /* Eventuel complément d'info. */ + int ret; /* Bilan de lecture des args. */ + GProcContext *ctx; /* Contexte de désassemble */ + +#define PROC_CONTEXT_PUSH_DROP_POINT_METHOD PYTHON_METHOD_DEF \ +( \ + push_drop_point, "$self, level, addr, /, extra=None", \ + METH_VARARGS, py_proc_context, \ + "Inject a new virtual address to disassemble during the" \ + " disassembling process.\n" \ + "\n" \ + "The priority of this point is given by the" \ + " pychrysalide.arch.ProcContext.DisassPriorityLevel value." \ + " Extra information may also be provided, as a Python object." \ +) + + extra = Py_None; + + ret = PyArg_ParseTuple(args, "O&K|O", convert_to_disass_priority_level, &level, &addr, &extra); + if (!ret) return NULL; + + ctx = G_PROC_CONTEXT(pygobject_get(self)); + + g_proc_context_push_drop_point(ctx, level, addr, extra); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet représentant un contexte de désasssemblage. * +* args = arguments fournis pour l'opération. * +* * +* Description : Empile une adresse de nouveau symbole à prendre en compte. * +* * +* Retour : - * +* * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_proc_context_push_new_symbol_at(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + vmpa2t *addr; /* Adresse de symbole à ajouter*/ + int ret; /* Bilan de lecture des args. */ + GProcContext *ctx; /* Contexte de désassemble */ + +#define PROC_CONTEXT_PUSH_NEW_SYMBOL_AT_METHOD PYTHON_METHOD_DEF \ +( \ + push_new_symbol_at, "$self, addr", \ + METH_VARARGS, py_proc_context, \ + "Collect the location of a symbol for the disassembling process.\n" \ + "\n" \ + "This location must be able to get converted into" \ + " a pychrysalide.arch.vmpa instance." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_any_to_vmpa, &addr); + if (!ret) return NULL; + + ctx = G_PROC_CONTEXT(pygobject_get(self)); + + g_proc_context_push_new_symbol_at(ctx, addr); + + clean_vmpa_arg(addr); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet représentant un contexte de désasssemblage. * +* args = arguments fournis pour l'opération. * +* * +* Description : Note la mise en place d'un élément pendant le désassemblage. * +* * +* Retour : - * +* * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_proc_context_add_db_item(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GDbItem *item; /* Elément à ajouter */ + int ret; /* Bilan de lecture des args. */ + GProcContext *ctx; /* Contexte de désassemble */ + +#define PROC_CONTEXT_ADD_DB_ITEM_METHOD PYTHON_METHOD_DEF \ +( \ + add_db_item, "$self, item", \ + METH_VARARGS, py_proc_context, \ + "Collect an extra item to include in the final disassembled" \ + " content.\n" \ + "\n" \ + "The item to consider has to be a pychrysalide.analysis.db.DbItem" \ + " instance." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_db_item, &item); + if (!ret) return NULL; + + ctx = G_PROC_CONTEXT(pygobject_get(self)); + + g_proc_context_add_db_item(ctx, item); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + /****************************************************************************** * * * Paramètres : - * @@ -53,6 +457,10 @@ PyTypeObject *get_python_proc_context_type(void) { static PyMethodDef py_proc_context_methods[] = { + PROC_CONTEXT_PUSH_DROP_POINT_WRAPPER, + PROC_CONTEXT_PUSH_DROP_POINT_METHOD, + PROC_CONTEXT_PUSH_NEW_SYMBOL_AT_METHOD, + PROC_CONTEXT_ADD_DB_ITEM_METHOD, { NULL } }; @@ -69,11 +477,14 @@ PyTypeObject *get_python_proc_context_type(void) .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_BASETYPE, - .tp_doc = "PyChrysalide disassembly context.", + .tp_doc = PROC_CONTEXT_DOC, .tp_methods = py_proc_context_methods, .tp_getset = py_proc_context_getseters, + .tp_init = py_proc_context_init, + .tp_new = py_proc_context_new, + }; return &py_proc_context_type; @@ -103,8 +514,6 @@ bool ensure_python_proc_context_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - APPLY_ABSTRACT_FLAG(type); - module = get_access_to_python_module("pychrysalide.arch"); dict = PyModule_GetDict(module); diff --git a/src/arch/context.c b/src/arch/context.c index 431fdb7..fc68640 100644 --- a/src/arch/context.c +++ b/src/arch/context.c @@ -227,8 +227,7 @@ static void _g_proc_context_push_drop_point(GProcContext *ctx, DisassPriorityLev { ctx->dp_allocated[level] += DP_ALLOC_BLOCK; - ctx->drop_points[level] = (virt_t *)realloc(ctx->drop_points[level], - ctx->dp_allocated[level] * sizeof(virt_t)); + ctx->drop_points[level] = realloc(ctx->drop_points[level], ctx->dp_allocated[level] * sizeof(virt_t)); } @@ -339,7 +338,7 @@ void g_proc_context_push_new_symbol_at(GProcContext *ctx, const vmpa2t *addr) { g_mutex_lock(&ctx->es_access); - ctx->extra_symbols = (vmpa2t *)realloc(ctx->extra_symbols, ++ctx->esyms_count * sizeof(vmpa2t)); + ctx->extra_symbols = realloc(ctx->extra_symbols, ++ctx->esyms_count * sizeof(vmpa2t)); copy_vmpa(&ctx->extra_symbols[ctx->esyms_count - 1], addr); @@ -403,7 +402,7 @@ void g_proc_context_add_db_item(GProcContext *ctx, GDbItem *item) { ctx->items_allocated += DB_ALLOC_SIZE; - ctx->items = (GDbItem **)realloc(ctx->items, sizeof(GDbItem *) * ctx->items_allocated); + ctx->items = realloc(ctx->items, sizeof(GDbItem *) * ctx->items_allocated); } diff --git a/src/arch/context.h b/src/arch/context.h index 255909f..f54419d 100644 --- a/src/arch/context.h +++ b/src/arch/context.h @@ -34,12 +34,12 @@ -#define G_TYPE_PROC_CONTEXT g_proc_context_get_type() -#define G_PROC_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_proc_context_get_type(), GProcContext)) -#define G_IS_PROC_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_proc_context_get_type())) -#define G_PROC_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PROC_CONTEXT, GProcContextClass)) -#define G_IS_PROC_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PROC_CONTEXT)) -#define G_PROC_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PROC_CONTEXT, GProcContextClass)) +#define G_TYPE_PROC_CONTEXT g_proc_context_get_type() +#define G_PROC_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PROC_CONTEXT, GProcContext)) +#define G_IS_PROC_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PROC_CONTEXT)) +#define G_PROC_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PROC_CONTEXT, GProcContextClass)) +#define G_IS_PROC_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PROC_CONTEXT)) +#define G_PROC_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PROC_CONTEXT, GProcContextClass)) /* Définition d'un contexte pour processeur (instance) */ -- cgit v0.11.2-87-g4458