diff options
Diffstat (limited to 'plugins/androhelpers/switch.c')
-rw-r--r-- | plugins/androhelpers/switch.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/plugins/androhelpers/switch.c b/plugins/androhelpers/switch.c new file mode 100644 index 0000000..a6f6f5b --- /dev/null +++ b/plugins/androhelpers/switch.c @@ -0,0 +1,360 @@ + +/* OpenIDA - Outil d'analyse de fichiers binaires + * switch.c - apport de précisions sur les aiguillages Dalvik + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#include "switch.h" + + +#include <string.h> + + +#include <arch/dalvik/instruction.h> +#include <arch/dalvik/instruction-def.h> +#include <arch/dalvik/operands/target.h> +#include <format/dex/dex-int.h> +#include <../i18n.h> + + + +/* Récupère les détails d'un aiguillage. */ +static bool load_dex_switch(const GArchInstruction *, GArchInstruction *, const GDexFormat *, dex_switch *); + +/* Lie les instructions selon les cas d'un aiguillage. */ +static void link_all_switch_cases(GArchInstruction *, const dex_switch *, GArchInstruction *, vmpa_t, vmpa_t); + +/* 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); + +/* Recherche des aiguillages dans chaque instruction. */ +static void look_for_switch_instructions(const GDexMethod *, GArchInstruction *, const GLoadedBinary *, const GDexFormat *, bool); + + + +/****************************************************************************** +* * +* Paramètres : instr = instruction d'aiguillage rencontrée. * +* instrs = liste des instructions pour tout le binaire. * +* format = format du binaire Dex. * +* dswitch = détails de l'aiguillage à reconstituer. [OUT] * +* * +* Description : Récupère les détails d'un aiguillage. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool load_dex_switch(const GArchInstruction *instr, GArchInstruction *instrs, const GDexFormat *format, dex_switch *dswitch) +{ + bool result; /* Bilan à retourner */ + GArchOperand *operand; /* Operande à manipuler */ + const GImmOperand *imm; /* Valeur concrête */ + vmpa_t addr; /* Adresse du corps des infos */ + GArchInstruction *info; /* Corps des infos brutes */ + off_t pos; /* Position dans le binaire */ + uint32_t *targets; /* Cibles relatives à corriger */ + uint16_t i; /* Boucle de parcours */ + + /* Récupération de l'opérande */ + + operand = g_arch_instruction_get_operand(instr, 1); + + if (!G_IS_DALVIK_TARGET_OPERAND(operand)) + return false; + + imm = g_dalvik_target_operand_get_value(G_DALVIK_TARGET_OPERAND(operand)); + + if (!g_imm_operand_to_vmpa_t(imm, &addr)) + return false; + + /* Lecture des détails */ + + info = g_arch_instruction_find_by_address(instrs, addr, true); + if (info == NULL) + return false; + + g_arch_instruction_get_location(info, &pos, NULL, NULL); + + result = read_dex_switch(format, &pos, dswitch); + + /* Ajustement relatif */ + + if (result) + { + g_arch_instruction_get_location(instr, NULL, NULL, &addr); + + if (dswitch->packed.ident == DPO_PACKED_SWITCH) + targets = dswitch->packed.targets; + else + targets = dswitch->sparse.targets; + + for (i = 0; i < dswitch->packed.size; i++) + targets[i] = ((uint32_t)addr) + targets[i] * sizeof(uint16_t); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction d'aiguillage rencontrée. * +* dswitch = détails de l'aiguillage à reconstituer. * +* instrs = liste des instructions pour tout le binaire. * +* start = début de la zone théoriquement couverte. * +* end = fin de la zone théoriquement couverte. * +* * +* Description : Lie les instructions selon les cas d'un aiguillage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void link_all_switch_cases(GArchInstruction *instr, const dex_switch *dswitch, GArchInstruction *instrs, vmpa_t start, vmpa_t end) +{ + uint32_t *targets; /* Cibles relatives à corriger */ + uint16_t i; /* Boucle de parcours */ + GArchInstruction *next; /* Instruction suivante */ + + /* Valeurs définies */ + + if (dswitch->packed.ident == DPO_PACKED_SWITCH) + targets = dswitch->packed.targets; + else + targets = dswitch->sparse.targets; + + for (i = 0; i < dswitch->packed.size; i++) + { + if (!(start <= targets[i] && targets[i] < end)) + continue; + + next = g_arch_instruction_find_by_address(instrs, (vmpa_t)targets[i], true); + + if (next != NULL) + g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP); + + } + + /* Cas du défaut */ + + next = g_arch_instruction_get_next_iter(instrs, instr, end); + + if (next != NULL) + g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP); + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction d'aiguillage rencontrée. * +* dswitch = détails de l'aiguillage à reconstituer. * +* instrs = liste des instructions pour tout le binaire. * +* binary = représentation binaire à traiter. * +* start = début de la zone théoriquement couverte. * +* end = fin de la zone théoriquement couverte. * +* * +* Description : Insère des indications dans le texte humainement lisibles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void mark_all_switch_cases(const GArchInstruction *instr, const dex_switch *dswitch, GArchInstruction *instrs, const GLoadedBinary *binary, vmpa_t start, vmpa_t end) +{ + GCodeBuffer *buffer; /* Contenu textuel à modifier */ + uint32_t *targets; /* Cibles relatives à corriger */ + uint16_t i; /* Boucle de parcours */ + uint16_t index; /* Véritable indice recalculé */ + uint32_t value; /* Valeur à indiquer */ + GBufferLine *line; /* Nouvelle ligne à compléter */ + size_t len; /* Taille de la description */ + char *fulldesc; /* Description complète */ + GArchInstruction *next; /* Instruction suivante */ + vmpa_t addr; /* Adresse de cette instruction*/ + + buffer = g_loaded_binary_get_disassembled_buffer(binary); + + /* Valeurs définies */ + + if (dswitch->packed.ident == DPO_PACKED_SWITCH) + targets = dswitch->packed.targets; + else + targets = dswitch->sparse.targets; + + for (i = dswitch->packed.size; i > 0; i--) + { + index = i - 1; + + if (!(start <= targets[index] && targets[index] < end)) + continue; + + if (dswitch->packed.ident == DPO_PACKED_SWITCH) + value = dswitch->packed.first_key + index; + else + value = dswitch->sparse.keys[index]; + + line = g_code_buffer_insert_at(buffer, (vmpa_t)targets[index], true); + g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD); + + len = strlen(_("; Case for value 0x%08x (%d)")) + 8 + strlen("4294967295U") /* UINT_MAX */; + fulldesc = (char *)calloc(len + 1, sizeof(char)); + len = snprintf(fulldesc, len + 1, _("; Case for value 0x%08x (%d)"), value, value); + + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, fulldesc, len, RTT_INDICATION); + + free(fulldesc); + + } + + /* Cas du défaut */ + + next = g_arch_instruction_get_next_iter(instrs, instr, end); + + if (next != NULL) + { + g_arch_instruction_get_location(next, NULL, NULL, &addr); + + line = g_code_buffer_insert_at(buffer, addr, true); + g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD); + + fulldesc = _("; Default case"); + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, + fulldesc, strlen(fulldesc), RTT_INDICATION); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : method = routine à venir parcourir. * +* instrs = liste des instructions pour tout le binaire. * +* binary = représentation binaire à traiter. * +* format = format du binaire Dex. * +* link = édition de liens ou impression de commentaires ? * +* * +* Description : Recherche des aiguillages dans chaque instruction. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void look_for_switch_instructions(const GDexMethod *method, GArchInstruction *instrs, const GLoadedBinary *binary, const GDexFormat *format, bool link) +{ + GBinRoutine *routine; /* Abstraction de la méthode */ + vmpa_t start; /* Début de la zone couverte */ + vmpa_t end; /* Fin de la zone couverte */ + GArchInstruction *iter; /* Boucle de parcours */ + DalvikOpcodes opcode; /* Type d'instruction Dalvik */ + dex_switch dswitch; /* Infos d'aiguillage */ + + routine = g_dex_method_get_routine(method); + + start = g_binary_routine_get_address(routine); + end = start + g_binary_routine_get_size(routine); + + for (iter = g_arch_instruction_find_by_address(instrs, start, true); + iter != NULL; + iter = g_arch_instruction_get_next_iter(instrs, iter, end)) + { + opcode = g_dalvik_instruction_get_opcode(G_DALVIK_INSTRUCTION(iter)); + + if (opcode != DOP_PACKED_SWITCH && opcode != DOP_SPARSE_SWITCH) + continue; + + if (!load_dex_switch(iter, instrs, format, &dswitch)) + continue; + + if (link) + link_all_switch_cases(iter, &dswitch, instrs, start, end); + else + mark_all_switch_cases(iter, &dswitch, instrs, binary, start, end); + + reset_dex_switch(&dswitch); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : binary = représentation binaire à traiter. * +* link = édition de liens ou impression de commentaires ? * +* * +* Description : Traite les données binaires associées aux switchs. * +* * +* Retour : true si une action a été menée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool extract_switch_info(GLoadedBinary *binary, bool link) +{ + GArchInstruction *instrs; /* Instructions Dalvik */ + 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 */ + + instrs = g_loaded_binary_get_instructions(binary); + 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_switch_instructions(method, instrs, binary, format, 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_switch_instructions(method, instrs, binary, format, link); + } + + } + + return true; + +} |