/* Chrysalide - Outil d'analyse de fichiers binaires * try_n_catch.c - support des exceptions chez Android * * Copyright (C) 2012-2013 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 "try_n_catch.h" #include #include #include #include #include <../i18n.h> /* Mémorisation d'un lien vers un gestionnaire */ typedef struct _caught_exception { vmpa_t addr; /* Adresse du code de gestion */ GArchInstruction *instr; /* Première instruction visée */ char *desc; /* Nom de l'exception */ } caught_exception; /* Valide la zone couverte par le gestionnaire d'exceptions. */ static bool check_covered_area(const try_item *, const GBinRoutine *); /* Rattache les gestionnaires d'exception à leur code couvert. */ static void attach_caught_code(const GLoadedBinary *, const GBinRoutine *, const try_item *, const caught_exception *, size_t); /* Insère des indications dans le texte humainement lisibles. */ static void mark_exception_handlers(const GLoadedBinary *, uleb128_t, caught_exception **, size_t *); /* Construit des listes pointant sur les différentes gestions. */ static caught_exception **build_all_destinations_list(const GLoadedBinary *, const GBinRoutine *, const encoded_catch_handler_list *, size_t **); /* Recherche et met en avant tous les gestionnaires d'exception. */ static void look_for_exception_handlers(const GLoadedBinary *, const GDexFormat *, GDexMethod *, bool); /****************************************************************************** * * * Paramètres : try = informations sur la gestion à consulter. * * routine = routine associée, pour validation. * * * * Description : Valide la zone couverte par le gestionnaire d'exceptions. * * * * Retour : Validité de la zone couverte. * * * * Remarques : - * * * ******************************************************************************/ static bool check_covered_area(const try_item *try, const GBinRoutine *routine) { off_t length; /* Taille de la zone de code */ vmpa_t covered_start; /* Début de la zone couverte */ vmpa_t covered_end; /* Fin de la zone couverte */ length = g_binary_routine_get_size(routine); covered_start = try->start_addr * sizeof(uint16_t); covered_end = covered_start + try->insn_count * sizeof(uint16_t); return (covered_end <= length); } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * routine = routine associée, pour l'accès au instructions. * * try = informations sur la gestion à consulter. * * handlers = arrivées des liens vers les gestionnaires. * * count = nombre de ces arrivées. * * * * Description : Rattache les gestionnaires d'exception à leur code couvert. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void attach_caught_code(const GLoadedBinary *binary, const GBinRoutine *routine, const try_item *try, const caught_exception *handlers, size_t count) { vmpa_t start; /* Début de la zone couverte */ vmpa_t end; /* Fin de la zone couverte */ GArchProcessor *proc; /* Processeur de l'architecture*/ GArchInstruction *instrs; /* Instructions Dalvik */ GArchInstruction *first; /* Première instruction */ GArchInstruction *next; /* Dernière instruction + 1 */ GArchInstruction *prev; /* Instruction à détacher */ GArchInstruction *iter; /* Boucle de parcours #1 */ size_t i; /* Boucle de parcours #2 */ start = g_binary_routine_get_address(routine); start += try->start_addr * sizeof(uint16_t); end = start + try->insn_count * sizeof(uint16_t); proc = g_loaded_binary_get_processor(binary); instrs = NULL;//g_arch_processor_get_disassembled_instructions(proc); first = g_arch_instruction_find_by_address(instrs, start, true); next = g_arch_instruction_find_by_address(instrs, end, true); if (first == NULL || next == NULL) goto acc_exit; /* Si des détachements sont nécessaires... */ if (!g_arch_instruction_has_sources(first) && try->start_addr > 0) { prev = g_arch_instruction_get_prev_iter(instrs, first); g_arch_instruction_link_with(prev, first, ILT_EXEC_FLOW); } if (!g_arch_instruction_has_sources(next) && (try->start_addr > 0 || try->insn_count > 0)) { prev = g_arch_instruction_get_prev_iter(instrs, next); g_arch_instruction_link_with(prev, next, ILT_EXEC_FLOW); } /* Rattachements ? */ if (handlers != NULL) { for (iter = first; iter != NULL; iter = g_arch_instruction_get_next_iter(instrs, iter, end)) { if (!g_arch_instruction_has_destinations(iter)) continue; for (i = 0; i < count; i++) g_arch_instruction_link_with(iter, handlers[i].instr, ILT_CATCH_EXCEPTION); } } acc_exit: g_object_unref(G_OBJECT(proc)); } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * size = nombre de groupe à parcourir. * * handlers = ensemble des groupes de gestionnaires. * * count = liste des quantités de gestionnaires groupés. * * * * Description : Insère des indications dans le texte humainement lisibles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mark_exception_handlers(const GLoadedBinary *binary, uleb128_t size, caught_exception **handlers, size_t *count) { GCodeBuffer *buffer; /* Contenu textuel à modifier */ uleb128_t i; /* Boucle de parcours #1 */ size_t j; /* Boucle de parcours #2 */ GBufferLine *line; /* Nouvelle ligne à compléter */ size_t len; /* Taille de la description */ char *fulldesc; /* Description complète */ buffer = g_loaded_binary_get_disassembled_buffer(binary); for (i = 0; i < size; i++) for (j = 0; j < count[i]; j++) { line = g_code_buffer_insert_at(buffer, handlers[i][j].addr, true); g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD); g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, "; ", 2, RTT_INDICATION, NULL); len = strlen(_("Handler for caught '%s'")) + strlen(handlers[i][j].desc); fulldesc = (char *)calloc(len + 1, sizeof(char)); snprintf(fulldesc, len + 1, _("Handler for caught '%s'"), handlers[i][j].desc); g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, fulldesc, len, RTT_INDICATION, NULL); free(fulldesc); } } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * routine = routine associée, pour l'accès au instructions. * * hlist = liste de tous les gestionnaires en place. * * count = quantité de destinations trouvées. [OUT] * * * * Description : Construit des listes pointant sur les différentes gestions. * * * * Retour : Adresses des codes à lier systématiquement. * * * * Remarques : - * * * ******************************************************************************/ static caught_exception **build_all_destinations_list(const GLoadedBinary *binary, const GBinRoutine *routine, const encoded_catch_handler_list *hlist, size_t **count) { caught_exception **result; /* Liste de listes à retourner */ vmpa_t start; /* Début du code de la routine */ GDexFormat *format; /* Format du binaire chargé */ GArchProcessor *proc; /* Processeur de l'architecture*/ GArchInstruction *instrs; /* Instructions Dalvik */ uleb128_t i; /* Boucle de parcours #1 */ encoded_catch_handler *handlers; /* Groupe de gestionnaires */ leb128_t max; /* Quantité d'exception */ leb128_t j; /* Boucle de parcours #2 */ caught_exception *excep; /* Raccourci confortable */ GDataType *type; /* Type de l'exception */ start = g_binary_routine_get_address(routine); format = G_DEX_FORMAT(g_loaded_binary_get_format(binary)); proc = g_loaded_binary_get_processor(binary); instrs = NULL;//g_arch_processor_get_disassembled_instructions(proc); instrs = g_arch_instruction_find_by_address(instrs, start, true); /* Création d'un espace mémoire pour les listes */ result = (caught_exception **)calloc(hlist->size, sizeof(caught_exception *)); *count = (size_t *)calloc(hlist->size, sizeof(size_t)); /* Parcours de chaque groupe de gestionnaires */ for (i = 0; i < hlist->size; i++) { handlers = &hlist->list[i]; max = leb128_abs(handlers->size); (*count)[i] = max + (handlers->size < 0 ? 1 : 0); result[i] = (caught_exception *)calloc((*count)[i], sizeof(caught_exception)); (*count)[i] = 0; for (j = 0; j < max; j++) { excep = &result[i][(*count)[i]]; excep->addr = start + handlers->handlers[j].addr * sizeof(uint16_t); excep->instr = g_arch_instruction_find_by_address(instrs, excep->addr, true); if (excep->instr == NULL) continue; type = get_type_from_dex_pool(format, handlers->handlers[j].type_idx); if (type == NULL) continue; excep->desc = g_data_type_to_string(type); g_object_unref(G_OBJECT(type)); (*count)[i]++; } if (handlers->size < 0) { excep = &result[i][(*count)[i]]; excep->addr = start + handlers->catch_all_addr * sizeof(uint16_t); excep->instr = g_arch_instruction_find_by_address(instrs, excep->addr, true); if (excep->instr != NULL) { excep->desc = strdup(_("default")); (*count)[i]++; } } } g_object_unref(G_OBJECT(proc)); g_object_unref(G_OBJECT(format)); return result; } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * format = format du binaire Dex. * * method = méthode à analyser. * * link = édition de liens ou impression de commentaires ? * * * * Description : Recherche et met en avant tous les gestionnaires d'exception.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void look_for_exception_handlers(const GLoadedBinary *binary, const GDexFormat *format, GDexMethod *method, bool link) { const code_item *body; /* Description du corps */ GBinRoutine *routine; /* Abstraction globale */ encoded_catch_handler_list *hlist; /* Confort vers la liste brute */ caught_exception **handlers; /* Interprétation des gestions */ size_t *count; /* Tailles des groupes */ uint16_t i; /* Boucle de parcours #1 */ try_item *try; /* Raccourci vers une zone */ uleb128_t index; /* Indice du bon gestionnaire */ size_t j; /* Boucle de parcours #2 */ body = g_dex_method_get_dex_body(method); if (body == NULL) return; if (body->tries_size == 0) return; routine = g_dex_method_get_routine(method); hlist = body->handlers; handlers = build_all_destinations_list(binary, routine, hlist, &count); if (link) /* Pour chaque zone couverte... */ for (i = 0; i < body->tries_size; i++) { try = &body->tries[i]; if (!check_covered_area(try, routine)) continue; for (index = 0; index < hlist->size; index++) if (try->handler_off == hlist->list[index].offset) break; if (index == hlist->size) continue; attach_caught_code(binary, routine, try, handlers[index], count[index]); } else /* Ajout des précisions */ mark_exception_handlers(binary, hlist->size, handlers, count); /* Libération de la mémoire utilisée */ for (index = 0; index < hlist->size; index++) { for (j = 0; j < count[index]; j++) free(handlers[index][j].desc); if (handlers[index] != NULL) free(handlers[index]); } if (handlers != NULL) free(handlers); if (count != NULL) free(count); g_object_unref(G_OBJECT(routine)); } /****************************************************************************** * * * Paramètres : binary = représentation binaire à traiter. * * link = édition de liens ou impression de commentaires ? * * * * Description : Traite tous les gestionnaires d'exception trouvés. * * * * Retour : true si une action a été menée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool process_exception_handlers(GLoadedBinary *binary, bool link) { GDexFormat *format; /* Format du binaire chargé */ size_t cls_count; /* Nombre de classes trouvées */ size_t i; /* Boucle de parcours #1 */ GDexClass *class; /* Classe à analyser */ size_t meth_count; /* Nombre de méthodes trouvées */ size_t j; /* Boucle de parcours #2 */ GDexMethod *method; /* Méthode à parcourir */ format = G_DEX_FORMAT(g_loaded_binary_get_format(binary)); cls_count = g_dex_format_count_classes(format); for (i = 0; i < cls_count; i++) { class = g_dex_format_get_class(format, i); meth_count = g_dex_class_count_methods(class, false); for (j = 0; j < meth_count; j++) { method = g_dex_class_get_method(class, false, j); look_for_exception_handlers(binary, format, method, link); } meth_count = g_dex_class_count_methods(class, true); for (j = 0; j < meth_count; j++) { method = g_dex_class_get_method(class, true, j); look_for_exception_handlers(binary, format, method, link); } } g_object_unref(G_OBJECT(format)); return true; }