/* Chrysalide - Outil d'analyse de fichiers binaires * coder.c - lecture automatisée des spécifications d'architecture * * Copyright (C) 2014 Cyrille Bagard * * This file is part of Chrysalide. * * 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 "coder.h" #include #include #include #include #include #include #include #include #include "helpers.h" /* -------------------------- CONSTRUCTION SELON COMMANDES -------------------------- */ /* Suivi des constructions */ struct _rented_coder { const char *outdir; /* Lieu d'enregistrement */ const char *arch; /* Architecture à traiter */ const char *header; /* En-tête pour les en-têtes */ pre_processor *pp; /* Pré-processeur avec macros */ char *copyright; /* Récupération des droits */ char *ins; /* Désignation humaine */ char *details; /* Eventuels compléments */ encoding_spec **specs; /* Définitions déjà en place */ size_t specs_count; /* Nombre de ces définitions */ encoding_spec *cur_spec; /* Définition courante */ }; /* --------------------------- REPRESENTATION D'ENCODAGES --------------------------- */ /* --------------------------- GESTION DES CHAMPS DE BITS --------------------------- */ /* Elément d'un mot décodé */ typedef struct _dec_bitfield { char *name; /* Désignation humaine */ unsigned int start; /* Position de départ */ unsigned int length; /* Taille du champ */ } dec_bitfield; /* ---------------------------- CONVERSION DES ARGUMENTS ---------------------------- */ /* Fonction de conversion */ typedef struct _conv_func { char *dest; /* Variable de destination */ char *func; /* Fonction de conversion */ char *arg; /* Argument de cette fonction */ } conv_func; /* --------------------------- GENERATIONS DE CODE SOURCE --------------------------- */ /* Ouvre un fichier en écriture pour y placer du code. */ static int create_code_file(const rented_coder *, const char *, const char *, const char *, char, bool *); /* Ecrit une partie des fonctions issues des spécifications. */ static bool dump_all_matching_specs_in_coder(const rented_coder *, const string_exch *, int, int); /* ---------------------------------------------------------------------------------- */ /* CONSTRUCTION SELON COMMANDES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : - * * * * Description : Débute la définition d'une fonction de désassemblage. * * * * Retour : Gestionnaire mis en place. * * * * Remarques : - * * * ******************************************************************************/ rented_coder *create_coder(void) { rented_coder *result; /* Structure à renvoyer */ result = (rented_coder *)calloc(1, sizeof(rented_coder)); result->pp = create_pre_processor(); result->cur_spec = create_encoding_spec(); return result; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * * * Description : Supprime le codeur de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void delete_coder(rented_coder *coder) { size_t i; /* Boucle de parcours */ delete_pre_processor(coder->pp); if (coder->ins != NULL) free(coder->ins); if (coder->details != NULL) free(coder->details); for (i = 0; i < coder->specs_count; i++) delete_encoding_spec(coder->specs[i]); if (coder->specs != NULL) free(coder->specs); delete_encoding_spec(coder->cur_spec); free(coder); } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * * * Description : Détermine si les propriétés de base d'un codeur sont là. * * * * Retour : Bilan de l'état opérationnel. * * * * Remarques : - * * * ******************************************************************************/ bool do_basic_checks_with_coder(const rented_coder *coder) { return (coder->outdir != NULL && coder->arch != NULL && coder->header != NULL); } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * outdir = répertoire de génération des fichiers. * * * * Description : Spécifie le répertoire de base pour les sorties de code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void set_coder_output_directory(rented_coder *coder, const char *outdir) { coder->outdir = outdir; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * arch = désignation pour le code de l'architecture lue. * * * * Description : Détermine l'architecture visée par les traitements. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void set_coder_arch(rented_coder *coder, const char *arch) { coder->arch = arch; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * header = base des définitions de protection d'en-têtes. * * * * Description : Définit la base des protections des fichiers d'en-tête. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void set_coder_header_base(rented_coder *coder, const char *header) { coder->header = header; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * * * Description : Fournit le pré-processeur du compilateur. * * * * Retour : Pré-processeur à manipuler. * * * * Remarques : - * * * ******************************************************************************/ pre_processor *get_coder_pre_proc(const rented_coder *coder) { return coder->pp; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain.* * copy = droits de copie en anglais. * * ins = désignation humaine de l'instruction. * * details = compléments d'informations éventuels ou NULL. * * * * Description : Enregistre les contours d'une instruction d'assemblage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void save_notes_for_coder(rented_coder *coder, char *copy, char *ins, const char *details) { coder->copyright = copy; coder->ins = make_string_lower(ins); coder->details = (details != NULL ? make_callable(details, true) : strdup("")); } /* ---------------------------------------------------------------------------------- */ /* REPRESENTATION D'ENCODAGES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * * * Description : Fournit un lien vers les spécifications courantes. * * * * Retour : Spécification en cours d'édition. * * * * Remarques : - * * * ******************************************************************************/ encoding_spec *get_current_encoding_spec(const rented_coder *coder) { return coder->cur_spec; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * prefix = distinction principale entre les définitions. * * index = distinction secondaire entre les définitions. * * * * Description : Enregistre une définition supplémentaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void push_encoding_spec(rented_coder *coder, char *prefix, unsigned int index) { encoding_spec *spec; /* Définition à compléter */ spec = coder->cur_spec; define_encoding_spec_code_name(spec, prefix, index); coder->specs = (encoding_spec **)realloc(coder->specs, ++coder->specs_count * sizeof(encoding_spec *)); coder->specs[coder->specs_count - 1] = spec; coder->cur_spec = create_encoding_spec(); } /* ---------------------------------------------------------------------------------- */ /* GENERATIONS DE CODE SOURCE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain. * * dir = répertoire final de destination. * * prefix = type d'encodage à répercuter sur le nom de fichier. * * name = nom brut du fichier à ouvrir. * * ext = extension à donner au fichier à ouvrir. * * exist = indique si le fichier était présent avant. [OUT] * * * * Description : Ouvre un fichier en écriture pour y placer du code. * * * * Retour : Descripteur du fichier ouvert ou -1 en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ static int create_code_file(const rented_coder *coder, const char *dir, const char *prefix, const char *name, char ext, bool *exist) { int result; /* Descripteur à retourner */ size_t length; /* Taille du nom de fichier */ char *pathname; /* Chemin d'accès à constituer */ length = strlen(coder->outdir) + 1 + strlen(dir) + 1 + strlen(prefix) + strlen(name) + 3; pathname = (char *)calloc(length, sizeof(char)); snprintf(pathname, length, "%s/%s/%s%s.%c", coder->outdir, dir, prefix, name, ext); *exist = (access(pathname, W_OK) == 0); result = open(pathname, O_WRONLY | O_CREAT | O_APPEND, 0644); if (result == -1) perror("open()"); free(pathname); if (!*exist && result != -1) { dprintf(result, "\n"); dprintf(result, "/* Chrysalide - Outil d'analyse de fichiers binaires\n"); dprintf(result, " * %s%s.%c - traduction d'instructions ARMv7\n", prefix, name, ext); dprintf(result, " *\n"); dprintf(result, " * %s\n", coder->copyright); dprintf(result, " *\n"); dprintf(result, " * This file is part of Chrysalide.\n"); dprintf(result, " *\n"); dprintf(result, " * Chrysalide is free software; you can redistribute it and/or modify\n"); dprintf(result, " * it under the terms of the GNU General Public License as published by\n"); dprintf(result, " * the Free Software Foundation; either version 3 of the License, or\n"); dprintf(result, " * (at your option) any later version.\n"); dprintf(result, " *\n"); dprintf(result, " * Chrysalide is distributed in the hope that it will be useful,\n"); dprintf(result, " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); dprintf(result, " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); dprintf(result, " * GNU General Public License for more details.\n"); dprintf(result, " *\n"); dprintf(result, " * You should have received a copy of the GNU General Public License\n"); dprintf(result, " * along with Foobar. If not, see .\n"); dprintf(result, " */\n"); dprintf(result, "\n"); dprintf(result, "\n"); } return result; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement de l'humain.* * * * Description : Débute la définition des fonctions issues des spécifications.* * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool dump_all_routines_using_coder(const rented_coder *coder) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ const string_exch *encoding; /* Type d'encodage visé */ bool exist; /* Présence du fichier visé ? */ int header_fd; /* Fichier de déclarations */ char *uname; /* Nom en majuscule */ char *dash; /* Présence d'un tiret ? */ char *filename; /* Nom de fichier commun */ int code_fd; /* Fichier de définitions */ result = true; for (i = 0; i < count_encodings(coder->pp) && result; i++) { encoding = find_encoding(coder->pp, i); /* Fichier de déclarations */ header_fd = create_code_file(coder, "opcodes", encoding->dest, "opcodes", 'h', &exist); if (header_fd == -1) return false; if (!exist) { uname = make_string_upper(strdup(encoding->dest)); dprintf(header_fd, "#ifndef %s_%sOPCODES_OPCODES_H\n", coder->header, uname); dprintf(header_fd, "#define %s_%sOPCODES_OPCODES_H\n", coder->header, uname); free(uname); dprintf(header_fd, "\n"); dprintf(header_fd, "\n"); dprintf(header_fd, "##INCLUDES##\n"); dprintf(header_fd, "\n"); dprintf(header_fd, "\n"); dprintf(header_fd, "\n"); } /* Fichier de définitions */ dash = strchr(coder->ins, '-'); if (dash == NULL) code_fd = create_code_file(coder, "opcodes", encoding->dest, coder->ins, 'c', &exist); else { filename = strdup(coder->ins); dash = strchr(filename, '-'); *dash = '\0'; code_fd = create_code_file(coder, "opcodes", encoding->dest, filename, 'c', &exist); free(filename); } if (!exist) { dprintf(code_fd, "#include \"%sopcodes.h\"\n", encoding->dest); dprintf(code_fd, "\n"); dprintf(code_fd, "##INCLUDES##\n"); } if (code_fd == -1) { close(header_fd); return false; } /* Production de code... */ result = dump_all_matching_specs_in_coder(coder, encoding, header_fd, code_fd); close(header_fd); close(code_fd); } return result; } /****************************************************************************** * * * Paramètres : coder = gestion par la machine en remplacement d'humain. * * encoding = sélection de l'encodage à traiter. * * hfd = flux ouvert en écriture pour les déclarations. * * cfd = flux ouvert en écriture pour les définitions. * * * * Description : Ecrit une partie des fonctions issues des spécifications. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool dump_all_matching_specs_in_coder(const rented_coder *coder, const string_exch *encoding, int hfd, int cfd) { bool result; /* Bilan à retourner */ char *keyword; /* Mot clef appelable en code */ unsigned int wide; /* Taille des mots */ size_t i; /* Boucle de parcours */ encoding_spec *spec; /* Définition à traiter */ coding_bits *bits; /* Gestionnaire de bits */ size_t maxlen; /* Taille à compléter */ result = true; keyword = make_callable(coder->ins, false); /* Recherche de la taille des mots */ wide = -1; for (i = 0; i < coder->specs_count; i++) { spec = coder->specs[i]; if (!has_encoding_spec_prefix(spec, encoding->src)) continue; bits = get_bits_in_encoding_spec(spec); wide = count_coded_bits(bits); break; } /* Rien n'a été trouvé à faire... */ if (wide == -1) goto damsic_exit; /* Désassemblage : déclaration */ dprintf(hfd, "/* Décode une instruction de type '%s'. */\n", coder->ins); dprintf(hfd, "GArchInstruction *%s_read_%sinstr_%s%s(uint%u_t);\n", coder->arch, encoding->dest, keyword, coder->details, wide); dprintf(hfd, "\n"); /* Désassemblage : définitions */ dprintf(cfd, "\n"); dprintf(cfd, "/******************************************************************************\n"); dprintf(cfd, "* *\n"); dprintf(cfd, "* Paramètres : raw = données brutes à analyser. *\n"); dprintf(cfd, "* *\n"); dprintf(cfd, "* Description : Décode une instruction de type '%s'.", coder->ins); maxlen = 28 - strlen(coder->ins); if (maxlen < 28) dprintf(cfd, "%*s\n", (int)maxlen, "*"); else dprintf(cfd, "*\n"); dprintf(cfd, " *\n"); dprintf(cfd, "* Retour : Bilan de l'opération. *\n"); dprintf(cfd, "* *\n"); dprintf(cfd, "* Remarques : - *\n"); dprintf(cfd, "* *\n"); dprintf(cfd, "******************************************************************************/\n"); dprintf(cfd, "\n"); dprintf(cfd, "GArchInstruction *%s_read_%sinstr_%s%s(uint%u_t raw)", coder->arch, encoding->dest, keyword, coder->details, wide); dprintf(cfd, "\n"); dprintf(cfd, "{"); dprintf(cfd, "\n"); dprintf(cfd, "\tGArchInstruction *result; /* Instruction créée à renvoyer*/\n"); dprintf(cfd, "\n"); dprintf(cfd, "\tresult = NULL;\n"); dprintf(cfd, "\n"); for (i = 0; i < coder->specs_count && result; i++) { spec = coder->specs[i]; if (!has_encoding_spec_prefix(spec, encoding->src)) continue; result = write_encoding_spec_disass(spec, cfd, coder->arch, encoding->dest, coder->ins, coder->details, wide, coder->pp); } dprintf(cfd, "\treturn result;\n"); dprintf(cfd, "\n"); dprintf(cfd, "}\n"); dprintf(cfd, "\n"); damsic_exit: free(keyword); return result; }