/* Chrysalide - Outil d'analyse de fichiers binaires * gbincontent.c - prototypes pour le chargement de données binaires en mémoire * * Copyright (C) 2015 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 "gbincontent.h" #include #include #include #include #include #include #include #include #include "../common/endianness.h" /* Aire de contenu binaire */ typedef struct _binary_part { bin_t *data; /* Contenu binaire représenté */ mrange_t range; /* Couverture du binaire */ } binary_part; /* Content de données binaires quelconques (instance) */ struct _GBinContent { GObject parent; /* A laisser en premier */ binary_part *parts; /* Parties prises en compte */ size_t count; /* Nombre de ces parties */ GChecksum *checksum; /* Calcul de l'empreinte */ bool cs_computed; /* Calcul effectué ? */ }; /* Content de données binaires quelconques (classe) */ struct _GBinContentClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe des contenus de données binaires. */ static void g_binary_content_class_init(GBinContentClass *); /* Initialise une instance de contenu de données binaires. */ static void g_binary_content_init(GBinContent *); /* Supprime toutes les références externes. */ static void g_binary_content_dispose(GBinContent *); /* Procède à la libération totale de la mémoire. */ static void g_binary_content_finalize(GBinContent *); /* Retrouve la zone adaptée pour une localisation de données. */ static const binary_part *g_binary_content_find_part(const GBinContent *, const vmpa2t *, phys_t *); /* Indique le type défini par la GLib pour les contenus de données. */ G_DEFINE_TYPE(GBinContent, g_binary_content, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des contenus de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_content_class_init(GBinContentClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_content_dispose; object->finalize = (GObjectFinalizeFunc)g_binary_content_finalize; } /****************************************************************************** * * * Paramètres : content = instance à initialiser. * * * * Description : Initialise une instance de contenu de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_content_init(GBinContent *content) { content->checksum = g_checksum_new(G_CHECKSUM_SHA256); assert(content->checksum != NULL); content->cs_computed = false; } /****************************************************************************** * * * Paramètres : content = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_content_dispose(GBinContent *content) { g_checksum_free(content->checksum); G_OBJECT_CLASS(g_binary_content_parent_class)->dispose(G_OBJECT(content)); } /****************************************************************************** * * * Paramètres : content = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_content_finalize(GBinContent *content) { size_t i; /* Boucle de parcours */ for (i = 0; i < content->count; i++) free(content->parts[i].data); if (content->parts != NULL) free(content->parts); G_OBJECT_CLASS(g_binary_content_parent_class)->finalize(G_OBJECT(content)); } /****************************************************************************** * * * Paramètres : filename = chemin d'accès au fichier à charger. * * * * Description : Charge en mémoire le contenu d'un fichier donné. * * * * Retour : Représentation de contenu à manipuler ou NULL en cas d'échec.* * * * Remarques : - * * * ******************************************************************************/ GBinContent *g_binary_content_new_from_file(const char *filename) { GBinContent *result; /* Structure à retourner */ int fd; /* Descripteur du fichier */ struct stat info; /* Informations sur le fichier */ int ret; /* Bilan d'un appel */ void *content; /* Contenu brut du fichier */ vmpa2t base; /* Localisation des données */ /* Récupération des données */ fd = open(filename, O_RDONLY); if (fd == -1) { perror("open"); goto gbcnff_error; } ret = fstat(fd, &info); if (ret == -1) { close(fd); perror("fstat"); goto gbcnff_error; } content = mmap(NULL, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (content == MAP_FAILED) { close(fd); perror("mmap"); goto gbcnff_error; } /* Constitution du contenu officiel */ result = g_object_new(G_TYPE_BIN_CONTENT, NULL); result->parts = (binary_part *)calloc(1, sizeof(binary_part)); result->count = 1; result->parts[0].data = (bin_t *)malloc(info.st_size); memcpy(result->parts[0].data, content, info.st_size); munmap(content, info.st_size); close(fd); init_vmpa(&base, 0, VMPA_NO_VIRTUAL); init_mrange(&result->parts[0].range, &base, info.st_size); return result; gbcnff_error: return NULL; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * * * Description : Fournit une empreinte unique (SHA256) pour les données. * * * * Retour : Chaîne représentant l'empreinte du contenu binaire. * * * * Remarques : - * * * ******************************************************************************/ const gchar *g_binary_content_get_cheksum(GBinContent *content) { size_t i; /* Boucle de parcours */ binary_part *part; /* Bloc parcouru pour analyse */ if (!content->cs_computed) { g_checksum_reset(content->checksum); for (i = 0; i < content->count; i++) { part = &content->parts[i]; g_checksum_update(content->checksum, part->data, get_mrange_length(&part->range)); } content->cs_computed = true; } return g_checksum_get_string(content->checksum); } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * * * Description : Détermine le nombre d'octets lisibles. * * * * Retour : Quantité représentée. * * * * Remarques : - * * * ******************************************************************************/ phys_t g_binary_content_compute_size(const GBinContent *content) { phys_t result; /* Quantité trouvée à retourner*/ size_t i; /* Boucle de parcours */ result = 0; for (i = 0; i < content->count; i++) result = get_mrange_length(&content->parts[i].range); return result; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture globale demandée. * * start = position de la tête de lecture dans la zone. [OUT] * * * * Description : Retrouve la zone adaptée pour une localisation de données. * * * * Retour : Partie trouvée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ static const binary_part *g_binary_content_find_part(const GBinContent *content, const vmpa2t *addr, phys_t *start) { const binary_part *result; /* Trouvaille à retourner */ size_t i; /* Boucle de parcours */ binary_part *part; /* Zone mémoire manipulée */ result = NULL; for (i = 0; i < content->count && result == NULL; i++) { part = &content->parts[i]; if (mrange_contains_addr(&part->range, addr)) result = part; } if (result != NULL) *start = compute_vmpa_diff(get_mrange_addr(&result->range), addr); return result; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture. * * length = quantité d'octets à lire. * * out = réceptacle disponible pour ces données. [OUT] * * * * Description : Fournit une portion des données représentées. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_content_get_raw(const GBinContent *content, vmpa2t *addr, phys_t length, bin_t *out) { bool result; phys_t offset; /* FIXME */ offset = get_phy_addr(addr); memcpy(out, &content->parts[0].data[offset], length); advance_vmpa(addr, length); return true; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture. * * low = position éventuelle des 4 bits visés. [OUT] * * endian = ordre des bits dans la source. * * val = lieu d'enregistrement de la lecture. [OUT] * * * * Description : Lit un nombre non signé sur quatre bits. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_content_read_u4(const GBinContent *content, vmpa2t *addr, bool *low, SourceEndian endian, uint8_t *val) { phys_t start; /* Tête de lecture relative */ const binary_part *part; /* Zone de mémoire effective */ bin_t *data; /* Contenu binaire représenté */ part = g_binary_content_find_part(content, addr, &start); if (part == NULL) return false; if ((get_mrange_length(&part->range) - start) < 1) return false; data = part->data; if (*low) { *val = data[start] & 0x0f; *low = false; } else { *val = (data[start] & 0xf0) >> 4; *low = true; advance_vmpa(addr, 4); } return true; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture. * * endian = ordre des bits dans la source. * * val = lieu d'enregistrement de la lecture. [OUT] * * * * Description : Lit un nombre non signé sur un octet. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_content_read_u8(const GBinContent *content, vmpa2t *addr, SourceEndian endian, uint8_t *val) { phys_t start; /* Tête de lecture relative */ const binary_part *part; /* Zone de mémoire effective */ bin_t *data; /* Contenu binaire représenté */ part = g_binary_content_find_part(content, addr, &start); if (part == NULL) return false; if ((get_mrange_length(&part->range) - start) < 1) return false; data = part->data; *val = data[start]; advance_vmpa(addr, 1); return true; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture. * * endian = ordre des bits dans la source. * * val = lieu d'enregistrement de la lecture. [OUT] * * * * Description : Lit un nombre non signé sur deux octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_content_read_u16(const GBinContent *content, vmpa2t *addr, SourceEndian endian, uint16_t *val) { phys_t start; /* Tête de lecture relative */ const binary_part *part; /* Zone de mémoire effective */ bin_t *data; /* Contenu binaire représenté */ part = g_binary_content_find_part(content, addr, &start); if (part == NULL) return false; if ((get_mrange_length(&part->range) - start) < 2) return false; data = part->data; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *val = data[start] | (uint16_t)data[start + 1] << 8; #elif __BYTE_ORDER == __BIG_ENDIAN *val = data[start + 1] | (uint16_t)data[start] << 8; #else # error "TODO : extra byte order !" #endif break; case SRE_MIDDLE: assert(false); /* TODO */ break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *val = data[start + 1] | (uint16_t)data[start] << 8; #elif __BYTE_ORDER == __BIG_ENDIAN *val = data[start] | (uint16_t)data[start + 1] << 8; #else # error "TODO : extra byte order !" #endif break; } advance_vmpa(addr, 2); return true; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture. * * endian = ordre des bits dans la source. * * val = lieu d'enregistrement de la lecture. [OUT] * * * * Description : Lit un nombre non signé sur quatre octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_content_read_u32(const GBinContent *content, vmpa2t *addr, SourceEndian endian, uint32_t *val) { phys_t start; /* Tête de lecture relative */ const binary_part *part; /* Zone de mémoire effective */ bin_t *data; /* Contenu binaire représenté */ part = g_binary_content_find_part(content, addr, &start); if (part == NULL) return false; if ((get_mrange_length(&part->range) - start) < 4) return false; data = part->data; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *val = data[start] | (uint32_t)data[start + 1] << 8; *val |= data[start + 2] << 16 | (uint32_t)data[start + 3] << 24; #elif __BYTE_ORDER == __BIG_ENDIAN *val = data[start + 3] | (uint32_t)data[start + 2] << 8; *val |= data[start + 1] << 16 | (uint32_t)data[start] << 24; #else # error "TODO : extra byte order !" #endif break; case SRE_MIDDLE: assert(false); /* TODO */ break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *val = data[start + 3] | (uint32_t)data[start + 2] << 8; *val |= data[start + 1] << 16 | (uint32_t)data[start] << 24; #elif __BYTE_ORDER == __BIG_ENDIAN *val = data[start] | (uint32_t)data[start + 1] << 8; *val |= data[start + 2] << 16 | (uint32_t)data[start + 3] << 24; #else # error "TODO : extra byte order !" #endif break; } advance_vmpa(addr, 4); return true; } /****************************************************************************** * * * Paramètres : content = contenu binaire à venir lire. * * addr = position de la tête de lecture. * * endian = ordre des bits dans la source. * * val = lieu d'enregistrement de la lecture. [OUT] * * * * Description : Lit un nombre non signé sur huit octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_content_read_u64(const GBinContent *content, vmpa2t *addr, SourceEndian endian, uint64_t *val) { phys_t start; /* Tête de lecture relative */ const binary_part *part; /* Zone de mémoire effective */ bin_t *data; /* Contenu binaire représenté */ part = g_binary_content_find_part(content, addr, &start); if (part == NULL) return false; if ((get_mrange_length(&part->range) - start) < 8) return false; data = part->data; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *val = (uint64_t)data[start] | (uint64_t)data[start + 1] << 8; *val |= (uint64_t)data[start + 2] << 16 | (uint64_t)data[start + 3] << 24; *val |= (uint64_t)data[start + 4] << 32 | (uint64_t)data[start + 5] << 40; *val |= (uint64_t)data[start + 6] << 48 | (uint64_t)data[start + 7] << 56; #elif __BYTE_ORDER == __BIG_ENDIAN *val = (uint64_t)data[start + 7] | (uint64_t)data[start + 6] << 8; *val |= (uint64_t)data[start + 5] << 16 | (uint64_t)data[start + 4] << 24; *val |= (uint64_t)data[start + 3] << 32 | (uint64_t)data[start + 2] << 40; *val |= (uint64_t)data[start + 1] << 48 | (uint64_t)data[start] << 56; #else # error "TODO : extra byte order !" #endif break; case SRE_MIDDLE: assert(false); /* TODO */ break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *val = (uint64_t)data[start + 7] | (uint64_t)data[start + 6] << 8; *val |= (uint64_t)data[start + 5] << 16 | (uint64_t)data[start + 4] << 24; *val |= (uint64_t)data[start + 3] << 32 | (uint64_t)data[start + 2] << 40; *val |= (uint64_t)data[start + 1] << 48 | (uint64_t)data[start] << 56; #elif __BYTE_ORDER == __BIG_ENDIAN *val = (uint64_t)data[start] | (uint64_t)data[start + 1] << 8; *val |= (uint64_t)data[start + 2] << 16 | (uint64_t)data[start + 3] << 24; *val |= (uint64_t)data[start + 4] << 32 | (uint64_t)data[start + 5] << 40; *val |= (uint64_t)data[start + 6] << 48 | (uint64_t)data[start + 7] << 56; #else # error "TODO : extra byte order !" #endif break; } advance_vmpa(addr, 8); return true; } const bin_t *g_binary_content_get(GBinContent *content, off_t *length) { *length = content->parts[0].range.length; return content->parts[0].data; }