/* Chrysalide - Outil d'analyse de fichiers binaires * fetch.c - ajouts de sauts à traiter durant la phase de désassemblage * * Copyright (C) 2015-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 Chrysalide. If not, see . */ #include "fetch.h" #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 */ GBinContent *content; /* Contenu binaire à relire */ 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*/ 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 */ content = g_binary_format_get_content(G_BIN_FORMAT(format)); copy_vmpa(&pos, &loaded_addr); ret = g_binary_content_read_u32(content, &pos, SRE_LITTLE /* FIXME */, &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); if (comment != NULL) { g_db_comment_add_static_text(comment, "\n"); g_db_comment_add_dynamic_text(comment, desc); } else { comment = g_db_comment_new_inlined(&loaded_addr, BLF_HAS_CODE, false); g_db_item_set_volatile(G_DB_ITEM(comment), true); g_db_comment_add_dynamic_text(comment, desc); _g_preload_info_add_comment(info, comment); } g_preload_info_unlock_comments(info); } /* 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); }