diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2018-06-17 16:11:45 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2018-06-17 16:11:45 (GMT) |
commit | 378be1ab322dce8e8377d692829d6877758e5960 (patch) | |
tree | 17dc518687a45649caa68304cc2a5750a0a50554 /plugins | |
parent | 1f7e9506775f66a3a5f2859779d33b914eee8ef4 (diff) |
Annotated linux kernel syscalls using a new plugin.
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/lnxsyscalls/Makefile.am | 39 | ||||
-rw-r--r-- | plugins/lnxsyscalls/collect.c | 643 | ||||
-rw-r--r-- | plugins/lnxsyscalls/collect.h | 63 | ||||
-rw-r--r-- | plugins/lnxsyscalls/core.c | 168 | ||||
-rw-r--r-- | plugins/lnxsyscalls/core.h | 41 | ||||
-rw-r--r-- | plugins/lnxsyscalls/db.c | 245 | ||||
-rw-r--r-- | plugins/lnxsyscalls/db.h | 52 | ||||
-rw-r--r-- | plugins/lnxsyscalls/hops.h | 67 | ||||
-rw-r--r-- | plugins/lnxsyscalls/hops_armv7.c | 262 | ||||
-rw-r--r-- | plugins/lnxsyscalls/hops_armv7.h | 37 | ||||
-rw-r--r-- | plugins/lnxsyscalls/hunter.c | 346 | ||||
-rw-r--r-- | plugins/lnxsyscalls/hunter.h | 56 | ||||
-rw-r--r-- | plugins/lnxsyscalls/linux-syscalls.db | bin | 0 -> 49152 bytes | |||
-rw-r--r-- | plugins/lnxsyscalls/syscall.c | 108 | ||||
-rw-r--r-- | plugins/lnxsyscalls/syscall.h | 58 | ||||
-rw-r--r-- | plugins/lnxsyscalls/writer.c | 222 | ||||
-rw-r--r-- | plugins/lnxsyscalls/writer.h | 50 |
18 files changed, 2458 insertions, 1 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 25774db..90556de 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -4,4 +4,4 @@ if HAVE_PYTHON3_CONFIG endif # androhelpers -SUBDIRS = arm dalvik devdbg dex dexbnf elf fmtp libcsem mobicore $(PYTHON3_SUBDIRS) readdex readelf readmc ropgadgets +SUBDIRS = arm dalvik devdbg dex dexbnf elf fmtp libcsem lnxsyscalls mobicore $(PYTHON3_SUBDIRS) readdex readelf readmc ropgadgets diff --git a/plugins/lnxsyscalls/Makefile.am b/plugins/lnxsyscalls/Makefile.am new file mode 100644 index 0000000..251c1c9 --- /dev/null +++ b/plugins/lnxsyscalls/Makefile.am @@ -0,0 +1,39 @@ + +lib_LTLIBRARIES = liblnxsyscalls.la + +libdir = $(pluginsdir) + + +liblnxsyscalls_la_SOURCES = \ + collect.h collect.c \ + core.h core.c \ + db.h db.c \ + hops.h \ + hops_armv7.h hops_armv7.c \ + hunter.h hunter.c \ + syscall.h syscall.c \ + writer.h writer.c + +liblnxsyscalls_la_CFLAGS = $(AM_CFLAGS) + +liblnxsyscalls_la_LIBADD = + +liblnxsyscalls_la_LDFLAGS = $(LIBPYTHON_LIBS) $(LIBPYGOBJECT_LIBS) \ + -L$(top_srcdir)/src/.libs -lchrysacore + + +db_DATA = linux-syscalls.db + +dbdir = $(pluginsdir) + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(liblnxsyscalls_la_SOURCES:%c=) + + +AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) + +SUBDIRS = diff --git a/plugins/lnxsyscalls/collect.c b/plugins/lnxsyscalls/collect.c new file mode 100644 index 0000000..a71e833 --- /dev/null +++ b/plugins/lnxsyscalls/collect.c @@ -0,0 +1,643 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * collect.c - collecte de différents registres en remontant le flot d'exécution + * + * 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "collect.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include <arch/operands/register.h> + + +#include "hops.h" + + + +/* Suivi de l'usage d'un registre */ +typedef struct _collected_register +{ + GArchRegister *reg; /* Registre usité à tracer */ + + bool required; /* Registre utile ? */ + + GArchInstruction *written; /* Emplacement d'écriture */ + +} collected_register; + +/* Suivi d'un flot d'exécution */ +typedef struct _call_stack +{ + collected_register *registers; /* Liste de registres suivis */ + size_t count; /* Taille de cette liste */ + + instr_iter_t *iter; /* Boucle de parcours */ + bool use_current; /* Traite l'instruction pointée*/ + + bool skip_syscall; /* Acceptation des rencontres */ + +} call_stack; + +/* Collection de registres */ +struct _tracked_path +{ + call_stack *stacks; /* Piles d'exécution suivies */ + size_t count; /* Nombre de ces piles */ + +}; + + +/* Copie les informations de pile d'appels. */ +static void copy_call_stack(call_stack *, const call_stack *); + +/* Libère la mémoire des infos relatives à une pile d'appels. */ +static void clean_call_stack(call_stack *); + +/* Fournit une structure de suivi de registres pour une branche. */ +static size_t fork_register_tracker(tracked_path *, size_t, GArchProcessor *, GArchInstruction *); + +/* Change la tête de lecture pour le parcours des instructions. */ +static void change_register_tracker_iter(tracked_path *, size_t, GArchProcessor *, GArchInstruction *); + +/* Détermine si tous les registres recherchés ont été trouvés. */ +static bool got_all_tracked_registers(const tracked_path *, size_t); + + + +/****************************************************************************** +* * +* Paramètres : dest = zone de destination des données copiées. [OUT] * +* src = source des données à copier. * +* * +* Description : Copie les informations de pile d'appels. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void copy_call_stack(call_stack *dest, const call_stack *src) +{ + size_t i; /* Boucle de parcours */ + + if (src->count == 0) + { + dest->registers = NULL; + dest->count = 0; + } + + else + { + dest->registers = (collected_register *)malloc(src->count * sizeof(collected_register)); + dest->count = src->count; + + for (i = 0; i < src->count; i++) + { + dest->registers[i].reg = src->registers[i].reg; + dest->registers[i].required = src->registers[i].required; + dest->registers[i].written = src->registers[i].written; + + g_object_ref(G_OBJECT(dest->registers[i].reg)); + + if (dest->registers[i].written != NULL) + g_object_ref(G_OBJECT(dest->registers[i].written)); + + } + + } + + dest->iter = copy_instruction_iterator(src->iter); + dest->use_current = src->use_current; + + dest->skip_syscall = src->skip_syscall; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = information sur une pile d'appels à supprimer. * +* * +* Description : Libère la mémoire des infos relatives à une pile d'appels. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void clean_call_stack(call_stack *stack) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < stack->count; i++) + { + g_object_unref(G_OBJECT(stack->registers[i].reg)); + + if (stack->registers[i].written != NULL) + g_object_unref(G_OBJECT(stack->registers[i].written)); + + } + + if (stack->registers != NULL) + free(stack->registers); + + if (stack->iter != NULL) + delete_instruction_iterator(stack->iter); + +} + + +/****************************************************************************** +* * +* Paramètres : base = position de parcours initiale. * +* * +* Description : Crée une structure de suivi de registres vide. * +* * +* Retour : Structure prête à emploi. * +* * +* Remarques : - * +* * +******************************************************************************/ + +tracked_path *create_register_tracker(instr_iter_t *base) +{ + tracked_path *result; /* Structure à retourner */ + + result = (tracked_path *)malloc(sizeof(tracked_path)); + + result->stacks = (call_stack *)malloc(sizeof(call_stack)); + result->count = 1; + + copy_call_stack(&result->stacks[0], + (call_stack []) { + { + .count = 0, + .iter = base, + .use_current = true, + .skip_syscall = true + } + }); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : model = suivi déjà en place d'où l'inspiration doit venir. * +* sid = identifiant de la pile d'exécution initiale. * +* * +* Description : Crée une structure de suivi de registres initialisée. * +* * +* Retour : Structure prête à emploi. * +* * +* Remarques : - * +* * +******************************************************************************/ + +tracked_path *create_register_tracker_from(const tracked_path *model, size_t sid) +{ + tracked_path *result; /* Structure à retourner */ + + result = (tracked_path *)malloc(sizeof(tracked_path)); + + result->stacks = (call_stack *)malloc(sizeof(call_stack)); + result->count = 1; + + copy_call_stack(&result->stacks[0], &model->stacks[sid]); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à traiter. * +* * +* Description : Efface une structure de suivi de registres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void delete_register_tracker(tracked_path *path) +{ + size_t i; /* Boucle de parcours */ + + assert(path->count >= 1); + + for (i = 0; i < path->count; i++) + clean_call_stack(&path->stacks[i]); + + free(path->stacks); + + free(path); + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à consulter. * +* * +* Description : Dénombre les piles d'exécutions différentes conservées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +size_t count_register_tracker_stacks(const tracked_path *path) +{ + size_t result; /* Quantité à retourner */ + + result = path->count; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à traiter. * +* sid = identifiant de la pile d'exécution racine à copier. * +* proc = processeur de l'architecture pour les instructions. * +* dest = prochaine instruction à traiter. * +* * +* Description : Fournit une structure de suivi de registres pour une branche.* +* * +* Retour : Indice de la nouvelle pile d'exécution à suivre. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static size_t fork_register_tracker(tracked_path *path, size_t sid, GArchProcessor *proc, GArchInstruction *dest) +{ + size_t result; /* Indice à retourner */ + + result = path->count; + + path->stacks = (call_stack *)realloc(path->stacks, ++path->count * sizeof(call_stack)); + + copy_call_stack(&path->stacks[result], &path->stacks[sid]); + + change_register_tracker_iter(path, result, proc, dest); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à traiter. * +* sid = identifiant de la pile d'exécution à traiter. * +* proc = processeur de l'architecture pour les instructions. * +* dest = prochaine instruction à traiter. * +* * +* Description : Change la tête de lecture pour le parcours des instructions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void change_register_tracker_iter(tracked_path *path, size_t sid, GArchProcessor *proc, GArchInstruction *dest) +{ + const mrange_t *range; /* Couverture d'une instruction*/ + instr_iter_t *iter; /* Tête de lecture */ + + delete_instruction_iterator(path->stacks[sid].iter); + + range = g_arch_instruction_get_range(dest); + iter = g_arch_processor_get_iter_from_address(proc, get_mrange_addr(range)); + + path->stacks[sid].iter = iter; + path->stacks[sid].use_current = true; + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à traiter. * +* sid = identifiant de la pile d'exécution à traiter. * +* reg = registre concerné par la procédure. * +* where = localisation de l'écriture ou importance de la note. * +* * +* Description : Note le besoin ou l'usage d'un registre donné. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void mark_register_in_tracker(tracked_path *path, size_t sid, GArchRegister *reg, GArchInstruction *where) +{ + call_stack *stack; /* Pile d'exécution concernée */ + size_t i; /* Boucle de parcours */ + collected_register *collected; /* Accès simplifié */ + int ret; /* Bilan d'une comparaison */ + + stack = &path->stacks[sid]; + + /* Mise à jour d'un élément présent ? */ + + for (i = 0; i < stack->count; i++) + { + collected = &stack->registers[i]; + + ret = g_arch_register_compare(collected->reg, reg); + if (ret != 0) continue; + + if (where == NULL) + collected->required = true; + + else + { + if (collected->written == NULL) + { + collected->written = where; + g_object_ref(G_OBJECT(where)); + } + + } + + break; + + } + + /* Ajout d'une nouvelle note */ + + if (i == stack->count) + { + stack->count++; + stack->registers = (collected_register *)realloc(stack->registers, + stack->count * sizeof(collected_register)); + + collected = &stack->registers[stack->count - 1]; + + collected->reg = reg; + g_object_ref(G_OBJECT(reg)); + + if (where == NULL) + { + collected->required = true; + collected->written = NULL; + } + + else + { + collected->required = false; + collected->written = where; + g_object_ref(G_OBJECT(where)); + } + + } + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à consulter. * +* sid = identifiant de la pile d'exécution à traiter. * +* * +* Description : Détermine si tous les registres recherchés ont été trouvés. * +* * +* Retour : Besoin en poursuite d'études. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool got_all_tracked_registers(const tracked_path *path, size_t sid) +{ + bool result; /* Bilan à retourner */ + call_stack *stack; /* Pile d'exécution concernée */ + size_t i; /* Boucle de parcours */ + + result = true; + + stack = &path->stacks[sid]; + + for (i = 0; i < stack->count && result; i++) + if (stack->registers[i].required) + result = (stack->registers[i].written != NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à consulter et compléter. * +* sid = identifiant de la pile d'exécution à traiter. * +* proc = processeur de l'architecture pour les instructions. * +* hops = opérations spécialement adaptées à une architecture. * +* * +* Description : Se lance à la recherche de la définition de registres. * +* * +* Retour : true si toutes les définitions demandées ont été trouvées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool look_for_registers(tracked_path *path, size_t sid, GArchProcessor *proc, const hunting_ops *hops) +{ + bool result; /* Bilan de l'opération */ + call_stack *stack; /* Pile d'exécution concernée */ + GArchInstruction *instr; /* Instruction analysée */ + GArchOperand *operand; /* Destination d'instruction ? */ + GArchRegister *reg; /* Registre en première ligne */ + size_t count; /* Nombre de sources présentes */ + bool first; /* Premier aiguillage ? */ + size_t i; /* Boucle de parcours */ + instr_link_t *link; /* Détails d'un lien */ + size_t next; /* Indice de la pile suivante */ + + stack = &path->stacks[sid]; + + while (stack->iter != NULL && !got_all_tracked_registers(path, sid)) + { + if (stack->use_current) + { + instr = get_instruction_iterator_current(stack->iter); + stack->use_current = false; + } + + else + instr = get_instruction_iterator_prev(stack->iter); + + /* Détection de fin de parcours (#1) */ + + if (instr == NULL) + { + delete_instruction_iterator(stack->iter); + stack->iter = NULL; + break; + } + + if (hops->is_syscall(instr) && !stack->skip_syscall) + { + delete_instruction_iterator(stack->iter); + stack->iter = NULL; + break; + } + + stack->skip_syscall = false; + + /* Traitement de l'instruction courante */ + + operand = g_arch_instruction_get_operand(instr, 0); + + if (G_IS_REGISTER_OPERAND(operand)) + { + reg = g_register_operand_get_register(G_REGISTER_OPERAND(operand)); + + mark_register_in_tracker(path, sid, reg, instr); + + } + + /* Détermination de l'instruction suivante */ + + g_arch_instruction_lock_src(instr); + + count = g_arch_instruction_count_sources(instr); + + first = true; + + for (i = 0; i < count; i++) + { + link = g_arch_instruction_get_source(instr, i); + + switch (link->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: + + if (first) + { + change_register_tracker_iter(path, sid, proc, link->linked); + first = false; + } + + else + { + next = fork_register_tracker(path, sid, proc, link->linked); + look_for_registers(path, next, proc, hops); + } + + break; + + default: + break; + + } + + } + + g_arch_instruction_unlock_src(instr); + + /* Détection de fin de parcours (#2) */ + + if (g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START) + { + delete_instruction_iterator(stack->iter); + stack->iter = NULL; + break; + } + + } + + result = got_all_tracked_registers(path, sid); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : path = chemin d'exécution à traiter. * +* sid = identifiant de la pile d'exécution à traiter. * +* reg = registre concerné par la procédure. * +* * +* Description : Retrouve la dernière modification d'un registre donné. * +* * +* Retour : Localisation de l'écriture ou importante de la note. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GArchInstruction *get_register_write_location(const tracked_path *path, size_t sid, const GArchRegister *reg) +{ + GArchInstruction *result; /* Emplacement à retourner */ + call_stack *stack; /* Pile d'exécution concernée */ + size_t i; /* Boucle de parcours */ + collected_register *collected; /* Accès simplifié */ + int ret; /* Bilan d'une comparaison */ + + result = NULL; + + stack = &path->stacks[sid]; + + for (i = 0; i < stack->count; i++) + { + collected = &stack->registers[i]; + + ret = g_arch_register_compare(collected->reg, reg); + if (ret != 0) continue; + + result = collected->written; + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + break; + + } + + return result; + +} diff --git a/plugins/lnxsyscalls/collect.h b/plugins/lnxsyscalls/collect.h new file mode 100644 index 0000000..370c27e --- /dev/null +++ b/plugins/lnxsyscalls/collect.h @@ -0,0 +1,63 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * collect.h - prototypes pour la collecte de différents registres en remontant le flot d'exécution + * + * 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_COLLECT_H +#define _PLUGINS_LNXSYSCALLS_COLLECT_H + + +#include <arch/instriter.h> +#include <arch/register.h> + + + +/* Depuis hops.h : opérations adaptées pour une chasse donnée */ +typedef struct _hunting_ops hunting_ops; + +/* Collection de registres */ +typedef struct _tracked_path tracked_path; + + +/* Crée une structure de suivi de registres vide. */ +tracked_path *create_register_tracker(instr_iter_t *); + +/* Crée une structure de suivi de registres initialisée. */ +tracked_path *create_register_tracker_from(const tracked_path *, size_t); + +/* Efface une structure de suivi de registres. */ +void delete_register_tracker(tracked_path *); + +/* Dénombre les piles d'exécutions différentes conservées. */ +size_t count_register_tracker_stacks(const tracked_path *); + +/* Note le besoin ou l'usage d'un registre donné. */ +void mark_register_in_tracker(tracked_path *, size_t, GArchRegister *, GArchInstruction *); + +/* Se lance à la recherche de la définition de registres. */ +bool look_for_registers(tracked_path *, size_t, GArchProcessor *, const hunting_ops *); + +/* Retrouve la dernière modification d'un registre donné. */ +GArchInstruction *get_register_write_location(const tracked_path *, size_t, const GArchRegister *); + + + +#endif /* _PLUGINS_LNXSYSCALLS_COLLECT_H */ diff --git a/plugins/lnxsyscalls/core.c b/plugins/lnxsyscalls/core.c new file mode 100644 index 0000000..f28e776 --- /dev/null +++ b/plugins/lnxsyscalls/core.c @@ -0,0 +1,168 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core.c - greffon détaillant les appels système + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "core.h" + + +#include <i18n.h> + + +#include <core/global.h> +#include <core/nproc.h> + + +#include "db.h" +#include "hops_armv7.h" +#include "hunter.h" + + + +DEFINE_CHRYSALIDE_PLUGIN("Linux System Calls", "Describes each Linux system call with its arguments", \ + "0.1.0", EMPTY_PG_LIST(.required), AL(PGA_PLUGIN_INIT, PGA_DISASSEMBLY_ENDED)); + + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte du chargement du greffon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) +{ + bool result; /* Bilan à retourner */ + sqlite3 *db; /* Base de données présente */ + + db = open_syscalls_database(plugin); + + if (db != NULL) + { + introduce_syscalls_database(db, plugin); + + close_syscalls_database(db); + + result = true; + + } + + else + result = false; + + return result; + +} + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* binary = binaire dont le contenu est en cours de traitement.* +* status = barre de statut à tenir informée. * +* context = contexte de désassemblage. * +* * +* Description : Exécute une action pendant un désassemblage de binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +G_MODULE_EXPORT void process_binary_disassembly(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) +{ + GBinFormat *format; /* Format du binaire chargé */ + const char *arch; /* Architecture d'exécution */ + const hunting_ops *hops; /* Opérations particulières */ + size_t sym_count; /* Nombre de ces symboles */ + guint runs_count; /* Qté d'exécutions parallèles */ + size_t run_size; /* Volume réparti par exécution*/ + activity_id_t id; /* Identifiant de progression */ + GWorkQueue *queue; /* Gestionnaire de différés */ + wgroup_id_t gid; /* Identifiant pour les tâches */ + guint i; /* Boucle de parcours */ + size_t begin; /* Début de bloc de traitement */ + size_t end; /* Fin d'un bloc de traitement */ + GGateHunter *hunter; /* Tâche d'étude à programmer */ + + format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); + + arch = g_exe_format_get_target_machine(G_EXE_FORMAT(format)); + + if (strcmp(arch, "armv7") == 0) + hops = get_armv7_hunting_ops(); + + else + { + g_plugin_module_log_variadic_message(plugin, LMT_WARNING, + _("No suitable backend to track syscalls!")); + goto pbd_exit; + } + + g_binary_format_lock_symbols_rd(format); + + sym_count = g_binary_format_count_symbols(format); + + runs_count = get_max_online_threads(); + + run_size = sym_count / runs_count; + + id = gtk_status_stack_add_activity(status, _("Looking for Linux syscalls..."), sym_count); + + queue = get_work_queue(); + + gid = g_work_queue_define_work_group(queue); + + for (i = 0; i < runs_count; i++) + { + begin = i * run_size; + + if ((i + 1) == runs_count) + end = sym_count; + else + end = begin + run_size; + + hunter = g_gate_hunter_new(plugin, binary, context, begin, end, id, hops); + + g_work_queue_schedule_work(queue, G_DELAYED_WORK(hunter), gid); + + } + + g_work_queue_wait_for_completion(queue, gid); + + g_work_queue_delete_work_group(queue, gid); + + gtk_status_stack_remove_activity(status, id); + + g_binary_format_unlock_symbols_rd(format); + + pbd_exit: + + g_object_unref(G_OBJECT(format)); + +} diff --git a/plugins/lnxsyscalls/core.h b/plugins/lnxsyscalls/core.h new file mode 100644 index 0000000..a8e987e --- /dev/null +++ b/plugins/lnxsyscalls/core.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core.h - prototypes pour le greffon détaillant les appels système + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_CORE_H +#define _PLUGINS_LNXSYSCALLS_CORE_H + + +#include <plugins/plugin.h> +#include <plugins/plugin-int.h> + + + +/* Prend acte du chargement du greffon. */ +G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); + +/* Exécute une action pendant un désassemblage de binaire. */ +G_MODULE_EXPORT void process_binary_disassembly(const GPluginModule *, PluginAction , GLoadedBinary *, GtkStatusStack *, GProcContext *); + + + +#endif /* _PLUGINS_LNXSYSCALLS_CORE_H */ diff --git a/plugins/lnxsyscalls/db.c b/plugins/lnxsyscalls/db.c new file mode 100644 index 0000000..d6325b5 --- /dev/null +++ b/plugins/lnxsyscalls/db.c @@ -0,0 +1,245 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * db.c - constitution d'identités d'appels depuis une base de données + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "db.h" + + +#include <assert.h> +#include <malloc.h> + + +#include <i18n.h> + + +#include <core/paths.h> +#include <plugins/plugin-int.h> + + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Ouvre la base de connaissances quant aux appels système. * +* * +* Retour : Base de données SQLite disponible ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +sqlite3 *open_syscalls_database(const GPluginModule *plugin) +{ + sqlite3 *result; /* Base de données à renvoyer */ + char *filename; /* Chemin vers la base */ + int ret; /* Bilan d'un appel */ + + filename = find_plugin_file("lnxsyscalls", "linux-syscalls.db"); + + if (filename == NULL) + { + g_plugin_module_log_simple_message(plugin, LMT_ERROR, _("Unable to find the syscalls database")); + result = NULL; + } + + else + { + ret = sqlite3_open(filename, &result); + + if (ret != SQLITE_OK) + { + g_plugin_module_log_simple_message(plugin, LMT_ERROR, _("Unable to load the syscalls database")); + result = NULL; + } + + free(filename); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : db = base de données SQLite à clôturer. * +* * +* Description : Ferme la base de connaissances quant aux appels système. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void close_syscalls_database(sqlite3 *db) +{ +#ifndef NDEBUG + int ret; /* Bilan d'un appel */ +#endif + +#ifndef NDEBUG + + ret = sqlite3_close(db); + assert(ret == SQLITE_OK); + +#else + + sqlite3_close(db); + +#endif + +} + + +/****************************************************************************** +* * +* Paramètres : db = base de données SQLite à consulter. * +* plugin = greffon à manipuler. * +* * +* Description : Présente le contenu de la base des appels système. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void introduce_syscalls_database(sqlite3 *db, const GPluginModule *plugin) +{ + const char *sql; /* Requête SQL à construire */ + sqlite3_stmt *stmt; /* Déclaration mise en place */ + int ret; /* Bilan d'un appel à SQLite */ + + sql = "SELECT arch, COUNT(nr) FROM Syscalls GROUP BY arch ORDER BY arch;"; + + ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + if (ret != SQLITE_OK) + { + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("Can't prepare statment '%s' (ret=%d): %s"), + sql, ret, sqlite3_errmsg(db)); + goto isd_exit; + } + + for (ret = sqlite3_step(stmt); ret == SQLITE_ROW; ret = sqlite3_step(stmt)) + { + g_plugin_module_log_variadic_message(plugin, LMT_INFO, + _("The database contains %d syscalls for the '%s' architecture"), + sqlite3_column_int(stmt, 1), + (char *)sqlite3_column_text(stmt, 0)); + } + + sqlite3_finalize(stmt); + + isd_exit: + + ; + +} + + +/****************************************************************************** +* * +* Paramètres : db = base de données SQLite à consulter. * +* plugin = greffon à manipuler. * +* arch = architecture visée par la procédure. * +* : nr = indice de l'appel système à décrire. * +* * +* Description : Construit l'identité d'un appel système pour un indice donné.* +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +syscall_info_t *extract_from_syscalls_database(sqlite3 *db, const GPluginModule *plugin, const char *arch, unsigned int nr) +{ + syscall_info_t *result; /* Description à retourner */ + const char *sql; /* Requête SQL à construire */ + size_t i; /* Boucle de parcours */ + sqlite3_stmt *stmt; /* Déclaration mise en place */ + int ret; /* Bilan d'un appel à SQLite */ + const char *arg; /* Eventuel argument d'appel */ + + result = NULL; + + sql = "SELECT name, arg_0, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6, filename, line" \ + " FROM Syscalls" \ + " WHERE arch = ? AND nr = ?;"; + + ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + if (ret != SQLITE_OK) + { + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("Can't prepare statment '%s' (ret=%d): %s"), + sql, ret, sqlite3_errmsg(db)); + goto efsd_exit; + } + + ret = sqlite3_bind_text(stmt, 1, arch, -1, NULL); + if (ret != SQLITE_OK) + { + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("Can't bind value for parameter nb 0 in '%s' (ret=%d): %s"), + sql, ret, sqlite3_errmsg(db)); + goto efsd_clean_exit; + } + + ret = sqlite3_bind_int(stmt, 2, nr); + if (ret != SQLITE_OK) + { + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("Can't bind value for parameter nb 1 in '%s' (ret=%d): %s"), + sql, ret, sqlite3_errmsg(db)); + goto efsd_clean_exit; + } + + ret = sqlite3_step(stmt); + + if (ret == SQLITE_ROW) + { + result = create_syscall_info(nr, (char *)sqlite3_column_text(stmt, 0)); + + for (i = 0; i < 6; i++) + { + arg = (char *)sqlite3_column_text(stmt, 1 + i); + + if (arg != NULL) + append_arg_to_syscall_info(result, arg); + + } + + } + + efsd_clean_exit: + + sqlite3_finalize(stmt); + + efsd_exit: + + return result; + +} diff --git a/plugins/lnxsyscalls/db.h b/plugins/lnxsyscalls/db.h new file mode 100644 index 0000000..7eae005 --- /dev/null +++ b/plugins/lnxsyscalls/db.h @@ -0,0 +1,52 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * db.h - prototypes pour la constitution d'identités d'appels depuis une base de données + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_DB_H +#define _PLUGINS_LNXSYSCALLS_DB_H + + +#include <sqlite3.h> + + +#include <plugins/plugin.h> + + +#include "syscall.h" + + + +/* Ouvre la base de connaissances quant aux appels système. */ +sqlite3 *open_syscalls_database(const GPluginModule *); + +/* Ferme la base de connaissances quant aux appels système. */ +void close_syscalls_database(sqlite3 *); + +/* Présente le contenu de la base des appels système. */ +void introduce_syscalls_database(sqlite3 *, const GPluginModule *); + +/* Construit l'identité d'un appel système pour un indice donné. */ +syscall_info_t *extract_from_syscalls_database(sqlite3 *, const GPluginModule *, const char *, unsigned int); + + + +#endif /* _PLUGINS_LNXSYSCALLS_DB_H */ diff --git a/plugins/lnxsyscalls/hops.h b/plugins/lnxsyscalls/hops.h new file mode 100644 index 0000000..42f2cc6 --- /dev/null +++ b/plugins/lnxsyscalls/hops.h @@ -0,0 +1,67 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hops.h - prototypes pour les particularités de chasse propres à une architecture + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_HOPS_H +#define _PLUGINS_LNXSYSCALLS_HOPS_H + + +#include <stdbool.h> + + +#include <arch/processor.h> + + +#include "collect.h" +#include "syscall.h" +#include "writer.h" + + + +/* Détermine si l'instruction lance un appel syystème. */ +typedef bool (* is_lsyscall_fc) (GArchInstruction *); + +/* Identifie le numéro d'appel système en cours de manipulation. */ +typedef bool (* resolve_lsyscall_nr_fc) (tracked_path *, GArchProcessor *, const hunting_ops *, unsigned int *); + +/* Marque les registres associés aux n premiers arguments. */ +typedef bool (* look_for_lsyscall_args_fc) (tracked_path *, size_t, size_t); + +/* Commente autant que possible un appel système brut. */ +typedef void (* comment_lsyscall_fc) (tracked_path *, size_t, syscall_info_t *, comment_writer *); + + +/* Opérations adaptées pour une chasse donnée */ +typedef struct _hunting_ops +{ + const char *arch; /* Rappel de l'architecture */ + + is_lsyscall_fc is_syscall; /* Identification d'un appel */ + resolve_lsyscall_nr_fc resolve_nr; /* Récupération d'un numéro */ + look_for_lsyscall_args_fc look_for_args;/* Mise à prix de N arguments */ + comment_lsyscall_fc comment; /* Inscription de commentaires */ + +} hunting_ops; + + + +#endif /* _PLUGINS_LNXSYSCALLS_HOPS_H */ diff --git a/plugins/lnxsyscalls/hops_armv7.c b/plugins/lnxsyscalls/hops_armv7.c new file mode 100644 index 0000000..58b2702 --- /dev/null +++ b/plugins/lnxsyscalls/hops_armv7.c @@ -0,0 +1,262 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hops_armv7.c - recherche d'appels système spécifiques à ARMv7 + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "hops_armv7.h" + + +#include <assert.h> +#include <string.h> + + +#include <plugins/arm/v7/registers/basic.h> + + + +/* Détermine si l'instruction lance un appel syystème. */ +static bool is_armv7_linux_syscall(GArchInstruction *); + +/* Identifie le numéro d'appel système en cours de manipulation. */ +static bool resolve_armv7_linux_syscall_number(tracked_path *, GArchProcessor *, const hunting_ops *, unsigned int *); + +/* Marque les registres associés aux n premiers arguments. */ +static bool look_for_armv7_linux_syscall_args(tracked_path *, size_t, size_t); + +/* Commente autant que possible un appel système brut. */ +static void comment_armv7_linux_syscall(tracked_path *, size_t, syscall_info_t *, comment_writer *); + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit les opérations spécifiques à ARMv7 pour une chasse. * +* * +* Retour : Ensemble d'opérations pour une chasse aux appels système. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const hunting_ops *get_armv7_hunting_ops(void) +{ + static const hunting_ops armv7_hops = { + + .arch = "arm", + + .is_syscall = is_armv7_linux_syscall, + .resolve_nr = resolve_armv7_linux_syscall_number, + .look_for_args = look_for_armv7_linux_syscall_args, + .comment = comment_armv7_linux_syscall + + }; + + return &armv7_hops; + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction à analyser. * +* * +* Description : Détermine si l'instruction lance un appel syystème. * +* * +* Retour : Bilan de l'analyse : true s'il s'agit bien d'un appel. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool is_armv7_linux_syscall(GArchInstruction *instr) +{ + bool result; /* Conclusion à diffuser */ + const char *kwd; /* Désignation d'instruction */ + + kwd = g_arch_instruction_get_keyword(instr, ASX_INTEL); + + result = (strcmp(kwd, "svc") == 0); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : exec = suivi de l'utilisation des registres. * +* proc = processeur de l'architecture pour les instructions. * +* hops = opérations spécialement adaptées à une architecture. * +* nr = numéro de l'appel système identifié. [OUT] * +* * +* Description : Identifie le numéro d'appel système en cours de manipulation.* +* * +* Retour : Bilan de l'opération : true en cas de succès, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool resolve_armv7_linux_syscall_number(tracked_path *exec, GArchProcessor *proc, const hunting_ops *hops, unsigned int *nr) +{ + bool result; /* Bilan à faire remonter */ + GArchRegister *reg; /* Registre portant le numéro */ + bool got_nr; /* Bilan d'une recherche */ + GArchInstruction *instr; /* Instruction d'importance */ + const char *kwd; /* Désignation d'instruction */ + GArchOperand *op; /* Opérande avec une constante */ + + result = false; + + /* On vise r7... */ + reg = g_armv7_basic_register_new(7); + mark_register_in_tracker(exec, 0, reg, NULL); + + assert(count_register_tracker_stacks(exec) == 1); + + got_nr = look_for_registers(exec, 0, proc, hops); + + if (got_nr) + { + instr = get_register_write_location(exec, 0, reg); + kwd = g_arch_instruction_get_keyword(instr, ASX_INTEL); + + /* ... et uniquement les instructions 'mov r7, <imm>' */ + if (strncmp(kwd, "mov", 3) != 0) + goto ralsn_exit; + + op = g_arch_instruction_get_operand(instr, 1); + + if (!G_IS_IMM_OPERAND(op)) + goto ralsn_exit; + + *nr = g_imm_operand_get_raw_value(G_IMM_OPERAND(op)); + result = true; + + } + + ralsn_exit: + + g_object_unref(G_OBJECT(reg)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : exec = chemin d'exécution à préparer. * +* sid = identifiant de la pile d'exécution à traiter. * +* argc = nombre d'arguments à repérer. * +* * +* Description : Marque les registres associés aux n premiers arguments. * +* * +* Retour : Bilan de l'opération : true en cas de succès, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool look_for_armv7_linux_syscall_args(tracked_path *exec, size_t sid, size_t argc) +{ + bool result; /* Bilan à faire remonter */ + size_t i; /* Boucle de parcours */ + GArchRegister *reg; /* Registre portant le numéro */ + + /** + * man 2 syscall : + * + * arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 + * ────────────────────────────────────────────────────────── + * arm/OABI a1 a2 a3 a4 v1 v2 v3 + * arm/EABI r0 r1 r2 r3 r4 r5 r6 + */ + + result = (argc <= 7); + + if (result) + for (i = 0; i < argc; i++) + { + reg = g_armv7_basic_register_new(i); + mark_register_in_tracker(exec, sid, reg, NULL); + g_object_ref(G_OBJECT(reg)); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : exec = chemin d'exécution identifié. * +* sid = identifiant de la pile d'exécution à traiter. * +* info = fiche d'identité d'un appel système. * +* writer = conservateur des commentaires à écrire au final. * +* * +* Description : Commente autant que possible un appel système brut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void comment_armv7_linux_syscall(tracked_path *exec, size_t sid, syscall_info_t *info, comment_writer *writer) +{ + GArchRegister *reg; /* Registre intervenant */ + GArchInstruction *instr; /* Instruction impliquée */ + size_t i; /* Boucle de parcours */ + + /* Type d'appel système */ + + reg = g_armv7_basic_register_new(7); + + instr = get_register_write_location(exec, sid, reg); + + if (instr != NULL) + add_comment_at(writer, info->name, instr); + + g_object_unref(G_OBJECT(instr)); + + g_object_unref(G_OBJECT(reg)); + + /* Eventuels arguments */ + + for (i = 0; i < info->argc; i++) + { + reg = g_armv7_basic_register_new(i); + + instr = get_register_write_location(exec, sid, reg); + + if (instr != NULL) + add_comment_at(writer, info->argv[i], instr); + + g_object_unref(G_OBJECT(instr)); + + g_object_unref(G_OBJECT(reg)); + + } + +} diff --git a/plugins/lnxsyscalls/hops_armv7.h b/plugins/lnxsyscalls/hops_armv7.h new file mode 100644 index 0000000..a0dad87 --- /dev/null +++ b/plugins/lnxsyscalls/hops_armv7.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hops_armv7.h - prototypes pour la recherche d'appels système spécifiques à ARMv7 + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_HOPS_ARMV7_H +#define _PLUGINS_LNXSYSCALLS_HOPS_ARMV7_H + + +#include "hops.h" + + + +/* Fournit les opérations spécifiques à ARMv7 pour une chasse. */ +const hunting_ops *get_armv7_hunting_ops(void); + + + +#endif /* _PLUGINS_LNXSYSCALLS_HOPS_ARMV7_H */ diff --git a/plugins/lnxsyscalls/hunter.c b/plugins/lnxsyscalls/hunter.c new file mode 100644 index 0000000..4cac8e6 --- /dev/null +++ b/plugins/lnxsyscalls/hunter.c @@ -0,0 +1,346 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hunter.c - recherche de portes vers le noyau + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "hunter.h" + + +#include <analysis/routine.h> +#include <glibext/delayed-int.h> + + +#include "db.h" + + + +/* Chasse à l'appel système (instance) */ +struct _GGateHunter +{ + GDelayedWork parent; /* A laisser en premier */ + + const GPluginModule *plugin; /* Liens pour les messages */ + + GLoadedBinary *binary; /* Binaire chargé et concerné */ + GBinFormat *format; /* Format de fichier manipulé */ + GProcContext *context; /* Contexte de désassemblage */ + + size_t begin; /* Point de départ du parcours */ + size_t end; /* Point d'arrivée exclu */ + + activity_id_t id; /* Identifiant pour messages */ + + const hunting_ops *hops; /* Opérations particulières */ + sqlite3 *db; /* Base de données à manipuler */ + +}; + +/* Chasse à l'appel système (classe) */ +struct _GGateHunterClass +{ + GDelayedWorkClass parent; /* A laisser en premier */ + +}; + + +/* Indique le type défini pour les tâches d'étude de routines. */ +GType g_gate_hunter_get_type(void); + +/* Initialise la classe des tâches d'étude de routines. */ +static void g_gate_hunter_class_init(GGateHunterClass *); + +/* Initialise une tâche d'étude de routines. */ +static void g_gate_hunter_init(GGateHunter *); + +/* Supprime toutes les références externes. */ +static void g_gate_hunter_dispose(GGateHunter *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gate_hunter_finalize(GGateHunter *); + +/* Effectue une recherche d'appels système. */ +static void g_gate_hunter_process(GGateHunter *, GtkStatusStack *); + + + +/* Indique le type défini pour les tâches d'étude de routines. */ +G_DEFINE_TYPE(GGateHunter, g_gate_hunter, G_TYPE_DELAYED_WORK); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des tâches d'étude de routines. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gate_hunter_class_init(GGateHunterClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GDelayedWorkClass *work; /* Version en classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_gate_hunter_dispose; + object->finalize = (GObjectFinalizeFunc)g_gate_hunter_finalize; + + work = G_DELAYED_WORK_CLASS(klass); + + work->run = (run_task_fc)g_gate_hunter_process; + +} + + +/****************************************************************************** +* * +* Paramètres : hunter = instance à initialiser. * +* * +* Description : Initialise une tâche d'étude de routines. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gate_hunter_init(GGateHunter *hunter) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : hunter = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gate_hunter_dispose(GGateHunter *hunter) +{ + g_object_unref(G_OBJECT(hunter->binary)); + + g_binary_format_unlock_symbols_rd(hunter->format); + g_object_unref(G_OBJECT(hunter->format)); + + g_object_unref(G_OBJECT(hunter->context)); + + G_OBJECT_CLASS(g_gate_hunter_parent_class)->dispose(G_OBJECT(hunter)); + +} + + +/****************************************************************************** +* * +* Paramètres : hunter = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gate_hunter_finalize(GGateHunter *hunter) +{ + if (hunter->db != NULL) + close_syscalls_database(hunter->db); + + G_OBJECT_CLASS(g_gate_hunter_parent_class)->finalize(G_OBJECT(hunter)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* binary = binaire dont la définition est à compléter. * +* context = contexte de désassemblage. * +* begin = point de départ du parcours de liste. * +* end = point d'arrivée exclu du parcours. * +* id = identifiant du message affiché à l'utilisateur. * +* hops = opérations spécifiques à une architecture. * +* * +* Description : Crée une tâche de recherche de portes différée. * +* * +* Retour : Tâche créée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GGateHunter *g_gate_hunter_new(const GPluginModule *plugin, GLoadedBinary *binary, GProcContext *context, size_t begin, size_t end, activity_id_t id, const hunting_ops *hops) +{ + GGateHunter *result; /* Tâche à retourner */ + + result = g_object_new(G_TYPE_GATE_HUNTER, NULL); + + result->plugin = plugin; + + result->binary = binary; + g_object_ref(G_OBJECT(binary)); + + result->format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); + + g_binary_format_lock_symbols_rd(result->format); + + result->context = context; + g_object_ref(G_OBJECT(context)); + + result->begin = begin; + result->end = end; + + result->id = id; + + result->hops = hops; + + result->db = open_syscalls_database(plugin); + if (result->db == NULL) goto gghn_db_error; + + return result; + + gghn_db_error: + + g_object_unref(G_OBJECT(result)); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : hunter = étude de routines à mener. * +* status = barre de statut à tenir informée. * +* * +* Description : Effectue une recherche d'appels système. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gate_hunter_process(GGateHunter *hunter, GtkStatusStack *status) +{ + GArchProcessor *proc; /* Architecture du binaire */ + size_t i; /* Boucle de parcours #1 */ + GBinSymbol *symbol; /* Commodité d'accès */ + const mrange_t *range; /* Couverture d'une routine */ + instr_iter_t *iter; /* Boucle de parcours #2 */ + GArchInstruction *instr; /* Instruction analysée */ + tracked_path *exec; /* Chemin d'exécution à suivre */ + unsigned int nr; /* Numéro d'appel système */ + bool ret; /* Bilan d'un appel */ + syscall_info_t *info; /* Information sur l'appel */ + size_t loop; /* Quantité de boucles en vue */ + size_t k; /* Boucle de parcours #3 */ + comment_writer *writer; /* Ecriture de commentaires */ + + proc = g_loaded_binary_get_processor(hunter->binary); + + for (i = hunter->begin; i < hunter->end; i++) + { + symbol = g_binary_format_get_symbol(hunter->format, i); + + if (!G_IS_BIN_ROUTINE(symbol)) + goto gghp_next; + + range = g_binary_symbol_get_range(symbol); + + iter = g_arch_processor_get_iter_from_address(proc, get_mrange_addr(range)); + + if (iter != NULL) + { + restrict_instruction_iterator(iter, range); + + for (instr = get_instruction_iterator_current(iter); + instr != NULL; + instr = get_instruction_iterator_next(iter)) + { + if (hunter->hops->is_syscall(instr)) + { + exec = create_register_tracker(iter); + + ret = hunter->hops->resolve_nr(exec, proc, hunter->hops, &nr); + if (!ret) goto unknown_syscall; + + info = extract_from_syscalls_database(hunter->db, hunter->plugin, hunter->hops->arch, nr); + if (info == NULL) goto unknown_syscall; + + loop = count_register_tracker_stacks(exec); + + for (k = 0; k < loop; k++) + { + ret = hunter->hops->look_for_args(exec, k, info->argc); + if (!ret) goto unknown_syscall; + + look_for_registers(exec, k, proc, hunter->hops); + + } + + writer = create_comment_writer(); + + loop = count_register_tracker_stacks(exec); + + for (k = 0; k < loop; k++) + hunter->hops->comment(exec, k, info, writer); + + write_all_comments(writer, G_PRELOAD_INFO(hunter->context)); + + delete_comment_writer(writer); + + unknown_syscall: + + delete_register_tracker(exec); + + } + + g_object_unref(G_OBJECT(instr)); + + } + + delete_instruction_iterator(iter); + + } + + gghp_next: + + gtk_status_stack_update_activity_value(status, hunter->id, 1); + + g_object_unref(G_OBJECT(symbol)); + + } + + g_object_unref(G_OBJECT(proc)); + +} diff --git a/plugins/lnxsyscalls/hunter.h b/plugins/lnxsyscalls/hunter.h new file mode 100644 index 0000000..100f426 --- /dev/null +++ b/plugins/lnxsyscalls/hunter.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hunter.h - prototypes pour la recherche de portes vers le noyau + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_HUNTER_H +#define _PLUGINS_LNXSYSCALLS_HUNTER_H + + +#include <analysis/binary.h> +#include <plugins/plugin.h> + + +#include "hops.h" + + + +#define G_TYPE_GATE_HUNTER g_gate_hunter_get_type() +#define G_GATE_HUNTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_gate_hunter_get_type(), GGateHunter)) +#define G_IS_GATE_HUNTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_gate_hunter_get_type())) +#define G_GATE_HUNTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GATE_HUNTER, GGateHunterClass)) +#define G_IS_GATE_HUNTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GATE_HUNTER)) +#define G_GATE_HUNTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GATE_HUNTER, GGateHunterClass)) + + +/* Chasse à l'appel système (instance) */ +typedef struct _GGateHunter GGateHunter; + +/* Chasse à l'appel système (classe) */ +typedef struct _GGateHunterClass GGateHunterClass; + + +/* Crée une tâche d'étude de routines différée. */ +GGateHunter *g_gate_hunter_new(const GPluginModule *, GLoadedBinary *, GProcContext *, size_t, size_t, activity_id_t, const hunting_ops *); + + + +#endif /* _PLUGINS_LNXSYSCALLS_HUNTER_H */ diff --git a/plugins/lnxsyscalls/linux-syscalls.db b/plugins/lnxsyscalls/linux-syscalls.db Binary files differnew file mode 100644 index 0000000..c1ea339 --- /dev/null +++ b/plugins/lnxsyscalls/linux-syscalls.db diff --git a/plugins/lnxsyscalls/syscall.c b/plugins/lnxsyscalls/syscall.c new file mode 100644 index 0000000..e177d6e --- /dev/null +++ b/plugins/lnxsyscalls/syscall.c @@ -0,0 +1,108 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * syscall.h - prototypes pour la définition d'appels système Linux + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "syscall.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + + +/****************************************************************************** +* * +* Paramètres : nr = indice de l'appel système à décrire. * +* name = désignation humaine de ce même appel système. * +* * +* Description : Crée un début de description d'appel système. * +* * +* Retour : Structure mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +syscall_info_t *create_syscall_info(unsigned int nr, const char *name) +{ + syscall_info_t *result; /* Description à retourner */ + + result = (syscall_info_t *)calloc(1, sizeof(syscall_info_t)); + + result->nr = nr; + result->name = strdup(name); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : info = description d'appel système à supprimer. * +* * +* Description : Efface de la mémoire une description d'appel système. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void delete_syscall_info(syscall_info_t *info) +{ + size_t i; /* Boucle de parcours */ + + free(info->name); + + for (i = 0; i < info->argc; i++) + free(info->argv[i]); + + if (info->filename != NULL) + free(info->filename); + + free(info); + +} + + +/****************************************************************************** +* * +* Paramètres : info = description d'appel système à compléter. * +* arg = description d'un argument supplémentaire. * +* * +* Description : Ajoute un argument à une description d'appel système. * +* * +* Retour : Structure mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +void append_arg_to_syscall_info(syscall_info_t *info, const char *arg) +{ + assert(info->argc < 6); + + info->argv[info->argc++] = strdup(arg); + +} diff --git a/plugins/lnxsyscalls/syscall.h b/plugins/lnxsyscalls/syscall.h new file mode 100644 index 0000000..ecbe911 --- /dev/null +++ b/plugins/lnxsyscalls/syscall.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * syscall.h - prototypes pour la définition d'appels système Linux + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_SYSCALL_H +#define _PLUGINS_LNXSYSCALLS_SYSCALL_H + + +#include <sys/types.h> + + + +/* Information quant à un appel */ +typedef struct _syscall_info_t +{ + unsigned int nr; /* Numéro de l'appel */ + char *name; /* Désignation de l'appel */ + + size_t argc; /* Nombre d'arguments utilisés */ + char *argv[6]; /* Argument possibles */ + + char *filename; /* Origine dans le code source */ + unsigned int line; /* Numéro de ligne dans ce code*/ + +} syscall_info_t ; + + +/* Crée un début de description d'appel système. */ +syscall_info_t *create_syscall_info(unsigned int, const char *); + +/* Efface de la mémoire une description d'appel système. */ +void delete_syscall_info(syscall_info_t *); + +/* Ajoute un argument à une description d'appel système. */ +void append_arg_to_syscall_info(syscall_info_t *, const char *); + + + +#endif /* _PLUGINS_LNXSYSCALLS_SYSCALL_H */ diff --git a/plugins/lnxsyscalls/writer.c b/plugins/lnxsyscalls/writer.c new file mode 100644 index 0000000..1263429 --- /dev/null +++ b/plugins/lnxsyscalls/writer.c @@ -0,0 +1,222 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * writer.c - mise en place de commentaires adaptés aux appels système + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "writer.h" + + +#include <malloc.h> +#include <string.h> + + +#include <analysis/db/items/comment.h> + + + +/* Empilement de commentaire à une adresse donnée */ +typedef struct _comment_data +{ + vmpa2t addr; /* Emplacement de l'insertion */ + + char **text; /* Pièces de texte rapportées */ + size_t count; /* Nombre de ces pièces */ + +} comment_data; + +/* Mémorisation des commentaires à insérer */ +struct _comment_writer +{ + comment_data *comments; /* Définitions de commentaire */ + size_t count; /* Nombre de ces commentaires */ + +}; + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée un espace de conservation pour commentaires d'appels. * +* * +* Retour : Structure de mémorisation en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +comment_writer *create_comment_writer(void) +{ + comment_writer *result; /* Structure à retourner */ + + result = (comment_writer *)malloc(sizeof(comment_writer)); + + result->comments = NULL; + result->count = 0; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : writer = ensemble de commentaires conservés à supprimer. * +* * +* Description : Détruit la conservation de commentaires pour appels. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void delete_comment_writer(comment_writer *writer) +{ + size_t i; /* Boucle de parcours #1 */ + comment_data *data; /* Facilités d'accès */ + size_t k; /* Boucle de parcours #2 */ + + for (i = 0; i < writer->count; i++) + { + data = &writer->comments[i]; + + for (k = 0; k < data->count; k++) + free(data->text[k]); + + if (data->text != NULL) + free(data->text); + + } + + if (writer->comments != NULL) + free(writer->comments); + + free(writer); + +} + + +/****************************************************************************** +* * +* Paramètres : writer = ensemble de commentaires conservés. * +* text = commentaire humainement lisible à insérer. * +* at = instruction dont l'adresse est la destination visée.* +* * +* Description : Complète un commentaire ou en insére un nouveau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void add_comment_at(comment_writer *writer, const char *text, GArchInstruction *at) +{ + const mrange_t *range; /* Couverture d'une instruction*/ + const vmpa2t *addr; /* Adresse de début liée */ + size_t i; /* Boucle de parcours */ + comment_data *target; /* Commentaire à éditer */ + bool new; /* Age de ce commentaire */ + + range = g_arch_instruction_get_range(at); + addr = get_mrange_addr(range); + + for (i = 0; i < writer->count; i++) + { + target = &writer->comments[i]; + + if (cmp_vmpa(addr, &target->addr) == 0) + break; + + } + + new = (i == writer->count); + + if (new) + { + writer->comments = (comment_data *)realloc(writer->comments, ++writer->count * sizeof(comment_data)); + + target = &writer->comments[writer->count - 1]; + + copy_vmpa(&target->addr, addr); + + target->text = NULL; + target->count = 0; + + } + + for (i = 0; i < target->count; i++) + if (strcmp(target->text[i], text) == 0) + break; + + if (i == target->count) + { + target->text = (char **)realloc(target->text, ++target->count * sizeof(char *)); + + target->text[target->count - 1] = strdup(text); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : writer = ensemble de commentaires conservés. * +* preload = contexte de désassemblage avec info préchargées. * +* * +* Description : Applique tous les commentaires à l'écriture anticipée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void write_all_comments(comment_writer *writer, GPreloadInfo *preload) +{ + size_t i; /* Boucle de parcours #1 */ + comment_data *target; /* Commentaire à éditer */ + GDbComment *comment; /* Commentaire final à intégrer*/ + size_t k; /* Boucle de parcours #2 */ + + for (i = 0; i < writer->count; i++) + { + target = &writer->comments[i]; + + comment = g_db_comment_new_inlined(&target->addr, BLF_HAS_CODE, false); + g_db_item_set_volatile(G_DB_ITEM(comment), true); + + for (k = 0; k < target->count; k++) + { + if (k > 0) + g_db_comment_add_static_text(comment, " / "); + + g_db_comment_add_dynamic_text(comment, strdup(target->text[k])); + + } + + g_preload_info_add_comment(preload, comment); + + } + +} diff --git a/plugins/lnxsyscalls/writer.h b/plugins/lnxsyscalls/writer.h new file mode 100644 index 0000000..113acda --- /dev/null +++ b/plugins/lnxsyscalls/writer.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * writer.h - prototypes pour la mise en place de commentaires adaptés aux appels système + * + * Copyright (C) 2018 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_LNXSYSCALLS_WRITER_H +#define _PLUGINS_LNXSYSCALLS_WRITER_H + + +#include <format/preload.h> + + + +/* Mémorisation des commentaires à insérer */ +typedef struct _comment_writer comment_writer; + + +/* Crée un espace de conservation pour commentaires d'appels. */ +comment_writer *create_comment_writer(void); + +/* Détruit la conservation de commentaires pour appels. */ +void delete_comment_writer(comment_writer *); + +/* Complète un commentaire ou en insére un nouveau. */ +void add_comment_at(comment_writer *, const char *, GArchInstruction *); + +/* Applique tous les commentaires à l'écriture anticipée. */ +void write_all_comments(comment_writer *, GPreloadInfo *); + + + +#endif /* _PLUGINS_LNXSYSCALLS_WRITER_H */ |