/* 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 Chrysalide. If not, see . */ #include "link.h" #include #include #include #include #include #include #include #include #include #include "operands/pool.h" #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; /****************************************************************************** * * * 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 une référence entre utilisation et origine de chaîne.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void handle_links_for_dalvik_string(GArchInstruction *instr, GArchProcessor *proc, GProcContext *context, GExeFormat *format) { GArchOperand *op; /* Opérande numérique en place */ uint32_t index; /* Indice dans la table Dex */ GBinSymbol *string; /* Emplacement de la chaîne */ const mrange_t *range; /* Zone d'occupation */ GArchInstruction *target; /* Ligne visée par la référence*/ 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); assert(G_IS_DALVIK_POOL_OPERAND(op)); assert(g_dalvik_pool_operand_get_pool_type(G_DALVIK_POOL_OPERAND(op)) == DPT_STRING); index = g_dalvik_pool_operand_get_index(G_DALVIK_POOL_OPERAND(op)); string = get_string_symbol_from_dex_pool(G_DEX_FORMAT(format), index); if (string != NULL) { range = g_binary_symbol_get_range(string); target = g_arch_processor_find_instr_by_address(proc, get_mrange_addr(range)); if (target != NULL) { g_arch_instruction_link_with(instr, target, ILT_REF); g_arch_instruction_link_with(target, instr, ILT_REF); g_object_unref(G_OBJECT(target)); } g_object_unref(G_OBJECT(string)); } g_object_unref(G_OBJECT(op)); } /****************************************************************************** * * * 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 */ const mrange_t *range; /* Emplacement de l'instruction*/ bool defined; /* Adresse définie ? */ vmpa2t addr; /* Adresse de destination */ GArchInstruction *switch_ins; /* Instruction de branchements */ 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); if (G_IS_TARGETABLE_OPERAND(op)) { range = g_arch_instruction_get_range(instr); defined = g_targetable_operand_get_addr(G_TARGETABLE_OPERAND(op), get_mrange_addr(range), G_BIN_FORMAT(format), proc, &addr); } else defined = false; g_object_unref(G_OBJECT(op)); 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) msg = stradd(msg, "\n"); 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 { msg = stradd(msg, "\n"); 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)); } } /****************************************************************************** * * * 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 une référence entre appelant et appelé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void handle_links_between_caller_and_callee(GArchInstruction *instr, GArchProcessor *proc, GProcContext *context, GExeFormat *format) { GArchOperand *op; /* Opérande numérique en place */ uint32_t index; /* Indice dans la table Dex */ GDexMethod *method; /* Méthode ciblée ici */ GBinRoutine *routine; /* Routine liée à la méthode */ const mrange_t *range; /* Zone d'occupation */ GArchInstruction *target; /* Ligne visée par la référence*/ 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); assert(G_IS_DALVIK_POOL_OPERAND(op)); assert(g_dalvik_pool_operand_get_pool_type(G_DALVIK_POOL_OPERAND(op)) == DPT_METHOD); index = g_dalvik_pool_operand_get_index(G_DALVIK_POOL_OPERAND(op)); method = get_method_from_dex_pool(G_DEX_FORMAT(format), index); if (method != NULL) { routine = g_dex_method_get_routine(method); range = g_binary_symbol_get_range(G_BIN_SYMBOL(routine)); if (range->addr.physical > 0) { target = g_arch_processor_find_instr_by_address(proc, get_mrange_addr(range)); if (target != NULL) { g_arch_instruction_link_with(instr, target, ILT_REF); g_object_unref(G_OBJECT(target)); } } g_object_unref(G_OBJECT(routine)); g_object_unref(G_OBJECT(method)); } g_object_unref(G_OBJECT(op)); }