/* Chrysalide - Outil d'analyse de fichiers binaires
* fetch.c - ajouts de sauts à traiter durant 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 "fetch.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "operands/it.h"
#include "operands/offset.h"
#include "operands/register.h"
#include "operands/reglist.h"
#include "../instruction.h"
#include "../register.h"
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant à faire suivre. *
* *
* Description : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void help_fetching_with_instruction_b_with_orig(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
const mrange_t *range; /* Emplacementt d'instruction */
virt_t pc; /* Position dans l'exécution */
GImmOperand *op; /* Opérande numérique en place */
int32_t offset; /* Décalage encodé en dur */
virt_t target; /* Adresse virtuelle visée */
range = g_arch_instruction_get_range(instr);
pc = get_virt_addr(get_mrange_addr(range));
switch (iset)
{
case AV7IS_ARM:
pc += 8;
break;
case AV7IS_THUMB:
pc += 4;
break;
default:
assert(0);
break;
}
//pc += get_mrange_length(range);
op = G_IMM_OPERAND(g_arch_instruction_get_operand(instr, 0));
if (g_imm_operand_get_value(op, MDS_32_BITS_SIGNED, &offset))
g_imm_operand_set_value(op, MDS_32_BITS_UNSIGNED, pc + offset);
else assert(0);
g_object_unref(G_OBJECT(op));
target = pc + offset;
//g_armv7_context_define_encoding(context, target, iset);
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, target, iset);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant à faire suivre. *
* *
* Description : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void help_fetching_with_instruction_bl_with_orig(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
const mrange_t *range; /* Emplacementt d'instruction */
virt_t pc; /* Position dans l'exécution */
GImmOperand *op; /* Opérande numérique en place */
int32_t offset; /* Décalage encodé en dur */
virt_t target; /* Adresse virtuelle visée */
range = g_arch_instruction_get_range(instr);
pc = get_virt_addr(get_mrange_addr(range));
/**
* Qu'on se trouve en mode Thumb ou ARM, l'instruction
* ne peut qu'être encodée sur 4 octets.
*/
assert(get_mrange_length(range) == 4);
switch (iset)
{
case AV7IS_ARM:
pc += 8;
break;
case AV7IS_THUMB:
pc += 4;
break;
default:
assert(0);
break;
}
op = G_IMM_OPERAND(g_arch_instruction_get_operand(instr, 0));
if (g_imm_operand_get_value(op, MDS_32_BITS_SIGNED, &offset))
g_imm_operand_set_value(op, MDS_32_BITS_UNSIGNED, pc + offset);
else assert(0);
g_object_unref(G_OBJECT(op));
target = pc + offset;
//g_armv7_context_define_encoding(context, target, iset);
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, target, iset);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant à inverser. *
* *
* Description : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void help_fetching_with_instruction_blx_with_dest(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
const mrange_t *range; /* Emplacementt d'instruction */
virt_t pc; /* Position dans l'exécution */
GImmOperand *op; /* Opérande numérique en place */
int32_t offset; /* Décalage encodé en dur */
virt_t target; /* Adresse virtuelle visée */
range = g_arch_instruction_get_range(instr);
pc = get_virt_addr(get_mrange_addr(range));
/**
* Qu'on se trouve en mode Thumb ou ARM, l'instruction
* ne peut qu'être encodée sur 4 octets.
*/
assert(get_mrange_length(range) == 4);
pc += 4;
pc -= pc % 4;
op = G_IMM_OPERAND(g_arch_instruction_get_operand(instr, 0));
if (g_imm_operand_get_value(op, MDS_32_BITS_SIGNED, &offset))
g_imm_operand_set_value(op, MDS_32_BITS_UNSIGNED, pc + offset);
else assert(0);
g_object_unref(G_OBJECT(op));
target = pc + offset;
//g_armv7_context_define_encoding(context, target, iset);
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, target, iset);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant à faire suivre. *
* *
* Description : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void help_fetching_with_instruction_bx_with_orig(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
GArchOperand *op; /* Opérande numérique en place */
GArmRegister *reg; /* Registre matériel manipulé */
bool is_pc; /* Nature de ce même registre */
const mrange_t *range; /* Emplacementt d'instruction */
virt_t pc; /* Position dans l'exécution */
op = g_arch_instruction_get_operand(instr, 0);
assert(G_IS_REGISTER_OPERAND(op));
/**
* On ne sait agir qu'avec le seul contenu facilement prédictible : pc !
*/
reg = G_ARM_REGISTER(g_register_operand_get_register(G_REGISTER_OPERAND(op)));
is_pc = (g_arm_register_get_index(reg) == 15 /* pc */);
g_object_unref(G_OBJECT(reg));
if (!is_pc) goto hfwibwo_no_pc;
/**
* On bascule alors le mode de décodage à cette adresse...
*/
range = g_arch_instruction_get_range(instr);
pc = get_virt_addr(get_mrange_addr(range));
switch (iset)
{
case AV7IS_ARM:
pc += 8;
//g_armv7_context_define_encoding(context,
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, pc, AV7IS_THUMB);
break;
case AV7IS_THUMB:
pc += 4;
//g_armv7_context_define_encoding(context,
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, pc, AV7IS_ARM);
break;
default:
assert(0);
break;
}
hfwibwo_no_pc:
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 : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void help_fetching_with_instruction_cb_n_z(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format)
{
const mrange_t *range; /* Emplacementt d'instruction */
virt_t pc; /* Position dans l'exécution */
GImmOperand *op; /* Opérande numérique en place */
uint32_t offset; /* Décalage encodé en dur */
virt_t target; /* Adresse virtuelle visée */
range = g_arch_instruction_get_range(instr);
pc = get_virt_addr(get_mrange_addr(range));
/**
* En mode Thumb, pc a pour valeur l'adresse courante plus 4.
*/
pc += 4;
op = G_IMM_OPERAND(g_arch_instruction_get_operand(instr, 1));
if (g_imm_operand_get_value(op, MDS_32_BITS_UNSIGNED, &offset))
g_imm_operand_set_value(op, MDS_32_BITS_UNSIGNED, pc + offset);
else assert(0);
g_object_unref(G_OBJECT(op));
target = pc + offset;
//g_armv7_context_define_encoding(context, target, AV7IS_THUMB);
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, target, AV7IS_THUMB);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant. *
* *
* Description : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void help_fetching_with_instruction_ldr_literal_with_orig(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
const mrange_t *range; /* Emplacementt d'instruction */
phys_t phys_pc; /* Position dans l'exécution */
GArchOperand *op; /* Opérande de surcouche */
uint32_t offset; /* Décalage encodé en dur */
bool ret; /* Bilan d'une récupération */
off_t val_offset; /* Position de valeur à lire */
vmpa2t loaded_addr; /* Adresse de valeur chargée */
mrange_t loaded_range; /* Espace de chargement */
GBinFormat *base; /* version parente du format */
GBinContent *content; /* Contenu binaire à relire */
SourceEndian endian; /* Boutisme du format parent */
uint32_t target; /* Adresse virtuelle visée */
vmpa2t pos; /* Tête de lecture de valeur */
VMPA_BUFFER(loc); /* Adresse au format texte */
GPreloadInfo *info; /* Informations préchargées */
GArchInstruction *loaded; /* Instruction de valeur */
bool inserted; /* Bilan d'une insertion */
char *desc; /* Description d'accompagnement*/
size_t index; /* Indice d'un existant ? */
GDbComment *comment; /* Définition de commentaire */
GArchOperand *new; /* Instruction de ciblage */
/* Récupération de l'adresse visée par le chargement */
range = g_arch_instruction_get_range(instr);
phys_pc = get_phy_addr(get_mrange_addr(range));
phys_pc &= ~3;
//phys_pc = (phys_pc + 3) & ~3;
switch (iset)
{
case AV7IS_ARM:
phys_pc += 8;
break;
case AV7IS_THUMB:
phys_pc += 4;
break;
default:
assert(0);
break;
}
g_arch_instruction_lock_operands(instr);
op = _g_arch_instruction_get_operand(instr, 1);
assert(G_IS_IMM_OPERAND(op));
ret = g_imm_operand_get_value(G_IMM_OPERAND(op), MDS_32_BITS_UNSIGNED, &offset);
if (!ret)
{
assert(0);
g_object_unref(G_OBJECT(op));
g_arch_instruction_unlock_operands(instr);
return;
}
/* Transformations et conservation d'une position de chargement */
val_offset = phys_pc + offset;
if (!g_exe_format_translate_offset_into_vmpa(format, val_offset, &loaded_addr))
{
/**
* Ce cas de figure correspond à la situation où une instruction "ldr"
* tente de charger une valeur qui se trouve sur un segment qui n'est
* pas chargé en mémoire par exemple.
*
* Dans la pratique, on pourrait parfois retrouver la valeur ciblée,
* mais effectivement cette donnée n'est pas disponible au moment de
* l'exécution.
*/
g_arch_processor_add_error(proc, APE_DISASSEMBLY, get_mrange_addr(range),
_("Unable to load a value which is not addressable"));
g_object_unref(G_OBJECT(op));
g_arch_instruction_unlock_operands(instr);
return;
}
init_mrange(&loaded_range, &loaded_addr, 4);
/* Lecture de la valeur vers laquelle renvoyer */
base = G_BIN_FORMAT(format);
content = g_known_format_get_content(G_KNOWN_FORMAT(base));
endian = g_binary_format_get_endianness(base);
copy_vmpa(&pos, &loaded_addr);
ret = g_binary_content_read_u32(content, &pos, endian, &target);
g_object_unref(G_OBJECT(content));
if (!ret)
{
g_object_unref(G_OBJECT(op));
g_arch_instruction_unlock_operands(instr);
return;
}
/* Réalise l'intégration de la valeur chargée */
copy_vmpa(&pos, &loaded_addr);
info = G_PRELOAD_INFO(context);
loaded = g_raw_instruction_new_from_value(&pos, MDS_32_BITS_UNSIGNED, target);
inserted = g_preload_info_add_instruction(info, loaded);
if (inserted)
{
/* Commentaire associé */
vmpa2_virt_to_string(get_mrange_addr(range), MDS_32_BITS, loc, NULL);
asprintf(&desc, _("Value used @ %s"), loc);
g_preload_info_lock_comments(info);
comment = g_preload_info_find_comment_at(info, &loaded_addr, &index);
if (comment == NULL)
{
comment = g_db_comment_new(&loaded_addr, CET_INLINED, BLF_HAS_CODE, desc);
g_db_item_add_flag(G_DB_ITEM(comment), DIF_VOLATILE);
_g_preload_info_add_comment(info, comment);
}
else
{
desc = strprep(desc, "\n");
desc = strprep(desc, g_db_comment_get_text(comment));
g_object_unref(G_OBJECT(comment));
comment = g_db_comment_new(&loaded_addr, CET_INLINED, BLF_HAS_CODE, desc);
g_db_item_add_flag(G_DB_ITEM(comment), DIF_VOLATILE);
g_preload_info_replace_comment_at(info, index, comment);
}
g_preload_info_unlock_comments(info);
free(desc);
}
/* Mise à jour de l'affichage et conclusion */
new = g_imm_operand_new_from_value(MDS_32_BITS_UNSIGNED, target);
_g_arch_instruction_replace_operand(instr, op, new);
g_object_unref(G_OBJECT(op));
g_arch_instruction_unlock_operands(instr);
//exit(0);
//target = pc + offset;
if (target & 0x1)
iset = AV7IS_THUMB;
//else
// iset = AV7IS_ARM;
//g_armv7_context_define_encoding(context, target, AV7IS_THUMB);
g_proc_context_push_drop_point(G_PROC_CONTEXT(context), DPL_OTHER, target, iset);
//exit(0);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant. *
* *
* Description : Applique la mise à jour d'un registre après coup. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void apply_write_back(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
GArchOperand *op; /* Opérande de registre */
g_arch_instruction_lock_operands(instr);
op = _g_arch_instruction_get_operand(instr, 0);
assert(G_IS_ARMV7_REGISTER_OPERAND(op));
g_armv7_register_operand_write_back(G_ARMV7_REGISTER_OPERAND(op), true);
g_object_unref(G_OBJECT(op));
g_arch_instruction_unlock_operands(instr);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant. *
* *
* Description : Applique la mise à jour d'un registre après coup au besoin. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void apply_write_back_from_registers(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
GArchOperand *op; /* Opérande à manipuler */
GArmV7RegisterOperand *regop; /* Opérande de registre */
GArchRegister *reg_ref; /* Registre de référence */
GArmV7RegListOperand *reglist; /* Opérande de liste de reg. */
size_t count; /* Taille de la liste */
bool inside; /* Intersection de registres */
size_t i; /* Boucle de parcours */
GArmV7Register *reg; /* Registre à analyser */
g_arch_instruction_lock_operands(instr);
op = _g_arch_instruction_get_operand(instr, 0);
assert(G_IS_ARMV7_REGISTER_OPERAND(op));
regop = G_ARMV7_REGISTER_OPERAND(op);
reg_ref = g_register_operand_get_register(G_REGISTER_OPERAND(regop));
op = _g_arch_instruction_get_operand(instr, 1);
assert(G_IS_ARMV7_REGLIST_OPERAND(op));
reglist = G_ARMV7_REGLIST_OPERAND(op);
count = g_armv7_reglist_count_registers(reglist);
inside = false;
for (i = 0; i < count && !inside; i++)
{
reg = g_armv7_reglist_operand_get_register(reglist, i);
inside = (g_arch_register_compare(reg_ref, G_ARCH_REGISTER(reg)) == 0);
g_object_unref(G_OBJECT(reg));
}
if (!inside)
g_armv7_register_operand_write_back(regop, true);
g_object_unref(G_OBJECT(regop));
g_object_unref(G_OBJECT(reglist));
g_arch_instruction_unlock_operands(instr);
}
/******************************************************************************
* *
* 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. *
* iset = type de jeu d'instructions courant. *
* *
* Description : Construit un suffixe adapté à une instruction IT. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void build_it_instruction_suffix(GArchInstruction *instr, GArchProcessor *proc, GArmV7Context *context, GExeFormat *format, ArmV7InstrSet iset)
{
GArchOperand *op; /* Opérande à manipuler */
GArmV7ITCondOperand *itcond; /* Opérande de l'instruction */
uint8_t firstcond; /* Indication sur la condition */
uint8_t mask; /* Masque d'application */
char suffix[4]; /* Suffixe à attribuer */
g_arch_instruction_lock_operands(instr);
op = _g_arch_instruction_get_operand(instr, 0);
assert(G_IS_ARMV7_ITCOND_OPERAND(op));
itcond = G_ARMV7_ITCOND_OPERAND(op);
firstcond = g_armv7_itcond_operand_get_firstcond(itcond);
mask = g_armv7_itcond_operand_get_mask(itcond);
firstcond &= 0x1;
if ((mask & 0x7) == 0x4)
{
suffix[0] = ((mask & 0x8) >> 3) == firstcond ? 't' : 'e';
suffix[1] = '\0';
}
else if ((mask & 0x3) == 0x2)
{
suffix[0] = ((mask & 0x8) >> 3) == firstcond ? 't' : 'e';
suffix[1] = ((mask & 0x4) >> 2) == firstcond ? 't' : 'e';
suffix[2] = '\0';
}
else if ((mask & 0x1) == 0x1)
{
suffix[0] = ((mask & 0x8) >> 3) == firstcond ? 't' : 'e';
suffix[1] = ((mask & 0x4) >> 2) == firstcond ? 't' : 'e';
suffix[2] = ((mask & 0x2) >> 1) == firstcond ? 't' : 'e';
suffix[3] = '\0';
}
else
suffix[0] = '\0';
if (suffix[0] != '\0')
g_arm_instruction_extend_keyword(G_ARM_INSTRUCTION(instr), suffix);
g_object_unref(G_OBJECT(itcond));
g_arch_instruction_unlock_operands(instr);
}