/* OpenIDA - Outil d'analyse de fichiers binaires * il.h - mise en place d'un langage intermédiaire * * Copyright (C) 2012 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 "il.h" #include "../blocks/flow.h" #include "../blocks/virtual.h" #include "../../decomp/expr/block.h" #include "../../decomp/instr/ite.h" /* --------------------- GESTION DES CONTEXTES DE DECOMPILATION --------------------- */ /* Détermine les registres utilisés avant leur initialisation. */ static bool track_used_registers(GFlowBlock *, BlockFollowPosition, GRAccessList **); /* Etablit le relévé des allocations de registre. */ static void setup_awaited_regs_allocation(const GInstrBlock *, vmpa_t); /* Met en place un contexte adapté aux sous-blocs d'un bloc. */ static GDecContext *create_new_context_for_sub_block(GDecContext *, GInstrBlock *); /* -------------------------- ENCADREMENT DES INSTRUCTIONS -------------------------- */ /* Retrouve et rassemble les instructions décompilées. */ static GDecInstruction *merge_decompiled_instructions(GDecInstruction *, GDecInstruction *); /* Procède à la décompilation d'un bloc déterminé. */ static GDecInstruction *decompiled_instructions_block(GFlowBlock *, GDecContext *); /* Procède à la décompilation d'un ensemble de blocs déterminé. */ static GDecInstruction *decompiled_instructions_blocks(GVirtualBlock *, GDecContext *); /* Procède à la décompilation d'un bloc basique quelconque. */ static GDecInstruction *decompiled_basic_block(GInstrBlock *, GDecContext *); /* ---------------------------------------------------------------------------------- */ /* GESTION DES CONTEXTES DE DECOMPILATION */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : block = bloc d'instructions visité. * * pos = indication sur la position du parcours. * * needed = suivi de l'usage des registres entre les blocs. * * * * Description : Détermine les registres utilisés avant leur initialisation. * * * * Retour : true pour mener un parcours complet. * * * * Remarques : - * * * ******************************************************************************/ static bool track_used_registers(GFlowBlock *block, BlockFollowPosition pos, GRAccessList **needed) { GRAccessList *old; /* Ancienne liste remplacée */ const GRAccessList *accesses; /* Accès divers aux registres */ size_t count; /* Taille d'un parcours */ GRAccessList *awaited; /* Satisfactions des besoins */ reg_access *access; /* Accès à un registre */ size_t i; /* Boucle de parcours */ reg_access *found; /* Besoin trouvé ou non */ switch (pos) { case BFP_ENTER: g_object_set_data(G_OBJECT(block), "needed_regs", *needed); break; case BFP_FOLLOW: *needed = g_raccess_list_new(); break; case BFP_BACK: old = *needed; *needed = G_RACCESS_LIST(g_object_get_data(G_OBJECT(block), "needed_regs")); g_raccess_list_merge(*needed, old); g_object_unref(G_OBJECT(old)); break; case BFP_EXIT: g_object_set_data(G_OBJECT(block), "needed_regs", NULL); accesses = g_flow_block_list_regs_accesses(block); count = g_raccess_list_count(accesses); awaited = g_flow_block_list_awaited_regs(block); for (i = 0; i < count; i++) { access = g_raccess_list_get(accesses, i); /* Enregistrement des contributions possibles */ found = g_raccess_list_find(*needed, access->reg); if (found != NULL) { /** * Si un autre bloc avait déjà un besoin, on ne prend note * qu'une seule fois ! */ if (g_raccess_list_find(awaited, access->reg) == NULL) g_raccess_list_add(awaited, access); g_raccess_list_delete(*needed, found); } /* Ajoute les besoins du bloc */ if (access->first_access == RAT_READ) g_raccess_list_add(*needed, access); } do { vmpa_t start, end; g_flow_block_get_boundary_addresses(block, &start, &end); printf(" -> flow (%d) : 0x%08x - 0x%08x | needed = %zu - provided = %zu\n", pos, start, end, g_raccess_list_count(*needed), g_raccess_list_count(awaited)); } while (0); break; } return true; } /****************************************************************************** * * * Paramètres : list = ensemble des instructions d'assemblage à traiter. * * start = adresse de départ de la routine visée. * * * * Description : Etablit le relévé des allocations de registre. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void setup_awaited_regs_allocation(const GInstrBlock *list, vmpa_t start) { GInstrBlock *first; /* Bloc de départ du flot */ GRAccessList *needed; /* Registres inter-blocs */ first = g_instr_block_find_by_addr(list, start, true); needed = g_raccess_list_new(); g_flow_block_follow(G_FLOW_BLOCK(first), list, BFP_ENTER | BFP_EXIT | BFP_EXIT, (flow_block_follow_cb)track_used_registers, &needed); g_object_unref(G_OBJECT(needed)); } /****************************************************************************** * * * Paramètres : ctx = contexte de décompilation courant. * * block = block regroupant les branches de division. * * * * Description : Met en place un contexte adapté aux sous-blocs d'un bloc. * * * * Retour : Nouveau contexte près à emploi. * * * * Remarques : - * * * ******************************************************************************/ static GDecContext *create_new_context_for_sub_block(GDecContext *ctx, GInstrBlock *block) { return g_dec_context_dup(ctx); } /* ---------------------------------------------------------------------------------- */ /* ENCADREMENT DES INSTRUCTIONS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : group = groupe d'instructions à compléter ou constituer. * * list = liste d'instructions à intégrer. * * * * Description : Retrouve et rassemble les instructions décompilées. * * * * Retour : Groupe fourni ou nouveau groupe créé pour l'occasion. * * * * Remarques : - * * * ******************************************************************************/ static GDecInstruction *merge_decompiled_instructions(GDecInstruction *group, GDecInstruction *list) { GExprBlock *block; /* Autre vision du groupe */ GDecInstruction *iter; /* Boucle de parcours */ if (group == NULL) block = NULL; else block = G_EXPR_BLOCK(group); for (iter = list; iter != NULL; iter = g_dec_instruction_get_next_iter(list, iter)) { if (block == NULL) block = G_EXPR_BLOCK(g_expr_block_new(iter)); else g_expr_block_add_item(block, iter); } return G_DEC_INSTRUCTION(block); } /****************************************************************************** * * * Paramètres : block = ensemble des instructions d'assemblage à traiter. * * ctx = contexte de soutien à associer à l'opération. * * * * Description : Procède à la décompilation d'un bloc déterminé. * * * * Retour : Instructions créées et enregistrées, ou NULL si erreur. * * * * Remarques : - * * * ******************************************************************************/ static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecContext *ctx) { GDecInstruction *res; GArchInstruction *instrs; /* Liste d'instructions natives*/ GArchInstruction *first; /* Première instruction du lot */ GArchInstruction *last; /* Dernière instruction du lot */ vmpa_t max; /* Adresse de fin du bloc */ GArchInstruction *iter; /* Boucle de parcours */ GDecInstruction *decomp; /* Dernier résultat de décomp. */ GInstrBlock *sub_parent; /* Groupe des sous-branches */ GDecContext *sub_ctx; /* Sous-contexte pour branche */ GDecInstruction *true_dinstr; /* Décompilation 'cond vraie' */ GDecInstruction *false_dinstr; /* Décompilation 'cond fausse' */ GArchInstruction *next; /* Instruction de branchement */ vmpa_t next_addr; /* Adresse de cette instruct° */ //GInstrBlock *next_parent; /* Bloc basique correspondant */ GInstrBlock *next_block; /* Sous-bloc basique direct */ instrs = g_flow_block_get_all_instructions_list(block); g_flow_block_get_boundary(block, &first, &last); g_flow_block_get_boundary_addresses(block, NULL, &max); max++; /* Décompilation du corps du bloc */ for (iter = first; iter != NULL; iter = g_arch_instruction_get_next_iter(instrs, iter, max)) { decomp = g_arch_instruction_decompile(iter, ctx); } /* Post-traitement selon les types de lien */ //res = g_dec_context_get_decomp_instrs(ctx); /* if ... then ... else ... */ if (G_IS_ITE_INSTRUCTION(decomp)) { sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); true_dinstr = NULL; next = g_arch_instruction_get_given_destination(last, ILT_JUMP_IF_TRUE); if (next != NULL) { g_arch_instruction_get_location(next, NULL, NULL, &next_addr); next_block = g_instr_block_find_by_addr(sub_parent, next_addr, false); if (next_block != NULL) { sub_ctx = create_new_context_for_sub_block(ctx, sub_parent); true_dinstr = decompiled_basic_block(next_block, sub_ctx); /* TODO : merge awaited */ g_object_unref(G_OBJECT(sub_ctx)); } } false_dinstr = NULL; next = g_arch_instruction_get_given_destination(last, ILT_JUMP_IF_FALSE); if (next != NULL) { g_arch_instruction_get_location(next, NULL, NULL, &next_addr); next_block = g_instr_block_find_by_addr(sub_parent, next_addr, false); if (next_block != NULL) { sub_ctx = create_new_context_for_sub_block(ctx, sub_parent); false_dinstr = decompiled_basic_block(next_block, sub_ctx); /* TODO : merge awaited */ g_object_unref(G_OBJECT(sub_ctx)); } } printf(" -> ite : %p + %p\n", true_dinstr, false_dinstr); printf(" -> ite : %s + %s\n", true_dinstr ? g_type_name(G_TYPE_FROM_INSTANCE(true_dinstr)) : "none", false_dinstr ? g_type_name(G_TYPE_FROM_INSTANCE(false_dinstr)) : "none"); g_ite_instruction_set_branches(G_ITE_INSTRUCTION(decomp), true_dinstr, false_dinstr); } /* Renvoi des instructions mises en place */ //return res; return g_dec_context_get_decomp_instrs(ctx); } /****************************************************************************** * * * Paramètres : block = ensemble des instructions d'assemblage à traiter. * * ctx = contexte de soutien à associer à l'opération. * * * * Description : Procède à la décompilation d'un ensemble de blocs déterminé. * * * * Retour : Instructions créées et enregistrées, ou NULL si erreur. * * * * Remarques : - * * * ******************************************************************************/ static GDecInstruction *decompiled_instructions_blocks(GVirtualBlock *block, GDecContext *ctx) { GDecInstruction *result; /* Instructions à renvoyer */ size_t count; /* Nombre de sous-blocs */ size_t i; /* Boucle de parcours */ GInstrBlock *sub_block; /* Sous-bloc à traiter */ GDecInstruction *dinstrs; /* Instructions décompilées */ result = NULL; count = g_virtual_block_count_children(block); for (i = 0; i < count; i++) { sub_block = g_virtual_block_get_child(block, i); /** * Les groupes de blocs sont forcément rattachés à une instruction, * donc ils sont décompilés depuis ces instructions, pas ici. */ if (!G_IS_FLOW_BLOCK(sub_block)) continue; /* FIXME */ //g_dec_context_set_decomp_instrs(ctx, NULL); dinstrs = decompiled_instructions_block(G_FLOW_BLOCK(sub_block), ctx); //result = merge_decompiled_instructions(result, dinstrs); } result = merge_decompiled_instructions(result, dinstrs); return result; } /****************************************************************************** * * * Paramètres : block = ensemble des instructions d'assemblage à traiter. * * ctx = contexte de soutien à associer à l'opération. * * * * Description : Procède à la décompilation d'un bloc basique quelconque. * * * * Retour : Instructions créées et enregistrées, ou NULL si erreur. * * * * Remarques : - * * * ******************************************************************************/ static GDecInstruction *decompiled_basic_block(GInstrBlock *block, GDecContext *ctx) { GDecInstruction *result; /* Instructions à retourner */ if (G_IS_VIRTUAL_BLOCK(block)) result = decompiled_instructions_blocks(G_VIRTUAL_BLOCK(block), ctx); else { result = decompiled_instructions_block(G_FLOW_BLOCK(block), ctx); if (!G_IS_EXPR_BLOCK(result)) result = merge_decompiled_instructions(NULL, result); } return result; } /****************************************************************************** * * * Paramètres : routine = routine dont le corps est à traiter. * * format = format du binaire contenant les instructions. * * proc = architecture du code machine. * * * * Description : Procède à la décompilation d'une routinée déterminée. * * * * Retour : Instructions créées ou NULL si erreur. * * * * Remarques : - * * * ******************************************************************************/ GDecInstruction *decompiled_routine_instructions(GBinRoutine *routine, GExeFormat *format, GArchProcessor *proc) { GDecInstruction *result; /* Instructions à retourner */ GDecContext *context; /* Contexte pour la décompil. */ GInstrBlock *blocks; /* Blocs basiques de routine */ context = g_arch_processor_get_decomp_context(proc); g_dec_context_set_info(context, routine, format); blocks = g_binary_routine_get_basic_blocks(routine); setup_awaited_regs_allocation(blocks, g_binary_routine_get_address(routine)); result = decompiled_basic_block(blocks, context); g_object_unref(context); return result; }