summaryrefslogtreecommitdiff
path: root/plugins/lnxsyscalls/collect.c
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2018-06-17 16:11:45 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2018-06-17 16:11:45 (GMT)
commit378be1ab322dce8e8377d692829d6877758e5960 (patch)
tree17dc518687a45649caa68304cc2a5750a0a50554 /plugins/lnxsyscalls/collect.c
parent1f7e9506775f66a3a5f2859779d33b914eee8ef4 (diff)
Annotated linux kernel syscalls using a new plugin.
Diffstat (limited to 'plugins/lnxsyscalls/collect.c')
-rw-r--r--plugins/lnxsyscalls/collect.c643
1 files changed, 643 insertions, 0 deletions
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;
+
+}