/* Chrysalide - Outil d'analyse de fichiers binaires * link.c - édition des liens après la phase de désassemblage * * Copyright (C) 2016-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 Foobar. If not, see . */ #include "link.h" #include #include #include #include #include #include #include #include #include "pseudo/switch.h" /* Mémorisation des cas rencontrés */ typedef struct _case_comment { bool valid; /* Entrée utilisable ? */ vmpa2t handler; /* Position du code associé */ bool is_default; /* Gestion par défaut ? */ union { int32_t key; /* Clef unique */ int32_t *keys; /* Ensemble de clefs dynamique */ }; size_t count; /* Nombre de clefs conservées */ } case_comment; /* REMME */ #define COMMENT_LINE_SEP "\n" /****************************************************************************** * * * Paramètres : instr = instruction ARMv7 à traiter. * * proc = représentation de l'architecture utilisée. * * context = contexte associé à la phase de désassemblage. * * format = acès aux données du binaire d'origine. * * * * Description : Etablit tous les liens liés à un embranchement compressé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void handle_dalvik_packed_switch_links(GArchInstruction *instr, GArchProcessor *proc, GProcContext *context, GExeFormat *format) { GArchOperand *op; /* Opérande numérique en place */ bool defined; /* Adresse définie ? */ vmpa2t addr; /* Adresse de destination */ virt_t virt; /* Adresse virtuelle */ GArchInstruction *switch_ins; /* Instruction de branchements */ const mrange_t *range; /* Zone d'occupation */ const vmpa2t *start_addr; /* Adresse de référentiel */ const int32_t *keys; /* Conditions de sauts */ const int32_t *targets; /* Positions relatives liées */ uint16_t count; /* Taille de ces tableaux */ case_comment *comments; /* Mémorisation progressive */ vmpa2t def_addr; /* Traitement par défaut */ GArchInstruction *target; /* Ligne visée par la référence*/ case_comment *comment; /* Commentaire à éditer */ uint16_t i; /* Boucle de parcours #1 */ uint16_t j; /* Boucle de parcours #2 */ int32_t tmp; /* Sauvegarde temporaire */ char *msg; /* Indication à imprimer */ size_t k; /* Boucle de parcours #3 */ char *int_val; /* Valeur en chaîne de carac. */ GDbComment *item; /* Indication sur la condition */ g_arch_instruction_lock_operands(instr); assert(_g_arch_instruction_count_operands(instr) == 2); op = _g_arch_instruction_get_operand(instr, 1); g_arch_instruction_unlock_operands(instr); defined = false; if (G_IS_TARGET_OPERAND(op)) { g_target_operand_get_addr(G_TARGET_OPERAND(op), &addr); defined = true; } else if (G_IS_IMM_OPERAND(op)) { if (g_imm_operand_to_virt_t(G_IMM_OPERAND(op), &virt)) { init_vmpa(&addr, VMPA_NO_PHYSICAL, virt); defined = true; } } if (defined) { switch_ins = g_arch_processor_find_instr_by_address(proc, &addr); if (G_IS_DALVIK_SWITCH_INSTR(switch_ins)) { range = g_arch_instruction_get_range(instr); start_addr = get_mrange_addr(range); /* Préparation de l'édition des commentaires */ count = g_dalvik_switch_get_data(G_DALVIK_SWITCH_INSTR(switch_ins), &keys, &targets); comments = (case_comment *)calloc(1 + count, sizeof(case_comment)); /* Cas par défaut */ compute_mrange_end_addr(range, &def_addr); target = g_arch_processor_find_instr_by_address(proc, &def_addr); if (target != NULL) { comment = &comments[0]; comment->valid = true; copy_vmpa(&comment->handler, &def_addr); comment->is_default = true; g_arch_instruction_link_with(instr, target, ILT_CASE_JUMP); g_object_unref(G_OBJECT(target)); } /* Autres cas */ for (i = 0; i < count; i++) { copy_vmpa(&addr, start_addr); advance_vmpa(&addr, targets[i] * sizeof(uint16_t)); if (cmp_vmpa(&addr, &def_addr) == 0) continue; target = g_arch_processor_find_instr_by_address(proc, &addr); if (target != NULL) { for (j = 0; j < (1 + count); j++) { if (!comments[j].valid) break; if (cmp_vmpa(&addr, &comments[j].handler) == 0) break; } assert(j < (1 + count)); comment = &comments[j]; if (!comment->valid) { comment->valid = true; copy_vmpa(&comment->handler, &addr); comment->key = keys[i]; comment->count = 1; } else { if (comment->count == 0) comment->key = keys[i]; if (comment->count == 1) { tmp = comment->key; comment->keys = (int32_t *)calloc(2, sizeof(int32_t)); comment->keys[0] = tmp; comment->keys[1] = keys[i]; comment->count = 2; } else { comment->count++; comment->keys = (int32_t *)realloc(comment->keys, comment->count * sizeof(int32_t)); comment->keys[comment->count - 1] = keys[i]; } } g_arch_instruction_link_with(instr, target, ILT_CASE_JUMP); g_object_unref(G_OBJECT(target)); } } /* Edition des commentaires et nettoyage */ for (j = 0; j < (1 + count); j++) { comment = &comments[j]; if (!comment->valid) break; switch (comment->count) { case 0: msg = NULL; break; case 1: asprintf(&msg, _("Case %d"), comment->key); break; default: msg = NULL; /** * Les spécifications indiquent que les clefs sont triées. * Donc nul besoin de s'occuper de leur ordre ici. */ for (k = 0; k < comment->count; k++) { if (k > 0) /* FIXME : encapsuler ! */ msg = stradd(msg, COMMENT_LINE_SEP); asprintf(&int_val, _("Case %d:"), comment->keys[k]); msg = stradd(msg, int_val); free(int_val); } break; } if (comment->is_default) { if (msg == NULL) msg = strdup(_("Defaut case:")); else { /* FIXME : encapsuler ! */ msg = stradd(msg, COMMENT_LINE_SEP); msg = stradd(msg, _("Defaut case")); } } item = g_db_comment_new_area(&comment->handler, BLF_NONE, msg, true); g_db_item_set_volatile(G_DB_ITEM(item), true); g_proc_context_add_db_item(context, G_DB_ITEM(item)); free(msg); if (comment->count > 1) free(comment->keys); } free(comments); } if (switch_ins != NULL) g_object_unref(G_OBJECT(switch_ins)); } }