/* Chrysalide - Outil d'analyse de fichiers binaires
* link.c - édition des liens après la phase de désassemblage
*
* Copyright (C) 2016-2017 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 "link.h"
#include
#include
#include
#include
#include
#include "pseudo/switch.h"
#include "../target.h"
#include "../../common/extstr.h"
/* Mémorisation des cas rencontrés */
typedef struct _case_comment
{
bool valid; /* Entrée utilisable ? */
vmpa2t handler; /* Position du code associé */
bool is_default; /* Gestion par défaut ? */
union
{
int32_t key; /* Clef unique */
int32_t *keys; /* Ensemble de clefs dynamique */
};
size_t count; /* Nombre de clefs conservées */
} case_comment;
/* REMME */
#define COMMENT_LINE_SEP "\n"
/******************************************************************************
* *
* Paramètres : instr = instruction ARMv7 à traiter. *
* proc = représentation de l'architecture utilisée. *
* context = contexte associé à la phase de désassemblage. *
* format = acès aux données du binaire d'origine. *
* *
* Description : Etablit tous les liens liés à un embranchement compressé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void handle_dalvik_packed_switch_links(GArchInstruction *instr, GArchProcessor *proc, GProcContext *context, GExeFormat *format)
{
GArchOperand *op; /* Opérande numérique en place */
bool defined; /* Adresse définie ? */
vmpa2t addr; /* Adresse de destination */
virt_t virt; /* Adresse virtuelle */
GArchInstruction *switch_ins; /* Instruction de branchements */
const mrange_t *range; /* Zone d'occupation */
const vmpa2t *start_addr; /* Adresse de référentiel */
const int32_t *keys; /* Conditions de sauts */
const int32_t *targets; /* Positions relatives liées */
uint16_t count; /* Taille de ces tableaux */
case_comment *comments; /* Mémorisation progressive */
vmpa2t def_addr; /* Traitement par défaut */
GArchInstruction *target; /* Ligne visée par la référence*/
case_comment *comment; /* Commentaire à éditer */
uint16_t i; /* Boucle de parcours #1 */
size_t j; /* Boucle de parcours #2 */
int32_t tmp; /* Sauvegarde temporaire */
char *msg; /* Indication à imprimer */
size_t k; /* Boucle de parcours #3 */
char *int_val; /* Valeur en chaîne de carac. */
GDbComment *item; /* Indication sur la condition */
g_arch_instruction_lock_operands(instr);
assert(_g_arch_instruction_count_operands(instr) == 2);
op = _g_arch_instruction_get_operand(instr, 1);
g_arch_instruction_unlock_operands(instr);
defined = false;
if (G_IS_TARGET_OPERAND(op))
{
g_target_operand_get_addr(G_TARGET_OPERAND(op), &addr);
defined = true;
}
else if (G_IS_IMM_OPERAND(op))
{
if (g_imm_operand_to_virt_t(G_IMM_OPERAND(op), &virt))
{
init_vmpa(&addr, VMPA_NO_PHYSICAL, virt);
defined = true;
}
}
if (defined)
{
switch_ins = g_arch_processor_find_instr_by_address(proc, &addr);
if (G_IS_DALVIK_SWITCH_INSTR(switch_ins))
{
range = g_arch_instruction_get_range(instr);
start_addr = get_mrange_addr(range);
/* Préparation de l'édition des commentaires */
count = g_dalvik_switch_get_data(G_DALVIK_SWITCH_INSTR(switch_ins), &keys, &targets);
comments = (case_comment *)calloc(1 + count, sizeof(case_comment));
/* Cas par défaut */
compute_mrange_end_addr(range, &def_addr);
target = g_arch_processor_find_instr_by_address(proc, &def_addr);
if (target != NULL)
{
comment = &comments[0];
comment->valid = true;
copy_vmpa(&comment->handler, &def_addr);
comment->is_default = true;
g_arch_instruction_link_with(instr, target, ILT_CASE_JUMP);
g_object_unref(G_OBJECT(target));
}
/* Autres cas */
for (i = 0; i < count; i++)
{
copy_vmpa(&addr, start_addr);
advance_vmpa(&addr, targets[i] * sizeof(uint16_t));
if (cmp_vmpa(&addr, &def_addr) == 0)
continue;
target = g_arch_processor_find_instr_by_address(proc, &addr);
if (target != NULL)
{
for (j = 0; j < (1 + count); j++)
{
if (!comments[j].valid)
break;
if (cmp_vmpa(&addr, &comments[j].handler) == 0)
break;
}
assert(j < (1 + count));
comment = &comments[j];
if (!comment->valid)
{
comment->valid = true;
copy_vmpa(&comment->handler, &addr);
comment->key = keys[i];
comment->count = 1;
}
else
{
if (comment->count == 0)
comment->key = keys[i];
if (comment->count == 1)
{
tmp = comment->key;
comment->keys = (int32_t *)calloc(2, sizeof(int32_t));
comment->keys[0] = tmp;
comment->keys[1] = keys[i];
comment->count = 2;
}
else
{
comment->count++;
comment->keys = (int32_t *)realloc(comment->keys, comment->count * sizeof(int32_t));
comment->keys[comment->count - 1] = keys[i];
}
}
g_arch_instruction_link_with(instr, target, ILT_CASE_JUMP);
g_object_unref(G_OBJECT(target));
}
}
/* Edition des commentaires et nettoyage */
for (j = 0; j < (1 + count); j++)
{
comment = &comments[j];
if (!comment->valid)
break;
switch (comment->count)
{
case 0:
msg = NULL;
break;
case 1:
asprintf(&msg, _("Case %d"), comment->key);
break;
default:
msg = NULL;
/**
* Les spécifications indiquent que les clefs sont triées.
* Donc nul besoin de s'occuper de leur ordre ici.
*/
for (k = 0; k < comment->count; k++)
{
if (k > 0)
/* FIXME : encapsuler ! */
msg = stradd(msg, COMMENT_LINE_SEP);
asprintf(&int_val, _("Case %d:"), comment->keys[k]);
msg = stradd(msg, int_val);
free(int_val);
}
break;
}
if (comment->is_default)
{
if (msg == NULL)
msg = strdup(_("Defaut case:"));
else
{
/* FIXME : encapsuler ! */
msg = stradd(msg, COMMENT_LINE_SEP);
msg = stradd(msg, _("Defaut case"));
}
}
item = g_db_comment_new_area(&comment->handler, BLF_NONE, msg, true);
g_db_item_set_volatile(G_DB_ITEM(item), true);
g_proc_context_add_db_item(context, G_DB_ITEM(item));
free(msg);
if (comment->count > 1)
free(comment->keys);
}
free(comments);
}
if (switch_ins != NULL)
g_object_unref(G_OBJECT(switch_ins));
}
}