/* OpenIDA - Outil d'analyse de fichiers binaires * theseus.c - décompilation en fonction du flot d'exécution * * Copyright (C) 2010-2011 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA 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. * * OpenIDA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "theseus.h" #include #include #include /* Traite en premier lieu les adresses marquées "impératives". */ static bool register_white_list(GOpenidaBinary *); /* Indique si une ligne peut faire l'objet d'un nouveau suivi. */ static bool can_line_be_followed(const GRenderingLine *); /* Indique si une ligne a déjà été traitée lors d'un suivi. */ static bool can_line_be_processed(const GRenderingLine *); /* Marque une ligne comme ayant été traitée. */ static void mark_line_as_done(GRenderingLine *, bool); /* Suit le flot d'exécution à partir d'un point donné. */ static bool follow_flow_from_line(GOpenidaBinary *, vmpa_t); /* Désassemble une nouvelle instruction à partir d'une adresse. */ static GRenderingLine *disassemble_target_address(GOpenidaBinary *, GRenderingLine *, vmpa_t); /* Insère dans le flux existant les nouvelles lignes crées. */ static bool insert_new_lines_into_flow(GOpenidaBinary *, GRenderingLine *, GRenderingLine *); /* mov edx, 0x80480fb call [edx] mov edx, 0x80480fb add edx, 0x8 call [edx] mov edx, 0x80480fb add edx, 0x10 call [edx] mov edx, 0x80480fb add edx, 0xc call [edx] mov edx, 0x80480fb add edx, 0x14 call [edx] mov edx, 0x80480fb add edx, 0x4 call [edx] */ static const vmpa_t _white_list[] = { 0x80480fbull, 0x8048103ull, 0x804810bull, 0x8048107ull, 0x804810full, 0x80480ffull }; static const size_t _white_list_count = 6; /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * * * Description : Initialise le greffon pour les bornes de routine. * * * * Retour : true. * * * * Remarques : - * * * ******************************************************************************/ G_MODULE_EXPORT bool init_plugin(GObject *ref) { return true; } /****************************************************************************** * * * Paramètres : - * * * * Description : Fournit une indication sur le type d'opération(s) menée(s). * * * * Retour : Description d'une action. * * * * Remarques : - * * * ******************************************************************************/ G_MODULE_EXPORT PluginAction get_plugin_action(void) { return PGA_CODE_PROCESS; } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * action = action attendue. * * * * Description : Exécute une action définie sur un binaire chargé. * * * * Retour : true si une action a été menée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ G_MODULE_EXPORT bool execute_action_on_binary(GOpenidaBinary *binary, PluginAction action) { bool result; /* Bilan à retourner */ GRenderingLine *lines; /* Lignes désassemblées */ GExeFormat *format; /* Format du binaire traité */ vmpa_t start; /* Point de départ de l'action */ GRenderingOptions *options; GRenderingLine *iter; lines = g_openida_binary_get_lines(binary); format = g_openida_binary_get_format(binary); start = g_exe_format_get_entry_point(format); result = register_white_list(binary); result &= follow_flow_from_line(binary, start); //follow_flow_from_line(binary, 0x0804810bull); options = g_openida_binary_get_options(binary); for (iter = lines; iter != NULL; iter = g_rendering_line_get_next_iter(lines, iter, NULL)) { printf(" >>>>>> %c%c ", g_object_get_data(G_OBJECT(iter), "theseus_white") != NULL ? '!' : ' ', g_object_get_data(G_OBJECT(iter), "theseus_done") != NULL ? '*' : ' '); g_content_exporter_add_text(G_CONTENT_EXPORTER(iter), options, 0/*MRD_BLOCK*/, stdout); fflush(stdout); printf("\n"); } //exit(-1); return result; } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * * * Description : Traite en premier lieu les adresses marquées "impératives". * * * * Retour : true si le déroulement s'est bien effectué, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool register_white_list(GOpenidaBinary *binary) { bool result; /* Bilan à retourner */ GRenderingLine *lines; /* Lignes désassemblées */ size_t i; /* Boucle de parcours */ GRenderingLine *target; /* Ligne à transformer ? */ GRenderingLine *last; /* Nouvelle ligne principale */ GRenderingLine *new; /* Nouvelles lignes de rendu */ result = true; lines = g_openida_binary_get_lines(binary); for (i = 0; i < _white_list_count && result; i++) { target = g_rendering_line_find_by_address(lines, NULL, _white_list[i]); target = g_rendering_line_loop_for_code(target, NULL); new = disassemble_target_address(binary, target, _white_list[i]); last = g_rendering_line_get_last_iter(new, NULL); mark_line_as_done(last, true); result = insert_new_lines_into_flow(binary, target, new); result &= follow_flow_from_line(binary, _white_list[i]); } return result; } /****************************************************************************** * * * Paramètres : line = ligne de rendu à analyser. * * * * Description : Indique si une ligne peut faire l'objet d'un nouveau suivi. * * * * Retour : true si le suivi peut continuer, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool can_line_be_followed(const GRenderingLine *line) { bool result; /* Bilan d'analyse à renvoyer */ result = true; if (g_object_get_data(G_OBJECT(line), "theseus_done") != NULL) result = (g_object_get_data(G_OBJECT(line), "theseus_white") != NULL); return result; } /****************************************************************************** * * * Paramètres : line = ligne de rendu à analyser. * * * * Description : Indique si une ligne a déjà été traitée lors d'un suivi. * * * * Retour : true si le suivi peut continuer, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool can_line_be_processed(const GRenderingLine *line) { return (g_object_get_data(G_OBJECT(line), "theseus_done") == NULL); } /****************************************************************************** * * * Paramètres : line = ligne de rendu à traiter. * * white = type de marquage à apposer (liste blanche ou normal).* * * * Description : Marque une ligne comme ayant été traitée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mark_line_as_done(GRenderingLine *line, bool white) { if (white) g_object_set_data(G_OBJECT(line), "theseus_white", line); else g_object_set_data(G_OBJECT(line), "theseus_done", line); } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * start = point de départ de la procédure. * * * * Description : Suit le flot d'exécution à partir d'un point donné. * * * * Retour : true si le déroulement s'est bien effectué, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool follow_flow_from_line(GOpenidaBinary *binary, vmpa_t start) { bool result; /* Bilan de opérations */ GRenderingLine *lines; /* Lignes désassemblées */ GRenderingLine *first; /* Première ligne à traiter */ vmpa_t addr; /* Adresse référencée */ GRenderingLine *new; /* Nouvelles lignes de rendu */ GRenderingLine *iter; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction à ausculter */ InstructionLinkType type; /* Type de référence */ GRenderingLine *target; /* Ligne visée par la référence*/ lines = g_openida_binary_get_lines(binary); first = g_rendering_line_find_by_address(lines, NULL, start); first = g_rendering_line_loop_for_code(first, NULL); if (!can_line_be_followed(first)) return true; //if (start != 0x0804810bull) return true; printf("###################################### Passage\n"); /* Alignement sur le point d'entrée */ result = true; addr = get_rendering_line_address(first); if (addr < start) { new = disassemble_target_address(binary, first, start); result = insert_new_lines_into_flow(binary, target, new); first = g_rendering_line_find_by_address(lines, NULL, start); //return true; } else g_object_set_data(G_OBJECT(first), "theseus_done", binary); printf("Analyse :: 0x%016llx\n", get_rendering_line_address(first)); /* Poursuite du suivi du flot... */ for (iter = g_rendering_line_get_next_iter(lines, first, NULL); iter != NULL/* && result*/; iter = g_rendering_line_get_next_iter(lines, iter, NULL)) { /* On ne traite que du code ici ! */ if (!G_IS_CODE_LINE(iter)) continue; /* printf("testing... 0x%08llx => %d\n", get_rendering_line_address(iter), can_line_be_processed(iter)); */ if (!can_line_be_processed(iter)) break; instr = g_code_line_get_instruction(G_CODE_LINE(iter)); /* Si le code n'es pas désassemblé ! */ if (G_IS_DB_INSTRUCTION(instr)) { new = disassemble_target_address(binary, iter, get_rendering_line_address(iter)); result = insert_new_lines_into_flow(binary, iter, new); if (!result) { mark_line_as_done(iter, false); break; } else { iter = new; mark_line_as_done(iter, false); instr = g_code_line_get_instruction(G_CODE_LINE(iter)); if (!G_IS_DB_INSTRUCTION(instr)) break; } } else mark_line_as_done(iter, false); type = g_arch_instruction_get_link(instr, &addr); if (get_rendering_line_address(iter) == 0x0804811aull) printf(" == got it !! ==\n"); switch (type) { case ILT_JUMP: case ILT_JUMP_IF_FALSE: case ILT_JUMP_IF_TRUE: result = follow_flow_from_line(binary, addr); break; default: break; } if (get_rendering_line_address(iter) == 0x0804811aull) printf(" == continue ? %d\n", result); } return result; } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * old = ligne contenant l'adresse visée à remplacer. * * target = adresse de la nouvelle instruction à voir. * * * * Description : Désassemble une nouvelle instruction à partir d'une adresse. * * * * Retour : Nouvelle(s) ligne(s) en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ static GRenderingLine *disassemble_target_address(GOpenidaBinary *binary, GRenderingLine *old, vmpa_t target) { GRenderingLine *result; /* Lignes en place à renvoyer */ GExeFormat *format; /* Format du binaire fourni */ GArchProcessor *proc; /* Architecture du binaire */ GRenderingOptions *options; /* Options de désassemblage */ bin_t *data; /* Données binaires chargées */ off_t length; /* Taille du code binaire */ vmpa_t old_addr; /* Adresse de ligne à casser */ off_t offset; /* Position dans le contenu */ vmpa_t i; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction décodée */ GRenderingLine *line; /* Nouvelle ligne de rendu */ result = NULL; format = g_openida_binary_get_format(binary); proc = get_arch_processor_from_format(format); options = g_openida_binary_get_options(binary); data = g_openida_binary_get_data(binary, &length); old_addr = get_rendering_line_address(old); if (!g_exe_format_translate_address_into_offset(format, old_addr, &offset)) return NULL; /* Si on doit laisser sur le carreau quelques octets perdus... */ for (i = 0; i < (target - old_addr); i++) { instr = g_db_instruction_new_from_data(data, &offset, length, 0/*base*/, proc); g_arch_instruction_set_location(instr, 0/*base*/ + offset - 1, 1, old_addr + i); line = g_code_line_new(old_addr + i, instr, options); g_rendering_line_add_to_lines(&result, line); } /* Décodage des instructions */ instr = g_arch_processor_decode_instruction(proc, NULL /*FIXME*/, data, &offset, length, 0/*offset*/, target); line = g_code_line_new(target, instr, options); g_rendering_line_add_to_lines(&result, line); mark_line_as_done(line, false); return result; } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * old = ancienne ligne, point de départ des remplacements. * * new = nouvelle(s) ligne(s) de représentation. * * * * Description : Insère dans le flux existant les nouvelles lignes crées. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool insert_new_lines_into_flow(GOpenidaBinary *binary, GRenderingLine *old, GRenderingLine *new) { bool result; /* Bilan de opérations */ GExeFormat *format; /* Format du binaire fourni */ GArchProcessor *proc; /* Architecture du binaire */ GRenderingOptions *options; /* Options de désassemblage */ bin_t *data; /* Données binaires chargées */ off_t length; /* Taille du code binaire */ GRenderingLine **root; /* Racine des lignes de rendu */ GRenderingLine *iter; /* Boucle de parcours */ vmpa_t start; /* Adresse de début du bloc */ vmpa_t end; /* Adresse de fin du bloc */ vmpa_t max; /* Adresse de fin de ligne */ vmpa_t i; /* Boucle de parcours */ off_t offset; /* Position dans le contenu */ GArchInstruction *instr; /* Instruction décodée */ GRenderingLine *line; /* Nouvelle ligne de rendu */ GRenderingLine *last; /* Dernière ligne à vérifier */ format = g_openida_binary_get_format(binary); proc = get_arch_processor_from_format(format); options = g_openida_binary_get_options(binary); data = g_openida_binary_get_data(binary, &length); /* Etendue du bloc de remplacement */ iter = g_rendering_line_get_last_iter(new, NULL); start = get_rendering_line_address(new); end = get_rendering_line_address(iter); end += get_rendering_line_length(iter) - 1; /* Bourrage nécessaire ? */ root = g_openida_binary_get_lines_root(binary); iter = g_rendering_line_find_by_address(*root, NULL, end); max = get_rendering_line_address(iter); max += get_rendering_line_length(iter); for (i = end + 1; i < max; i++) { if (!g_exe_format_translate_address_into_offset(format, i, &offset)) /** * Comme les adresses proviennent à l'origine de partie valide, * on considère que l'appel est toujours un succès. * Cela permet d'avoir deux bilan possible : procédure bien déroulée, * ou bien remplacement impossible pour cause de place déjà prise. */ /*return false*/; instr = g_db_instruction_new_from_data(data, &offset, length, 0/*base*/, proc); g_arch_instruction_set_location(instr, 0/*base*/ + offset - 1, 1, i); line = g_code_line_new(i, instr, options); g_rendering_line_add_to_lines(&new, line); } /* S'assure qu'on ne touche à rien de sacré */ result = true; last = g_rendering_line_find_by_address(*root, NULL, end); for (iter = g_rendering_line_find_by_address(*root, NULL, start); iter != NULL && result; iter = g_rendering_line_get_next_iter(*root, iter, last)) { result = can_line_be_processed(iter); } /* Remplacement en bonne et due forme */ if (result) { g_rendering_line_remove_range(root, start, end); g_rendering_line_insert_lines(root, &new); } return result; }