/* Chrysalide - Outil d'analyse de fichiers binaires
* link.c - édition des liens après la phase de désassemblage
*
* Copyright (C) 2017-2020 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 Chrysalide. If not, see .
*/
#include "link.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "operands/pool.h"
#include "pseudo/switch.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;
/******************************************************************************
* *
* 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 une référence entre utilisation et origine de chaîne.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void handle_links_for_dalvik_string(GArchInstruction *instr, GArchProcessor *proc, GProcContext *context, GExeFormat *format)
{
GArchOperand *op; /* Opérande numérique en place */
uint32_t index; /* Indice dans la table Dex */
GDexPool *pool; /* Table de ressources */
GBinSymbol *string; /* Emplacement de la chaîne */
const mrange_t *range; /* Zone d'occupation */
GArchInstruction *target; /* Ligne visée par la référence*/
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);
assert(G_IS_DALVIK_POOL_OPERAND(op));
assert(g_dalvik_pool_operand_get_pool_type(G_DALVIK_POOL_OPERAND(op)) == DPT_STRING);
index = g_dalvik_pool_operand_get_index(G_DALVIK_POOL_OPERAND(op));
pool = g_dex_format_get_pool(G_DEX_FORMAT(format));
string = g_dex_pool_get_string_symbol(pool, index);
g_object_unref(G_OBJECT(pool));
if (string != NULL)
{
range = g_binary_symbol_get_range(string);
target = g_arch_processor_find_instr_by_address(proc, get_mrange_addr(range));
if (target != NULL)
{
g_arch_instruction_link_with(instr, target, ILT_REF);
g_arch_instruction_link_with(target, instr, ILT_REF);
g_object_unref(G_OBJECT(target));
}
g_object_unref(G_OBJECT(string));
}
g_object_unref(G_OBJECT(op));
}
/******************************************************************************
* *
* 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 */
const mrange_t *range; /* Emplacement de l'instruction*/
bool defined; /* Adresse définie ? */
vmpa2t addr; /* Adresse de destination */
GArchInstruction *switch_ins; /* Instruction de branchements */
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 */
uint16_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);
if (G_IS_TARGETABLE_OPERAND(op))
{
range = g_arch_instruction_get_range(instr);
defined = g_targetable_operand_get_addr(G_TARGETABLE_OPERAND(op), get_mrange_addr(range),
G_BIN_FORMAT(format), proc, &addr);
}
else
defined = false;
g_object_unref(G_OBJECT(op));
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)
msg = stradd(msg, "\n");
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
{
msg = stradd(msg, "\n");
msg = stradd(msg, _("Defaut case"));
}
}
item = g_db_comment_new(&comment->handler, CET_BEFORE, BLF_NONE, msg);
g_db_item_add_flag(G_DB_ITEM(item), DIF_VOLATILE);
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));
}
}
/******************************************************************************
* *
* 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 une référence entre appelant et appelé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void handle_links_between_caller_and_callee(GArchInstruction *instr, GArchProcessor *proc, GProcContext *context, GExeFormat *format)
{
GArchOperand *op; /* Opérande numérique en place */
uint32_t index; /* Indice dans la table Dex */
GDexPool *pool; /* Table de ressources */
GDexMethod *method; /* Méthode ciblée ici */
GBinRoutine *routine; /* Routine liée à la méthode */
const mrange_t *range; /* Zone d'occupation */
GArchInstruction *target; /* Ligne visée par la référence*/
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);
assert(G_IS_DALVIK_POOL_OPERAND(op));
assert(g_dalvik_pool_operand_get_pool_type(G_DALVIK_POOL_OPERAND(op)) == DPT_METHOD);
index = g_dalvik_pool_operand_get_index(G_DALVIK_POOL_OPERAND(op));
pool = g_dex_format_get_pool(G_DEX_FORMAT(format));
method = g_dex_pool_get_method(pool, index);
g_object_unref(G_OBJECT(pool));
if (method != NULL)
{
routine = g_dex_method_get_routine(method);
range = g_binary_symbol_get_range(G_BIN_SYMBOL(routine));
if (range->addr.physical > 0)
{
target = g_arch_processor_find_instr_by_address(proc, get_mrange_addr(range));
if (target != NULL)
{
g_arch_instruction_link_with(instr, target, ILT_REF);
g_object_unref(G_OBJECT(target));
}
}
g_object_unref(G_OBJECT(routine));
g_object_unref(G_OBJECT(method));
}
g_object_unref(G_OBJECT(op));
}