/* Chrysalide - Outil d'analyse de fichiers binaires
* switch.c - apport de précisions sur les aiguillages Dalvik
*
* Copyright (C) 2012-2013 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* 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 "switch.h"
#include
#include
#include
#include
#include
#include
#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);
/* Prend en compte les absences de 'break' dans les cas. */
static void ensure_each_case_has_its_block(GArchInstruction *, GArchInstruction *);
/* 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 */
uint32_t value; /* Valeur à indiquer */
GArchOperand *imm; /* Forme de la valeur reconnue */
/* 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)
{
if (dswitch->packed.ident == DPO_PACKED_SWITCH)
value = dswitch->packed.first_key + i;
else
value = dswitch->sparse.keys[i];
imm = g_imm_operand_new_from_value(MDS_32_BITS_UNSIGNED, value);
g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP, imm);
ensure_each_case_has_its_block(next, instrs);
}
}
/* 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, NULL);
ensure_each_case_has_its_block(next, instrs);
}
}
/******************************************************************************
* *
* Paramètres : instr = instruction d'aiguillage rencontrée. *
* instrs = liste des instructions pour tout le binaire. *
* *
* Description : Prend en compte les absences de 'break' dans les cas. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void ensure_each_case_has_its_block(GArchInstruction *instr, GArchInstruction *instrs)
{
GArchInstruction *prev; /* Instruction avant un cas */
/**
* La situation où un cas n'a pas de 'break' conduit à une fusion du corps
* du cas avec le corps du cas suivant (partie commune).
* La fin du premier cas n'ayant pas de saut enregistré, rien n'entraîne un
* lien logique, et donc une séparation des deux cas en blocs distincts.
*
* Cette procédure établit les vérifications nécessaires, et rétablit
* la logique des liens et des blocs en cas de besoin.
*/
prev = g_arch_instruction_get_prev_iter(instrs, instr);
if (prev != NULL
&& !g_arch_instruction_has_destinations(prev)
&& !(g_arch_instruction_get_flags(prev) & AIF_RETURN_POINT))
{
g_arch_instruction_link_with(prev, instr, ILT_EXEC_FLOW, NULL);
}
}
/******************************************************************************
* *
* 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);
g_object_unref(G_OBJECT(routine));
for (iter = g_arch_instruction_find_by_address(instrs, start, true);
iter != NULL;
iter = g_arch_instruction_get_next_iter(instrs, iter, end))
{
if (!G_IS_DALVIK_INSTRUCTION(iter))
continue;
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)
{
GDexFormat *format; /* Format du binaire chargé */
GArchProcessor *proc; /* Processeur de l'architecture*/
GArchInstruction *instrs; /* Instructions Dalvik */
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));
proc = g_loaded_binary_get_processor(binary);
instrs = g_arch_processor_get_disassembled_instructions(proc);
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);
}
}
g_object_unref(G_OBJECT(proc));
return true;
}