summaryrefslogtreecommitdiff
path: root/plugins/androhelpers/switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/androhelpers/switch.c')
-rw-r--r--plugins/androhelpers/switch.c360
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;
+
+}