/* OpenIDA - Outil d'analyse de fichiers binaires
* try_n_catch.c - support des exceptions chez Android
*
* Copyright (C) 2012 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 .
*/
#include "try_n_catch.h"
#include
#include
#include
#include <../i18n.h>
/* Mémorisation d'un lien vers un gestionnaire */
typedef struct _caught_exception
{
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 *);
/* Construit une liste pointant sur les différentes gestions. */
static caught_exception *build_destinations_list(GLoadedBinary *, const try_item *, const encoded_catch_handler_list *, const GBinRoutine *, size_t *);
/* Rattache les gestionnaires d'exception à leur code couvert. */
static void attach_caught_code(GLoadedBinary *, const try_item *, const encoded_catch_handler_list *, const GBinRoutine *);
/* Recherche et met en avant tous les gestionnaires d'exception. */
static void look_for_exception_handlers(GLoadedBinary *, const GDexFormat *, GDexMethod *);
/******************************************************************************
* *
* 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. *
* try = informations sur la gestion à consulter. *
* hlist = liste de tous les gestionnaires en place. *
* routine = routine associée, pour l'accès au instructions. *
* count = quantité de destinations trouvées. [OUT] *
* *
* Description : Construit une liste pointant sur les différentes gestions. *
* *
* Retour : Adresse des codes à lier systématiquement. *
* *
* Remarques : - *
* *
******************************************************************************/
static caught_exception *build_destinations_list(GLoadedBinary *binary, const try_item *try, const encoded_catch_handler_list *hlist, const GBinRoutine *routine, size_t *count)
{
caught_exception *result; /* Liste à retourner */
GDexFormat *format; /* Format du binaire chargé */
vmpa_t start; /* Début du code de la routine */
GArchInstruction *instrs; /* Instructions Dalvik */
uleb128_t index; /* Indice du bon gestionnaire */
encoded_catch_handler *handlers; /* Groupe de gestionnaires */
leb128_t max; /* Quantité d'exception */
leb128_t i; /* Boucle de parcours */
vmpa_t handler_addr; /* Adresse du code de gestion */
GDataType *type; /* Type de l'exception */
format = G_DEX_FORMAT(g_loaded_binary_get_format(binary));
start = g_binary_routine_get_address(routine);
instrs = g_loaded_binary_get_instructions(binary);
instrs = g_arch_instruction_find_by_address(instrs, start, true);
for (index = 0; index < hlist->size; index++)
if (try->handler_off == hlist->list[index].offset)
break;
if (index == hlist->size)
{
*count = 0;
return NULL;
}
handlers = &hlist->list[index];
max = leb128_abs(handlers->size);
*count = max + (handlers->size < 0 ? 1 : 0);
result = (caught_exception *)calloc(*count, sizeof(caught_exception));
*count = 0;
for (i = 0; i < max; i++)
{
handler_addr = start + handlers->handlers[i].addr * sizeof(uint16_t);
result[*count].instr = g_arch_instruction_find_by_address(instrs, handler_addr, true);
if (result[*count].instr == NULL)
continue;
type = get_type_from_dex_pool(format, handlers->handlers[i].type_idx);
result[*count].desc = g_data_type_to_string(type);
(*count)++;
}
if (handlers->size < 0)
{
handler_addr = start + handlers->catch_all_addr * sizeof(uint16_t);
result[*count].instr = g_arch_instruction_find_by_address(instrs, handler_addr, true);
if (result[*count].instr != NULL)
{
result[*count].desc = strdup(_("default"));
(*count)++;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* try = informations sur la gestion à consulter. *
* handlers = liste de tous les gestionnaires en place. *
* routine = routine associée, pour l'accès au instructions. *
* *
* Description : Rattache les gestionnaires d'exception à leur code couvert. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void attach_caught_code(GLoadedBinary *binary, const try_item *try, const encoded_catch_handler_list *handlers, const GBinRoutine *routine)
{
vmpa_t start; /* Début de la zone couverte */
vmpa_t end; /* Début de la zone couverte */
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 */
size_t dests_count; /* Nombre de points d'arrivée */
caught_exception *dests; /* Points d'arrivée */
size_t i; /* Boucle de parcours */
start = g_binary_routine_get_address(routine);
start += try->start_addr * sizeof(uint16_t);
end = start + try->insn_count * sizeof(uint16_t);
instrs = g_loaded_binary_get_instructions(binary);
first = g_arch_instruction_find_by_address(instrs, start, true);
next = g_arch_instruction_find_by_address(instrs, end, true);
/* Si des détachements sont nécessaires... */
if (!g_arch_instruction_has_sources(first))
{
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))
{
prev = g_arch_instruction_get_prev_iter(instrs, next);
g_arch_instruction_link_with(prev, next, ILT_EXEC_FLOW);
}
/* Détermination du code des exceptions */
dests = build_destinations_list(binary, try, handlers, routine, &dests_count);
if (dests != NULL)
{
/* Rattachements */
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 < dests_count; i++)
g_arch_instruction_link_with(iter, dests[i].instr, ILT_CATCH_EXCEPTION);
}
/* Libération de la mémoire utilisée */
for (i = 0; i < dests_count; i++)
free(dests[i].desc);
free(dests);
}
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* format = format du binaire Dex. *
* method = méthode à analyser. *
* *
* Description : Recherche et met en avant tous les gestionnaires d'exception.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void look_for_exception_handlers(GLoadedBinary *binary, const GDexFormat *format, GDexMethod *method)
{
const code_item *body; /* Description du corps */
GBinRoutine *routine; /* Abstraction globale */
uint16_t i; /* Boucle de parcours */
body = g_dex_method_get_dex_body(method);
if (body->tries_size == 0)
return;
routine = g_dex_method_get_routine(method);
for (i = 0; i < body->tries_size; i++)
{
if (!check_covered_area(&body->tries[i], routine))
continue;
attach_caught_code(binary, &body->tries[i], body->handlers, routine);
}
}
/******************************************************************************
* *
* Paramètres : binary = représentation binaire à traiter. *
* *
* 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)
{
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 */
if (!G_IS_DEX_FORMAT(g_loaded_binary_get_format(binary)))
return false;
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);
}
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);
}
}
return true;
}