/* 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. *
* *
* Description : Donne accès à une portion des données représentées. *
* *
* Retour : Pointeur vers les données à lire ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
const bin_t *g_binary_content_get_raw_access(const GBinContent *content, vmpa2t *addr, phys_t length)
{
phys_t offset; /* Emplacement de départ */
/* FIXME */
offset = get_phy_addr(addr);
if ((offset + length) >= get_mrange_length(&content->parts[0].range))
return NULL;
advance_vmpa(addr, length);
return &content->parts[0].data[offset];
}
/******************************************************************************
* *
* 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; /* Bilan à remonter */
const bin_t *data; /* Pointeur vers données utiles*/
data = g_binary_content_get_raw_access(content, addr, length);
if (data != NULL)
{
result = true;
memcpy(out, data, length);
}
else
result = false;
return result;
}
/******************************************************************************
* *
* 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. *
* 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, 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;
}