diff options
-rw-r--r-- | ChangeLog | 31 | ||||
-rw-r--r-- | plugins/androhelpers/switch.c | 46 | ||||
-rw-r--r-- | src/analysis/block-int.h | 5 | ||||
-rw-r--r-- | src/analysis/block.c | 21 | ||||
-rw-r--r-- | src/analysis/block.h | 3 | ||||
-rw-r--r-- | src/analysis/blocks/flow.c | 27 | ||||
-rw-r--r-- | src/analysis/blocks/virtual.c | 27 | ||||
-rw-r--r-- | src/analysis/decomp/il.c | 373 | ||||
-rw-r--r-- | src/analysis/disass/macro.c | 97 | ||||
-rw-r--r-- | src/arch/dalvik/decomp/if.c | 5 | ||||
-rw-r--r-- | src/arch/dalvik/instruction.c | 12 | ||||
-rw-r--r-- | src/decomp/instr/Makefile.am | 1 | ||||
-rw-r--r-- | src/decomp/instr/keyword.c | 162 | ||||
-rw-r--r-- | src/decomp/instr/keyword.h | 68 | ||||
-rw-r--r-- | src/decomp/instr/switch.c | 11 |
15 files changed, 768 insertions, 121 deletions
@@ -1,3 +1,34 @@ +13-02-02 Cyrille Bagard <nocbos@gmail.com> + + * plugins/androhelpers/switch.c: + Ensure that switch cases which share instructions have distinct blocks. + + * src/analysis/block.c: + * src/analysis/block.h: + * src/analysis/block-int.h: + * src/analysis/blocks/flow.c: + * src/analysis/blocks/virtual.c: + Provide leaf blocks of a group of blocks. + + * src/analysis/decomp/il.c: + Reorganize the code. Close some switch cases with a 'break' keyword. + + * src/analysis/disass/macro.c: + + * src/arch/dalvik/decomp/if.c: + * src/arch/dalvik/instruction.c: + Decompile more 'if' instructions. + + * src/decomp/instr/keyword.c: + * src/decomp/instr/keyword.h: + New entries: allow to insert the 'break' keyword in decompiled code. + + * src/decomp/instr/Makefile.am: + Add the 'keyword.[ch]' files to libdecompinstr_la_SOURCES. + + * src/decomp/instr/switch.c: + Typos. + 13-01-31 Cyrille Bagard <nocbos@gmail.com> * plugins/androhelpers/switch.c: diff --git a/plugins/androhelpers/switch.c b/plugins/androhelpers/switch.c index 0a1e488..3bb900f 100644 --- a/plugins/androhelpers/switch.c +++ b/plugins/androhelpers/switch.c @@ -42,6 +42,9 @@ static bool load_dex_switch(const GArchInstruction *, GArchInstruction *, const /* Lie les instructions selon les cas d'un aiguillage. */ static void link_all_switch_cases(GArchInstruction *, const dex_switch *, GArchInstruction *, vmpa_t, vmpa_t); +/* Prend en compte les absences de 'break' dans les cas. */ +static void ensure_each_case_has_its_block(GArchInstruction *, GArchInstruction *); + /* Insère des indications dans le texte humainement lisibles. */ static void mark_all_switch_cases(const GArchInstruction *, const dex_switch *, GArchInstruction *, const GLoadedBinary *, vmpa_t, vmpa_t); @@ -166,6 +169,7 @@ static void link_all_switch_cases(GArchInstruction *instr, const dex_switch *dsw imm = g_imm_operand_new_from_value(MDS_32_BITS_UNSIGNED, value); g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP, imm); + ensure_each_case_has_its_block(next, instrs); } @@ -176,7 +180,49 @@ static void link_all_switch_cases(GArchInstruction *instr, const dex_switch *dsw next = g_arch_instruction_get_next_iter(instrs, instr, end); if (next != NULL) + { g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP, NULL); + ensure_each_case_has_its_block(next, instrs); + } + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction d'aiguillage rencontrée. * +* instrs = liste des instructions pour tout le binaire. * +* * +* Description : Prend en compte les absences de 'break' dans les cas. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void ensure_each_case_has_its_block(GArchInstruction *instr, GArchInstruction *instrs) +{ + GArchInstruction *prev; /* Instruction avant un cas */ + + /** + * La situation où un cas n'a pas de 'break' conduit à une fusion du corps + * du cas avec le corps du cas suivant (partie commune). + * La fin du premier cas n'ayant pas de saut enregistré, rien n'entraîne un + * lien logique, et donc une séparation des deux cas en blocs distincts. + * + * Cette procédure établit les vérifications nécessaires, et rétablit + * la logique des liens et des blocs en cas de besoin. + */ + + prev = g_arch_instruction_get_prev_iter(instrs, instr); + + if (prev != NULL + && !g_arch_instruction_has_destinations(prev) + && !g_arch_instruction_is_return(prev)) + { + g_arch_instruction_link_with(prev, instr, ILT_EXEC_FLOW, NULL); + } } diff --git a/src/analysis/block-int.h b/src/analysis/block-int.h index 419a8b0..069b50c 100644 --- a/src/analysis/block-int.h +++ b/src/analysis/block-int.h @@ -38,6 +38,10 @@ typedef bool (* visit_all_blocks_fc) (GInstrBlock *, instr_block_visitor_cb, voi /* Etablit la liste de tous les blocs présents. */ typedef void (* list_all_blocks_fc) (const GInstrBlock *, GInstrBlock ***, size_t *); +/* Etablit la liste de tous les blocs terminaux. */ +typedef void (* list_leafs_blocks_fc) (const GInstrBlock *, GInstrBlock ***, size_t *); + + /* Fournit les différents accès aux registres. */ //typedef const reg_access * (* list_regs_accesses_fc) (const GInstrBlock *, size_t *); @@ -51,6 +55,7 @@ struct _GInstrBlock find_by_addr_fc find_by_addr; /* Recherche par adresse */ visit_all_blocks_fc visit_blocks; /* Visite des différents blocs */ list_all_blocks_fc list_blocks; /* Liste de tous les blocs */ + list_leafs_blocks_fc list_leafs; /* Liste des blocs terminaux */ //list_regs_accesses_fc list_regs; /* Liste des accès registres */ GInstrBlock *links_block; /* Lieu des blocs attachés */ diff --git a/src/analysis/block.c b/src/analysis/block.c index 50d5644..d8d906a 100644 --- a/src/analysis/block.c +++ b/src/analysis/block.c @@ -202,6 +202,27 @@ void g_instr_block_list_all_blocks(const GInstrBlock *block, GInstrBlock ***list /****************************************************************************** * * +* Paramètres : block = bloc d'instructions à consulter. * +* list = ensemble de blocs à compléter. [OUT] * +* count = nombre de blocs au total. [OUT] * +* * +* Description : Etablit la liste de tous les blocs terminaux. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_instr_block_list_leafs_blocks(const GInstrBlock *block, GInstrBlock ***list, size_t *count) +{ + block->list_leafs(block, list, count); + +} + + +/****************************************************************************** +* * * Paramètres : block = bloc d'instructions à mettre à jour. * * links = bloc contenant les blocs liés au bloc. * * * diff --git a/src/analysis/block.h b/src/analysis/block.h index 7786ce5..d4e5a2b 100644 --- a/src/analysis/block.h +++ b/src/analysis/block.h @@ -74,6 +74,9 @@ bool g_instr_block_visit(GInstrBlock *, instr_block_visitor_cb, void *); /* Etablit la liste de tous les blocs présents. */ void g_instr_block_list_all_blocks(const GInstrBlock *, GInstrBlock ***, size_t *); +/* Etablit la liste de tous les blocs terminaux. */ +void g_instr_block_list_leafs_blocks(const GInstrBlock *, GInstrBlock ***, size_t *); + /* Définit l'ensemble contenant les blocs liés. */ void g_instr_block_set_links_block(GInstrBlock *, GInstrBlock *); diff --git a/src/analysis/blocks/flow.c b/src/analysis/blocks/flow.c index 4df729c..0b68a14 100644 --- a/src/analysis/blocks/flow.c +++ b/src/analysis/blocks/flow.c @@ -74,6 +74,9 @@ static bool g_flow_block_visit(GFlowBlock *, instr_block_visitor_cb, void *); /* Etablit la liste de tous les blocs présents. */ static void g_flow_block_list_all_blocks(const GFlowBlock *, GInstrBlock ***, size_t *); +/* Etablit la liste de tous les blocs terminaux. */ +static void g_flow_block_list_leafs_blocks(const GFlowBlock *, GInstrBlock ***, size_t *); + /* Prend note de l'usage d'un registre, au besoin. */ static void g_flow_block_memorize_access(GFlowBlock *, GArchRegister *, RegAccessType, vmpa_t); @@ -134,6 +137,7 @@ static void g_flow_block_init(GFlowBlock *block) parent->find_by_addr = (find_by_addr_fc)g_flow_block_find_by_addr; parent->visit_blocks = (visit_all_blocks_fc)g_flow_block_visit; parent->list_blocks = (list_all_blocks_fc)g_flow_block_list_all_blocks; + parent->list_leafs = (list_leafs_blocks_fc)g_flow_block_list_leafs_blocks; //parent->list_regs = (list_regs_accesses_fc)g_flow_block_list_regs_accesses; block->regs = g_raccess_list_new(); @@ -315,6 +319,29 @@ static void g_flow_block_list_all_blocks(const GFlowBlock *block, GInstrBlock ** /****************************************************************************** * * +* Paramètres : block = bloc d'instructions à consulter. * +* list = ensemble de blocs à compléter. [OUT] * +* count = nombre de blocs au total. [OUT] * +* * +* Description : Etablit la liste de tous les blocs terminaux. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_flow_block_list_leafs_blocks(const GFlowBlock *block, GInstrBlock ***list, size_t *count) +{ + (*list) = (GInstrBlock **)realloc(*list, ++(*count) * sizeof(GInstrBlock *)); + + (*list)[*count - 1] = G_INSTR_BLOCK(block); + +} + + +/****************************************************************************** +* * * Paramètres : block = bloc d'instructions à mettre à jour. * * reg = registre visé par l'opération. * * type = type d'accès à l'opérande. * diff --git a/src/analysis/blocks/virtual.c b/src/analysis/blocks/virtual.c index 42492e9..0cc6a0d 100644 --- a/src/analysis/blocks/virtual.c +++ b/src/analysis/blocks/virtual.c @@ -79,6 +79,9 @@ static bool g_virtual_block_visit(GVirtualBlock *, instr_block_visitor_cb, void /* Etablit la liste de tous les blocs présents. */ static void g_virtual_block_list_all_blocks(const GVirtualBlock *, GInstrBlock ***, size_t *); +/* Etablit la liste de tous les blocs terminaux. */ +static void g_virtual_block_list_leafs_blocks(const GVirtualBlock *, GInstrBlock ***, size_t *); + /* Fournit les différents accès aux registres. */ //static const reg_access *g_virtual_block_list_regs_accesses(const GVirtualBlock *, size_t *); @@ -133,6 +136,7 @@ static void g_virtual_block_init(GVirtualBlock *block) parent->find_by_addr = (find_by_addr_fc)g_virtual_block_find_by_addr; parent->visit_blocks = (visit_all_blocks_fc)g_virtual_block_visit; parent->list_blocks = (list_all_blocks_fc)g_virtual_block_list_all_blocks; + parent->list_leafs = (list_leafs_blocks_fc)g_virtual_block_list_leafs_blocks; //parent->list_regs = (list_regs_accesses_fc)g_virtual_block_list_regs_accesses; } @@ -300,6 +304,29 @@ static void g_virtual_block_list_all_blocks(const GVirtualBlock *block, GInstrBl } + +/****************************************************************************** +* * +* Paramètres : block = bloc d'instructions à consulter. * +* list = ensemble de blocs à compléter. [OUT] * +* count = nombre de blocs au total. [OUT] * +* * +* Description : Etablit la liste de tous les blocs terminaux. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_virtual_block_list_leafs_blocks(const GVirtualBlock *block, GInstrBlock ***list, size_t *count) +{ + if (block->children_count > 0) + g_instr_block_list_leafs_blocks(block->children[block->children_count - 1], list, count); + +} + + #if 0 /****************************************************************************** * * 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); + +} diff --git a/src/analysis/disass/macro.c b/src/analysis/disass/macro.c index 14dad95..993f70f 100644 --- a/src/analysis/disass/macro.c +++ b/src/analysis/disass/macro.c @@ -52,7 +52,7 @@ typedef struct _branch_info /* Indique si une adresse est retenue comme point de passage. */ -static bool is_addr_in_branch(const branch_info *, const vmpa_t *, bool); +static bool is_addr_in_branch(const branch_info *, const vmpa_t *, bool, size_t *); /* Identifie les différents points de passage d'une branche. */ static void find_next_jumps(GArchInstruction *, vmpa_t, vmpa_t, branch_info *); @@ -73,6 +73,7 @@ static GInstrBlock *build_instruction_block(GArchInstruction *, vmpa_t, vmpa_t, * Paramètres : info = informations à consulter. * * addr = adresse à rechercher. * * fast = autorise une recherche rapide. * +* pos = enregistrement de la position de la trouvaille. [OUT] * * * * Description : Indique si une adresse est retenue comme point de passage. * * * @@ -82,7 +83,7 @@ static GInstrBlock *build_instruction_block(GArchInstruction *, vmpa_t, vmpa_t, * * ******************************************************************************/ -static bool is_addr_in_branch(const branch_info *info, const vmpa_t *addr, bool fast) +static bool is_addr_in_branch(const branch_info *info, const vmpa_t *addr, bool fast, size_t *pos) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ @@ -92,12 +93,20 @@ static bool is_addr_in_branch(const branch_info *info, const vmpa_t *addr, bool if (!fast) for (i = 0; i < info->count && !result; i++) + { result = (info->jumps[i] == *addr); + if (result && pos != NULL) + *pos = i; + } else { ptr = bsearch(addr, info->jumps, info->count, sizeof(vmpa_t), (__compar_fn_t)compare_vmpa); result = (ptr != NULL); + + if (result && pos != NULL) + *pos = (ptr - ((void *)info->jumps)) / sizeof(vmpa_t); + } return result; @@ -130,7 +139,7 @@ static void find_next_jumps(GArchInstruction *instrs, vmpa_t start, vmpa_t end, vmpa_t addr; /* Adresse de la destination */ /* On évite de boucler... */ - if (is_addr_in_branch(info, &start, false)) + if (is_addr_in_branch(info, &start, false, NULL)) return; info->jumps = (vmpa_t *)realloc(info->jumps, ++(info->count) * sizeof(vmpa_t)); @@ -174,7 +183,7 @@ static void find_next_jumps(GArchInstruction *instrs, vmpa_t start, vmpa_t end, } /* Si on termine... */ - if (iter != NULL && !is_addr_in_branch(info, &end, false)) + if (iter != NULL && !is_addr_in_branch(info, &end, false, NULL)) { info->jumps = (vmpa_t *)realloc(info->jumps, ++(info->count) * sizeof(vmpa_t)); info->jumps[info->count - 1] = end; @@ -208,7 +217,7 @@ static vmpa_t compute_first_common_addr(branch_info *a, branch_info *b) //qsort(b->jumps, b->count, sizeof(vmpa_t), (__compar_fn_t)compare_vmpa); for (i = 0; i < a->count && result == VMPA_MAX; i++) - if (is_addr_in_branch(b, &a->jumps[i], false)) + if (is_addr_in_branch(b, &a->jumps[i], false, NULL)) result = a->jumps[i]; return result; @@ -247,7 +256,7 @@ static vmpa_t compute_first_common_addr_in_group(const branch_info *list, size_t keep = true; for (j = 1; j < count && keep; j++) - keep = is_addr_in_branch(&list[j], &list[0].jumps[i], false); + keep = is_addr_in_branch(&list[j], &list[0].jumps[i], false, NULL); if (keep) result = list[0].jumps[i]; @@ -259,6 +268,51 @@ static vmpa_t compute_first_common_addr_in_group(const branch_info *list, size_t } + + + +/****************************************************************************** +* * +* Paramètres : list = liste d'ensembles de jalons à parcourir. * +* count = taille de cette liste. * +* * +* Description : Retrouve le point de ralliement entre un groupe de branches. * +* * +* Retour : Adresse commune aux branches. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static vmpa_t avoid_cases_common_part(const branch_info *list, size_t cur, size_t count, vmpa_t max) +{ + vmpa_t result; /* Adresse trouvée à retourner */ + size_t found_pos; /* Plus petit tronc commun */ + size_t i; /* Boucle de parcours */ + size_t pos; /* Emplacement du cas commun */ + + result = max; + + found_pos = list[cur].count; + + for (i = 0; i < count; i++) + { + if (i == cur) continue; + + if (is_addr_in_branch(&list[cur], &list[i].jumps[0], false, &pos)) + { + if (pos < found_pos) + result = list[i].jumps[0]; + } + + } + + return result; + +} + + + /****************************************************************************** * * * Paramètres : instrs = ensemble des instructions d'assemblage. * @@ -447,13 +501,38 @@ static GInstrBlock *build_instruction_block(GArchInstruction *instrs, vmpa_t sta parent = block; group = g_virtual_block_new(); + do + { + size_t _i, _j; + + for (_i = 0; _i < cases_count; _i++) + { + printf(" [case %d] : ", _i); + + for (_j = 0; _j < cases_branches[_i].count; _j++) + printf("0x%08lx ", cases_branches[_i].jumps[_j]); + + printf("\n"); + + } + + } while (0); + + for (j = 0; j < cases_count; j++) { - printf(" ## %zu...", j); + printf(" ## %zu start=0x%08lx (0x%08lx)...\n", j, cases_branches[j].jumps[0], + next_addr); + + //block = build_instruction_block(instrs, cases_branches[j].jumps[0], end, next_addr); + + //next_addr = avoid_cases_common_part(cases_branches, j, cases_count, next_addr); - block = build_instruction_block(instrs, cases_branches[j].jumps[0], end, next_addr); + block = build_instruction_block(instrs, cases_branches[j].jumps[0], end, + avoid_cases_common_part(cases_branches, j, cases_count, next_addr)); - printf(" %p\n", block); + printf(" %p (0x%08lx)\n", block, + avoid_cases_common_part(cases_branches, j, cases_count, next_addr)); if (block != NULL) diff --git a/src/arch/dalvik/decomp/if.c b/src/arch/dalvik/decomp/if.c index 93e21d9..f7591b9 100644 --- a/src/arch/dalvik/decomp/if.c +++ b/src/arch/dalvik/decomp/if.c @@ -26,6 +26,7 @@ #include "../instruction.h" #include "../../../decomp/expr/cond.h" +#include "../../../decomp/expr/immediate.h" #include "../../../decomp/instr/ite.h" @@ -153,8 +154,8 @@ GDecInstruction *dalvik_decomp_instr_if_zero(const GArchInstruction *instr, GDec operand = g_arch_instruction_get_operand(instr, 0); op1 = g_dec_context_convert_register(ctx, operand, false, addr); - operand = g_imm_operand_new_from_value(MDS_8_BITS_UNSIGNED, (unsigned int)0); - op2 = g_imm_expression_new(operand); + operand = g_imm_operand_new_from_value(MDS_32_BITS_UNSIGNED, (unsigned int)0); + op2 = g_imm_expression_new(G_IMM_OPERAND(operand)); operand = g_arch_instruction_get_operand(instr, 2); jmp = 0x1234ull;/*g_dec_context_convert_register(ctx, operand, addr);*/ diff --git a/src/arch/dalvik/instruction.c b/src/arch/dalvik/instruction.c index 81b8993..1ab6e3c 100644 --- a/src/arch/dalvik/instruction.c +++ b/src/arch/dalvik/instruction.c @@ -117,12 +117,12 @@ static dalvik_instruction _instructions[DOP_COUNT] = { [DOP_IF_GE] = { 0x35, "if-ge", dalvik_decomp_instr_if }, [DOP_IF_GT] = { 0x36, "if-gt", dalvik_decomp_instr_if }, [DOP_IF_LE] = { 0x37, "if-le", dalvik_decomp_instr_if }, - [DOP_IF_EQZ] = { 0x38, "if-eqz"/*, dalvik_decomp_instr_if_zero*/ }, - [DOP_IF_NEZ] = { 0x39, "if-nez"/*, dalvik_decomp_instr_if_zero*/ }, - [DOP_IF_LTZ] = { 0x3a, "if-ltz"/*, dalvik_decomp_instr_if_zero*/ }, - [DOP_IF_GEZ] = { 0x3b, "if-gez"/*, dalvik_decomp_instr_if_zero*/ }, - [DOP_IF_GTZ] = { 0x3c, "if-gtz"/*, dalvik_decomp_instr_if_zero*/ }, - [DOP_IF_LEZ] = { 0x3d, "if-lez"/*, dalvik_decomp_instr_if_zero*/ }, + [DOP_IF_EQZ] = { 0x38, "if-eqz", dalvik_decomp_instr_if_zero }, + [DOP_IF_NEZ] = { 0x39, "if-nez", dalvik_decomp_instr_if_zero }, + [DOP_IF_LTZ] = { 0x3a, "if-ltz", dalvik_decomp_instr_if_zero }, + [DOP_IF_GEZ] = { 0x3b, "if-gez", dalvik_decomp_instr_if_zero }, + [DOP_IF_GTZ] = { 0x3c, "if-gtz", dalvik_decomp_instr_if_zero }, + [DOP_IF_LEZ] = { 0x3d, "if-lez", dalvik_decomp_instr_if_zero }, [DOP_UNUSED_3E] = { 0x3e, NULL /* unused */ }, [DOP_UNUSED_3F] = { 0x3f, NULL /* unused */ }, [DOP_UNUSED_40] = { 0x40, NULL /* unused */ }, diff --git a/src/decomp/instr/Makefile.am b/src/decomp/instr/Makefile.am index 6120cac..d299cd2 100644 --- a/src/decomp/instr/Makefile.am +++ b/src/decomp/instr/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libdecompinstr.la libdecompinstr_la_SOURCES = \ ite.h ite.c \ + keyword.h keyword.c \ switch.h switch.c libdecompinstr_la_LDFLAGS = diff --git a/src/decomp/instr/keyword.c b/src/decomp/instr/keyword.c new file mode 100644 index 0000000..eca1530 --- /dev/null +++ b/src/decomp/instr/keyword.c @@ -0,0 +1,162 @@ + +/* OpenIDA - Outil d'analyse de fichiers binaires + * keyword.c - insertions de mots clefs de haut niveau + * + * Copyright (C) 2013 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 <http://www.gnu.org/licenses/>. + */ + + +#include "keyword.h" + + +#include "../instruction-int.h" + + + +/* Définition d'un mot clef de haut niveau (instance) */ +struct _GKeywordInstruction +{ + GDecInstruction parent; /* A laisser en premier */ + + DecompiledKeyWord keyword; /* Mot clef représenté */ + +}; + + +/* Indique le type défini pour un mot clef de haut niveau. */ +struct _GKeywordInstructionClass +{ + GDecInstructionClass parent; /* A laisser en premier */ + +}; + + + +/* Initialise la classe des mots clefs de haut niveau. */ +static void g_keyword_instruction_class_init(GKeywordInstructionClass *); + +/* Initialise une instance de mot clef de haut niveau. */ +static void g_keyword_instruction_init(GKeywordInstruction *); + +/* Imprime pour l'écran un version humaine d'une instruction. */ +static GBufferLine *g_keyword_instruction_print(const GKeywordInstruction *, GCodeBuffer *, GBufferLine *, GLangOutput *); + + + +/* Indique le type défini pour un mot clef de haut niveau. */ +G_DEFINE_TYPE(GKeywordInstruction, g_keyword_instruction, G_TYPE_DEC_INSTRUCTION); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des mots clefs de haut niveau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_keyword_instruction_class_init(GKeywordInstructionClass *klass) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instance à initialiser. * +* * +* Description : Initialise une instance de mot clef de haut niveau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_keyword_instruction_init(GKeywordInstruction *instr) +{ + GDecInstruction *base; /* Autre version de l'objet */ + + base = G_DEC_INSTRUCTION(instr); + + base->print = (dec_instr_print_fc)g_keyword_instruction_print; + +} + + +/****************************************************************************** +* * +* Paramètres : keyword = mot clef à représenter. * +* * +* Description : Crée un mot clef de haut niveau. * +* * +* Retour : Instruction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDecInstruction *g_keyword_instruction_new(DecompiledKeyWord keyword) +{ + GKeywordInstruction *result; /* Expression à retourner */ + + result = g_object_new(G_TYPE_KEYWORD_INSTRUCTION, NULL); + + result->keyword = keyword; + + return G_DEC_INSTRUCTION(result); + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction à transcrire en version humaine. * +* buffer = tampon où doit se réaliser l'insertion. * +* line = ligne d'impression prête à emploi ou NULL. * +* output = langage de programmation de sortie. * +* * +* Description : Imprime pour l'écran un version humaine d'une instruction. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GBufferLine *g_keyword_instruction_print(const GKeywordInstruction *instr, GCodeBuffer *buffer, GBufferLine *line, GLangOutput *output) +{ + switch (instr->keyword) + { + case DKW_BREAK: + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "break", 5, RTT_KEY_WORD); + break; + + case DKW_CONTINUE: + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "continue", 8, RTT_KEY_WORD); + break; + + } + + return line; + +} diff --git a/src/decomp/instr/keyword.h b/src/decomp/instr/keyword.h new file mode 100644 index 0000000..69d6901 --- /dev/null +++ b/src/decomp/instr/keyword.h @@ -0,0 +1,68 @@ + +/* OpenIDA - Outil d'analyse de fichiers binaires + * keyword.h - prototypes pour les insertions de mots clefs de haut niveau + * + * Copyright (C) 2013 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DECOMP_INSTR_KEYWORD_H +#define _DECOMP_INSTR_KEYWORD_H + + +#include <glib-object.h> + + +#include "../instruction.h" + + + +#define G_TYPE_KEYWORD_INSTRUCTION g_keyword_instruction_get_type() +#define G_KEYWORD_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_keyword_instruction_get_type(), GKeywordInstruction)) +#define G_IS_KEYWORD_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_keyword_instruction_get_type())) +#define G_KEYWORD_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KEYWORD_INSTRUCTION, GKeywordInstructionClass)) +#define G_IS_KEYWORD_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KEYWORD_INSTRUCTION)) +#define G_KEYWORD_INSTRUCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KEYWORD_INSTRUCTION, GKeywordInstructionClass)) + + + +/* Type de mots clefs pris en charge */ +typedef enum _DecompiledKeyWord +{ + DKW_BREAK, /* 'break' */ + DKW_CONTINUE, /* 'continue' */ + +} DecompiledKeyWord; + + +/* Définition d'un mot clef de haut niveau (instance) */ +typedef struct _GKeywordInstruction GKeywordInstruction; + +/* Définition d'un mot clef de haut niveau (classe) */ +typedef struct _GKeywordInstructionClass GKeywordInstructionClass; + + +/* Indique le type défini pour un mot clef de haut niveau. */ +GType g_keyword_instruction_get_type(void); + +/* Crée un mot clef de haut niveau. */ +GDecInstruction *g_keyword_instruction_new(DecompiledKeyWord); + + + +#endif /* _DECOMP_INSTR_KEYWORD_H */ diff --git a/src/decomp/instr/switch.c b/src/decomp/instr/switch.c index 3c204a0..4b1662b 100644 --- a/src/decomp/instr/switch.c +++ b/src/decomp/instr/switch.c @@ -31,15 +31,16 @@ #include "../instruction-int.h" + /* Détails d'un cas */ typedef struct _case_info { - vmpa_t addr; /* Adresse des blocs associés */ + vmpa_t addr; /* Adresse des blocs associés */ - GDecExpression **values; /* Valeur d'embranchement */ - size_t values_count; /* Quantité de cas rassemblés */ + GDecExpression **values; /* Valeur d'embranchement */ + size_t values_count; /* Quantité de cas rassemblés */ - GDecInstruction *instrs; /* Instructions du cas */ + GDecInstruction *instrs; /* Instructions du cas */ } case_info; @@ -138,7 +139,7 @@ static void g_switch_instruction_init(GSwitchInstruction *instr) * * * Description : Exprime un aiguillage multiple du flux selon une valeur. * * * -* Retour : Expression mise en place. * +* Retour : Instruction mise en place. * * * * Remarques : - * * * |