diff options
Diffstat (limited to 'src/analysis/decomp')
-rw-r--r-- | src/analysis/decomp/il.c | 373 |
1 files changed, 274 insertions, 99 deletions
diff --git a/src/analysis/decomp/il.c b/src/analysis/decomp/il.c index f241e46..9e235a6 100644 --- a/src/analysis/decomp/il.c +++ b/src/analysis/decomp/il.c @@ -24,11 +24,15 @@ #include "il.h" +#include <malloc.h> + + #include "../blocks/flow.h" #include "../blocks/virtual.h" #include "../../decomp/expr/block.h" #include "../../decomp/expr/immediate.h" #include "../../decomp/instr/ite.h" +#include "../../decomp/instr/keyword.h" #include "../../decomp/instr/switch.h" @@ -67,6 +71,20 @@ static GDecInstruction *decompiled_basic_block(GInstrBlock *, GDecContext *); +/* --------------------------- DECOMPILATIONS SPECIFIQUES --------------------------- */ + + +/* Procède à la décompilation des éléments d'un 'if then else'. */ +static void build_ite_branches(GITEInstruction *, GFlowBlock *, GDecContext *); + +/* Termine le traitement d'un cas de 'switch'. */ +static void close_case_decomp_instructions(GDecInstruction *, GInstrBlock *, GArchInstruction **, size_t, size_t); + +/* Procède à la décompilation des éléments d'un 'switch'. */ +static void build_switch_branches(GSwitchInstruction *, GFlowBlock *, GDecContext *); + + + /* ---------------------------------------------------------------------------------- */ /* GESTION DES CONTEXTES DE DECOMPILATION */ /* ---------------------------------------------------------------------------------- */ @@ -328,27 +346,12 @@ static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecCon { 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 #1 */ GDecInstruction *decomp; /* Dernier résultat de décomp. */ - GInstrBlock *sub_parent; /* Groupe des sous-branches */ - GHashTable *sub_shared; /* Allocations communes */ - 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_block; /* Sous-bloc basique direct */ - GArchInstruction **dests; /* Instr. visée par une autre */ - link_extra_info *info; /* Compléments pour les liens */ - size_t dcount; /* Nombre de liens de dest. */ - size_t i; /* Boucle de parcours #2 */ - GDecInstruction *case_dinstr; /* Décompilation 'case' */ - GDecExpression *case_value; /* Valeur d'aiguillage */ instrs = g_flow_block_get_all_instructions_list(block); - g_flow_block_get_boundary(block, &first, &last); + g_flow_block_get_boundary(block, &first, NULL); g_flow_block_get_boundary_addresses(block, NULL, &max); max++; @@ -366,92 +369,11 @@ static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecCon /* if ... then ... else ... */ if (G_IS_ITE_INSTRUCTION(decomp)) - { - sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); - sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash, - (GEqualFunc)g_arch_register_equal, - g_object_unref, g_object_unref); - - 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, next_block, sub_shared); - true_dinstr = decompiled_basic_block(next_block, sub_ctx); - g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); - 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, next_block, sub_shared); - false_dinstr = decompiled_basic_block(next_block, sub_ctx); - g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); - g_object_unref(G_OBJECT(sub_ctx)); - } - - } - - g_ite_instruction_set_branches(G_ITE_INSTRUCTION(decomp), true_dinstr, false_dinstr); - - g_hash_table_unref(sub_shared); - - } + build_ite_branches(G_ITE_INSTRUCTION(decomp), block, ctx); /* switch ... case ... */ else if (G_IS_SWITCH_INSTRUCTION(decomp)) - { - sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); - sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash, - (GEqualFunc)g_arch_register_equal, - g_object_unref, g_object_unref); - - dcount = g_arch_instruction_get_destinations(last, &dests, NULL, &info); - - for (i = 0; i < dcount; i++) - { - g_arch_instruction_get_location(dests[i], 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, next_block, sub_shared); - case_dinstr = decompiled_basic_block(next_block, sub_ctx); - g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); - g_object_unref(G_OBJECT(sub_ctx)); - - if (info[i].imm != NULL) - { - case_value = G_DEC_EXPRESSION(g_imm_expression_new(info[i].imm)); - g_switch_instruction_add_case(G_SWITCH_INSTRUCTION(decomp), - case_value, case_dinstr, next_addr); - } - else g_switch_instruction_set_default_case(G_SWITCH_INSTRUCTION(decomp), - case_dinstr); - - } - - } - - g_hash_table_unref(sub_shared); - - } + build_switch_branches(G_SWITCH_INSTRUCTION(decomp), block, ctx); /* Renvoi des instructions mises en place */ @@ -578,3 +500,256 @@ GDecInstruction *decompiled_routine_instructions(GBinRoutine *routine, GExeForma return result; } + + + +/* ---------------------------------------------------------------------------------- */ +/* DECOMPILATIONS SPECIFIQUES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : decomp = instruction 'if ... then ... else' à compléter. * +* block = ensemble des instructions d'assemblage traitées. * +* ctx = contexte de soutien à associer à l'opération. * +* * +* Description : Procède à la décompilation des éléments d'un 'if then else'. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void build_ite_branches(GITEInstruction *decomp, GFlowBlock *block, GDecContext *ctx) +{ + GArchInstruction *last; /* Dernière instruction du lot */ + GInstrBlock *sub_parent; /* Groupe des sous-branches */ + GHashTable *sub_shared; /* Allocations communes */ + 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_block; /* Sous-bloc basique direct */ + + g_flow_block_get_boundary(block, NULL, &last); + + sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); + sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash, + (GEqualFunc)g_arch_register_equal, + g_object_unref, g_object_unref); + + true_dinstr = NULL; + false_dinstr = NULL; + + /* Branche 'true' */ + 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, next_block, sub_shared); + true_dinstr = decompiled_basic_block(next_block, sub_ctx); + g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); + g_object_unref(G_OBJECT(sub_ctx)); + } + + } + + /* Branche 'false' */ + 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, next_block, sub_shared); + false_dinstr = decompiled_basic_block(next_block, sub_ctx); + g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); + g_object_unref(G_OBJECT(sub_ctx)); + } + + } + + g_ite_instruction_set_branches(decomp, true_dinstr, false_dinstr); + + g_hash_table_unref(sub_shared); + +} + + +/****************************************************************************** +* * +* Paramètres : case_dinstr = instructions d'un cas de 'switch' décompilées. * +* case_block = bloc d'instructions assembleur correspondantes.* +* cases = listes des instructions des différents cas. * +* current = indice du cas courant. * +* ccount = nombre de cas présents dans le 'switch'. * +* * +* Description : Termine le traitement d'un cas de 'switch'. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void close_case_decomp_instructions(GDecInstruction *case_dinstr, GInstrBlock *case_block, GArchInstruction **cases, size_t current, size_t ccount) +{ + vmpa_t *cases_addr; /* Adresses des cas du 'switch'*/ + size_t i; /* Boucle de parcours #1 */ + GInstrBlock **leafs; /* Blocs terminaux du cas */ + size_t lcount; /* Nombre de blocs terminaux */ + vmpa_t common_addr; /* Adresse commune de suite */ + bool is_common; /* Suite commune ? */ + GArchInstruction *last; /* Dernière instruction de bloc*/ + GArchInstruction **dests; /* Instr. visée par une autre */ + size_t dcount; /* Nombre de liens de dest. */ + size_t j; /* Boucle de parcours #2 */ + vmpa_t addr; /* Adresse d'une instruction */ + bool jump_to_case; /* Suite dans le cas suivant ? */ + + /* Etablit la liste des adresses des cas */ + + cases_addr = (vmpa_t *)calloc(ccount, sizeof(vmpa_t)); + + for (i = 0; i < ccount; i++) + g_arch_instruction_get_location(cases[i], NULL, NULL, &cases_addr[i]); + + /* Récupère la (les) fin(s) du cas présent */ + + leafs = NULL; + lcount = 0; + + g_instr_block_list_leafs_blocks(case_block, &leafs, &lcount); + + /* Procède à une première analyse de la suite */ + + common_addr = VMPA_MAX; + is_common = true; + + for (i = 0; i < lcount && is_common; i++) + { + g_flow_block_get_boundary(G_FLOW_BLOCK(leafs[i]), NULL, &last); + dcount = g_arch_instruction_get_destinations(last, &dests, NULL, NULL); + + for (j = 0; j < dcount && is_common; j++) + { + g_arch_instruction_get_location(dests[j], NULL, NULL, &addr); + + if (common_addr == VMPA_MAX) + common_addr = addr; + else + is_common = (common_addr == addr); + + } + + } + + /* La sortie du cas est unique ! */ + if (is_common) + { + if (common_addr == VMPA_MAX) + goto ecdi_exit; + + jump_to_case = false; + + for (i = 0; i < ccount && !jump_to_case; i++) + if (i != current) + jump_to_case = (cases_addr[i] == common_addr); + + if (!jump_to_case) + g_expr_block_add_item(G_EXPR_BLOCK(case_dinstr), g_keyword_instruction_new(DKW_BREAK)); + + } + + /* ... ou il faut suivre les différentes branches... */ + else + { + + /* TODO */ + + } + + ecdi_exit: + + free(cases_addr); + +} + + +/****************************************************************************** +* * +* Paramètres : decomp = instruction 'switch' à compléter. * +* block = ensemble des instructions d'assemblage traitées. * +* ctx = contexte de soutien à associer à l'opération. * +* * +* Description : Procède à la décompilation des éléments d'un 'switch'. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void build_switch_branches(GSwitchInstruction *decomp, GFlowBlock *block, GDecContext *ctx) +{ + GArchInstruction *last; /* Dernière instruction du lot */ + GInstrBlock *sub_parent; /* Groupe des sous-branches */ + GHashTable *sub_shared; /* Allocations communes */ + GDecContext *sub_ctx; /* Sous-contexte pour branche */ + vmpa_t next_addr; /* Adresse de cette instruct° */ + GInstrBlock *next_block; /* Sous-bloc basique direct */ + GArchInstruction **dests; /* Instr. visée par une autre */ + link_extra_info *info; /* Compléments pour les liens */ + size_t dcount; /* Nombre de liens de dest. */ + size_t i; /* Boucle de parcours #2 */ + GDecInstruction *case_dinstr; /* Décompilation 'case' */ + GDecExpression *case_value; /* Valeur d'aiguillage */ + + g_flow_block_get_boundary(block, NULL, &last); + + sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); + sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash, + (GEqualFunc)g_arch_register_equal, + g_object_unref, g_object_unref); + + dcount = g_arch_instruction_get_destinations(last, &dests, NULL, &info); + + for (i = 0; i < dcount; i++) + { + g_arch_instruction_get_location(dests[i], 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, next_block, sub_shared); + case_dinstr = decompiled_basic_block(next_block, sub_ctx); + g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); + g_object_unref(G_OBJECT(sub_ctx)); + + close_case_decomp_instructions(case_dinstr, next_block, dests, i, dcount); + + if (info[i].imm != NULL) + { + case_value = G_DEC_EXPRESSION(g_imm_expression_new(info[i].imm)); + g_switch_instruction_add_case(G_SWITCH_INSTRUCTION(decomp), + case_value, case_dinstr, next_addr); + } + else g_switch_instruction_set_default_case(G_SWITCH_INSTRUCTION(decomp), + case_dinstr); + + } + + } + + g_hash_table_unref(sub_shared); + +} |