/* OpenIDA - Outil d'analyse de fichiers binaires * processor.c - gestion de l'architecture x86 * * Copyright (C) 2008 Cyrille Bagard * * This file is part of OpenIDA. * * 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 "processor.h" #include #include #include "../processor-int.h" #include "instruction.h" #include "opcodes.h" #include "operand.h" typedef asm_x86_instr * (* read_instr) (const uint8_t *, off_t *, off_t, uint64_t, const asm_x86_processor *); /* Carte d'identité d'un opcode */ typedef struct _x86_opcode { uint8_t prefix; /* préfixe eventuel */ uint8_t opcode; /* Opcode seul */ uint8_t op_ext; /* Extension de l'opcode */ bool opt_prefix; /* Préfixe optionnel ? */ bool has_op_ext; /* Ext. à prendre en compte ? */ const char *name; /* Désignation humaine */ read_instr read; /* Décodage de l'instruction */ } x86_opcode; #define EXT_OPCODE_MASK 0x38 #define register_opcode(target, _prefix, _opcode, _name, _read) \ do { \ target.prefix = _prefix; \ target.opcode = _opcode; \ target.opt_prefix = true; \ target.has_op_ext = false; \ target.name = _name; \ target.read = _read; \ } while (0) #define register_opcode_with_ext(target, _prefix, _opcode, _ext, _name, _read) \ do { \ target.prefix = _prefix; \ target.opcode = _opcode; \ target.op_ext = _ext << 3; \ target.opt_prefix = true; \ target.has_op_ext = true; \ target.name = _name; \ target.read = _read; \ } while (0) /* Définition générique d'une architecture */ struct _asm_x86_processor { asm_processor base; /* A laisser en premier... */ AsmOperandSize operand_size; /* Taille par défaut */ x86_opcode opcodes[X86_OP_COUNT]; /* Liste des opcodes supportés */ }; /* Enregistre toutes les instructions reconnues pour x86. */ void x86_register_instructions(asm_x86_processor *); /* Décode une instruction dans un flux de données. */ asm_instr *x86_fetch_instruction(const asm_x86_processor *, const uint8_t *, off_t *, off_t, uint64_t); /* Traduit une instruction en version humainement lisible. */ void x86_print_instruction(const asm_x86_processor *, const asm_x86_instr *, char *, size_t, AsmSyntax); /****************************************************************************** * * * Paramètres : - * * * * Description : Crée le support de l'architecture x86. * * * * Retour : Architecture mise en place. * * * * Remarques : - * * * ******************************************************************************/ asm_processor *create_x86_processor(void) { asm_x86_processor *result; /* Architecture à retourner */ result = (asm_x86_processor *)calloc(1, sizeof(asm_x86_processor)); result->operand_size = AOS_32_BITS; x86_register_instructions(result); ASM_PROCESSOR(result)->fetch_instr = (fetch_instruction)x86_fetch_instruction; ASM_PROCESSOR(result)->print_instr = (print_instruction)x86_print_instruction; return ASM_PROCESSOR(result); } /****************************************************************************** * * * Paramètres : proc = architecture visée par la consultation. * * * * Description : Fournit la taille courante des opérandes pour x86. * * * * Retour : Taille d'opérande (16 ou 32 bits). * * * * Remarques : - * * * ******************************************************************************/ AsmOperandSize get_x86_current_operand_size(const asm_x86_processor *proc) { return proc->operand_size; } /****************************************************************************** * * * Paramètres : proc = architecture visée par la consultation. * * * * Description : Fournit la taille supplantée des opérandes pour x86. * * * * Retour : Taille d'opérande (16 ou 32 bits). * * * * Remarques : - * * * ******************************************************************************/ AsmOperandSize switch_x86_operand_size(const asm_x86_processor *proc) { return (proc->operand_size == AOS_32_BITS ? AOS_16_BITS : AOS_32_BITS); } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * * * Description : Enregistre toutes les instructions reconnues pour x86. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void x86_register_instructions(asm_x86_processor *proc) { register_opcode(proc->opcodes[X86_OP_XOR_REG1632], 0x00/*0x66*/, 0x31, "xor", read_instr_xor_with_reg1632); register_opcode(proc->opcodes[X86_OP_INC_E_AX], 0x66, 0x40, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_CX], 0x66, 0x41, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_DX], 0x66, 0x42, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_BX], 0x66, 0x43, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_SP], 0x66, 0x44, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_BP], 0x66, 0x45, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_SI], 0x66, 0x46, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_INC_E_DI], 0x66, 0x47, "inc", read_instr_inc_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_AX], 0x66, 0x48, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_CX], 0x66, 0x49, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_DX], 0x66, 0x4a, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_BX], 0x66, 0x4b, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_SP], 0x66, 0x4c, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_BP], 0x66, 0x4d, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_SI], 0x66, 0x4e, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_DEC_E_DI], 0x66, 0x4f, "dec", read_instr_dec_1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_AX], 0x66, 0x50, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_CX], 0x66, 0x51, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_DX], 0x66, 0x52, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_BX], 0x66, 0x53, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_SP], 0x66, 0x54, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_BP], 0x66, 0x55, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_SI], 0x66, 0x56, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_E_DI], 0x66, 0x57, "push", read_instr_push_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_AX], 0x66, 0x58, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_CX], 0x66, 0x59, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_DX], 0x66, 0x5a, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_BX], 0x66, 0x5b, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_SP], 0x66, 0x5c, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_BP], 0x66, 0x5d, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_SI], 0x66, 0x5e, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_POP_E_DI], 0x66, 0x5f, "pop", read_instr_pop_reg1632); register_opcode(proc->opcodes[X86_OP_PUSH_IMM1632], 0x66, 0x68, "push", read_instr_push_imm1632); register_opcode(proc->opcodes[X86_OP_MOV_REG1632], 0x66, 0x89, "mov", read_instr_mov_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_ADD8_REG1632], 0x66, 0x83, 0, "add", read_instr_add8_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_OR8_REG1632], 0x66, 0x83, 1, "or", read_instr_or8_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_ADC8_REG1632], 0x66, 0x83, 2, "adc", read_instr_adc8_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_SBB8_REG1632], 0x66, 0x83, 3, "sbb", read_instr_sbb8_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_AND8_REG1632], 0x66, 0x83, 4, "and", read_instr_and8_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_SUB8_REG1632], 0x66, 0x83, 5, "sub", read_instr_sub8_with_reg1632); register_opcode_with_ext(proc->opcodes[X86_OP_XOR8_REG1632], 0x66, 0x83, 6, "xor", read_instr_xor8_with_reg1632); register_opcode(proc->opcodes[X86_OP_MOV_FROM_CONTENT1632], 0x66, 0x8b, "mov", read_instr_mov_from_content_1632); register_opcode(proc->opcodes[X86_OP_LEA], 0x66, 0x8d, "lea", read_instr_lea); register_opcode(proc->opcodes[X86_OP_NOP], 0x00, 0x90, "nop", read_instr_nop); register_opcode(proc->opcodes[X86_OP_MOV_E_AX], 0x66, 0xb8, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_CX], 0x66, 0xb9, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_DX], 0x66, 0xba, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_BX], 0x66, 0xbb, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_SP], 0x66, 0xbc, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_BP], 0x66, 0xbd, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_SI], 0x66, 0xbe, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_MOV_E_DI], 0x66, 0xbf, "mov", read_instr_mov_to_1632); register_opcode(proc->opcodes[X86_OP_RET], 0x00, 0xc3, "ret", read_instr_ret); register_opcode(proc->opcodes[X86_OP_MOV_TO_CONTENT1632], 0x66, 0xc7, "mov", read_instr_mov_to_content_1632); register_opcode(proc->opcodes[X86_OP_LEAVE], 0x00, 0xc9, "leave", read_instr_leave); register_opcode(proc->opcodes[X86_OP_INT], 0x00, 0xcd, "int", read_instr_int); register_opcode(proc->opcodes[X86_OP_CALL], 0x00, 0xe8, "call", read_instr_call); register_opcode(proc->opcodes[X86_OP_HLT], 0x00, 0xf4, "hlt", read_instr_hlt); register_opcode(proc->opcodes[X86_OP_PUSH_CONTENT], 0x66, 0xff, "push", read_instr_push_content); } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * len = taille totale des données à analyser. * * offset = adresse virtuelle de l'instruction. * * * * Description : Décode une instruction dans un flux de données. * * * * Retour : Instruction mise en place ou NULL. * * * * Remarques : - * * * ******************************************************************************/ asm_instr *x86_fetch_instruction(const asm_x86_processor *proc, const uint8_t *data, off_t *pos, off_t len, uint64_t offset) { asm_x86_instr *result; /* Résultat à faire remonter */ X86Opcodes i; /* Boucle de parcours */ off_t tmp; /* Tête de lecture */ off_t old_pos; /* Sauvegarde de la position */ result = NULL; for (i = 0; i < X86_OP_COUNT; i++) { if (proc->opcodes[i].prefix > 0 && data[*pos] == proc->opcodes[i].prefix && data[*pos + 1] == proc->opcodes[i].opcode) { tmp = *pos + 2; goto find_instr; } if (proc->opcodes[i].opt_prefix && data[*pos] == proc->opcodes[i].opcode) { tmp = *pos + 1; goto find_instr; } continue; find_instr: if (proc->opcodes[i].has_op_ext && (data[tmp] & EXT_OPCODE_MASK) != proc->opcodes[i].op_ext) continue; old_pos = *pos; result = proc->opcodes[i].read(data, pos, len, offset, proc); if (result != NULL) result->type = i; else { *pos = old_pos; printf("err while decoding at 0x%08llx :: [0x%02hhx] 0x%02hhx\n", offset, proc->opcodes[i].prefix, proc->opcodes[i].opcode); } break; } return ASM_INSTRUCTION(result); } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * instr = instruction à traiter. * * buffer = tampon de sortie mis à disposition. [OUT] * * len = taille de ce tampon. * * syntax = type de représentation demandée. * * * * Description : Traduit une instruction en version humainement lisible. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void x86_print_instruction(const asm_x86_processor *proc, const asm_x86_instr *instr, char *buffer, size_t len, AsmSyntax syntax) { size_t i; /* Boucle de parcours */ char opbuffer[3][64]; /* Tampon pour les textes */ /* Impression des opérandes */ for (i = 0; i < ASM_INSTRUCTION(instr)->operands_count; i++) switch (ASM_OPERAND(ASM_INSTRUCTION(instr)->operands[i])->type) { case AOT_NONE: print_db_operand(ASM_OPERAND(ASM_INSTRUCTION(instr)->operands[i]), opbuffer[i], 64, syntax); break; case AOT_IMM: print_imm_operand(ASM_OPERAND(ASM_INSTRUCTION(instr)->operands[i]), opbuffer[i], 64, syntax); break; case AOT_REG: x86_print_reg_operand(ASM_INSTRUCTION(instr)->operands[i], opbuffer[i], 64, syntax); break; case AOT_MEM: break; } /* Impression globale finale */ if (ASM_INSTRUCTION(instr)->opcode == DB_OPCODE) snprintf(buffer, len, "db\t%s", opbuffer[0]); else switch (ASM_INSTRUCTION(instr)->operands_count) { case 0: snprintf(buffer, len, "%s", proc->opcodes[instr->type].name); break; case 1: snprintf(buffer, len, "%s\t%s", proc->opcodes[instr->type].name, opbuffer[0]); break; case 2: switch (syntax) { case ASX_INTEL: snprintf(buffer, len, "%s\t%s, %s", proc->opcodes[instr->type].name, opbuffer[0], opbuffer[1]); break; case ASX_ATT: snprintf(buffer, len, "%s\t%s, %s", proc->opcodes[instr->type].name, opbuffer[1], opbuffer[0]); break; } break; } }