/* Chrysalide - Outil d'analyse de fichiers binaires
* class.c - manipulation des classes du format DEX
*
* Copyright (C) 2017-2020 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 "class.h"
#include
#include
#include "dex-int.h"
#include "pool.h"
/* Classe issue du code source (instance) */
struct _GDexClass
{
GObject parent; /* A laisser en premier */
GDexFormat *format; /* Lien vers la table globale */
class_def_item definition; /* Définition de la classe */
bool has_data; /* Indicateur de présence */
class_data_item data; /* Contenu de la classe */
GDexField **static_fields; /* Champs statiques */
size_t sfields_count; /* Quantité de ces champs */
GDexField **instance_fields; /* Champs propres à la classe */
size_t ifields_count; /* Quantité de ces champs */
GDexMethod **direct_methods; /* Méthodes propres */
size_t dmethods_count; /* Quantité de ces méthodes */
GDexMethod **virtual_methods; /* Méthodes virtuelles */
size_t vmethods_count; /* Quantité de ces méthodes */
};
/* Classe issue du code source (classe) */
struct _GDexClassClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Procède à l'initialisation d'une classe issue du code source. */
static void g_dex_class_class_init(GDexClassClass *);
/* Procède à l'initialisation d'une classe issue du code source. */
static void g_dex_class_init(GDexClass *);
/* Supprime toutes les références externes. */
static void g_dex_class_dispose(GDexClass *);
/* Procède à la libération totale de la mémoire. */
static void g_dex_class_finalize(GDexClass *);
/* Détermine le type d'une classe issue du code source. */
G_DEFINE_TYPE(GDexClass, g_dex_class, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GLib à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe issue du code source.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_dex_class_class_init(GDexClassClass *class)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)g_dex_class_dispose;
object->finalize = (GObjectFinalizeFunc)g_dex_class_finalize;
}
/******************************************************************************
* *
* Paramètres : class = composant GLib à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe issue du code source.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_dex_class_init(GDexClass *class)
{
}
/******************************************************************************
* *
* Paramètres : class = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_dex_class_dispose(GDexClass *class)
{
size_t i; /* Boucle de parcours */
if (class->static_fields != NULL)
for (i = 0; i < class->sfields_count; i++)
if (class->static_fields[i] != NULL)
g_object_unref(G_OBJECT(class->static_fields[i]));
if (class->instance_fields != NULL)
for (i = 0; i < class->ifields_count; i++)
if (class->instance_fields[i] != NULL)
g_object_unref(G_OBJECT(class->instance_fields[i]));
if (class->direct_methods != NULL)
for (i = 0; i < class->dmethods_count; i++)
if (class->direct_methods[i] != NULL)
g_object_unref(G_OBJECT(class->direct_methods[i]));
if (class->virtual_methods != NULL)
for (i = 0; i < class->vmethods_count; i++)
if (class->virtual_methods[i] != NULL)
g_object_unref(G_OBJECT(class->virtual_methods[i]));
g_object_unref(G_OBJECT(class->format));
G_OBJECT_CLASS(g_dex_class_parent_class)->dispose(G_OBJECT(class));
}
/******************************************************************************
* *
* Paramètres : class = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_dex_class_finalize(GDexClass *class)
{
if (class->has_data)
reset_dex_class_data_item(&class->data);
if (class->static_fields != NULL)
free(class->static_fields);
if (class->instance_fields != NULL)
free(class->instance_fields);
if (class->direct_methods != NULL)
free(class->direct_methods);
if (class->virtual_methods != NULL)
free(class->virtual_methods);
G_OBJECT_CLASS(g_dex_class_parent_class)->finalize(G_OBJECT(class));
}
/******************************************************************************
* *
* Paramètres : format = représentation interne du format DEX à consulter. *
* def = définitions générales associées à la classe. *
* *
* Description : Crée une nouvelle représentation de classe issue de code. *
* *
* Retour : Composant GLib créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GDexClass *g_dex_class_new(GDexFormat *format, const class_def_item *def)
{
GDexClass *result; /* Composant à retourner */
vmpa2t addr; /* Tête de lecture générique */
class_data_item data; /* Contenu de la classe */
uleb128_t index; /* Conservation du dernier id */
uleb128_t i; /* Boucle de parcours */
GDexField *field; /* Champ chargé */
GDexMethod *method; /* Méthode chargée */
result = g_object_new(G_TYPE_DEX_CLASS, NULL);
result->format = format;
g_object_ref(G_OBJECT(format));
result->definition = *def;
result->has_data = (def->class_data_off != 0);
/* Interface vide ? */
if (!result->has_data)
{
result->dmethods_count = 0;
result->direct_methods = NULL;
result->vmethods_count = 0;
result->virtual_methods = NULL;
goto gdcn_done;
}
init_vmpa(&addr, def->class_data_off, VMPA_NO_VIRTUAL);
if (!read_dex_class_data_item(format, &addr, &data))
goto gdcn_bad_item;
result->data = data;
/**
* Chargement des champs de classe.
*/
index = 0;
result->sfields_count = data.static_fields_size;
result->static_fields = (GDexField **)calloc(result->sfields_count, sizeof(GDexField *));
for (i = 0; i < result->sfields_count; i++)
{
field = g_dex_field_new(format, &data.static_fields[i], &index);
if (field == NULL) goto gdcn_bad_field;
result->static_fields[i] = field;
}
index = 0;
result->ifields_count = data.instance_fields_size;
result->instance_fields = (GDexField **)calloc(result->ifields_count, sizeof(GDexField *));
for (i = 0; i < result->ifields_count; i++)
{
field = g_dex_field_new(format, &data.instance_fields[i], &index);
if (field == NULL) goto gdcn_bad_field;
result->instance_fields[i] = field;
}
/**
* Chargement des méthodes de classe.
*/
index = 0;
result->dmethods_count = data.direct_methods_size;
result->direct_methods = (GDexMethod **)calloc(result->dmethods_count, sizeof(GDexMethod *));
for (i = 0; i < result->dmethods_count; i++)
{
method = g_dex_method_new_defined(format, &data.direct_methods[i], &index);
if (method == NULL) goto gdcn_bad_method;
result->direct_methods[i] = method;
}
index = 0;
result->vmethods_count = data.virtual_methods_size;
result->virtual_methods = (GDexMethod **)calloc(result->vmethods_count, sizeof(GDexMethod *));
for (i = 0; i < result->vmethods_count; i++)
{
method = g_dex_method_new_defined(format, &data.virtual_methods[i], &index);
if (method == NULL) goto gdcn_bad_method;
result->virtual_methods[i] = method;
}
gdcn_done:
return result;
gdcn_bad_method:
gdcn_bad_field:
gdcn_bad_item:
g_object_unref(G_OBJECT(result));
return NULL;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* *
* Description : Fournit la définition brute d'une classe. *
* *
* Retour : Données brutes issues du binaire chargé. *
* *
* Remarques : - *
* *
******************************************************************************/
const class_def_item *g_dex_class_get_definition(const GDexClass *class)
{
return &class->definition;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* *
* Description : Fournit la définition brute des données d'une classe. *
* *
* Retour : Données brutes issues du binaire chargé. *
* *
* Remarques : - *
* *
******************************************************************************/
const class_data_item *g_dex_class_get_data(const GDexClass *class)
{
return (class->has_data ? &class->data : NULL);
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* *
* Description : Indique le type Android d'une classe. *
* *
* Retour : Type de classe ou NULL en cas d'erreur. *
* *
* Remarques : - *
* *
******************************************************************************/
GDataType *g_dex_class_get_class_type(const GDexClass *class)
{
GDataType *result; /* Type à renvoyer */
GDexPool *pool; /* Table de ressources */
pool = g_dex_format_get_pool(class->format);
result = g_dex_pool_get_type_(pool, class->definition.class_idx);
g_object_unref(G_OBJECT(pool));
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* *
* Description : Indique le type Android parent d'une classe. *
* *
* Retour : Type de classe ou NULL en cas d'erreur. *
* *
* Remarques : - *
* *
******************************************************************************/
GDataType *g_dex_class_get_superclass_type(const GDexClass *class)
{
GDataType *result; /* Type à renvoyer */
GDexPool *pool; /* Table de ressources */
pool = g_dex_format_get_pool(class->format);
result = g_dex_pool_get_type_(pool, class->definition.superclass_idx);
g_object_unref(G_OBJECT(pool));
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* count = taille de la liste constituée. [OUT] *
* *
* Description : Indique le type Android des interfaces d'une classe. *
* *
* Retour : Types d'interfaces ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GDataType **g_dex_class_get_interface_types(const GDexClass *class, size_t *count)
{
GDataType **result; /* Types à renvoyer */
vmpa2t addr; /* Tête de lecture générique */
type_list interfaces; /* Liste des interfaces */
bool status; /* Bilan d'une lecture */
GDexPool *pool; /* Table de ressources */
size_t i; /* Boucle de parcours */
if (class->definition.interfaces_off == 0)
{
*count = 0;
result = NULL;
}
else
{
init_vmpa(&addr, class->definition.interfaces_off, VMPA_NO_VIRTUAL);
status = read_dex_type_list(class->format, &addr, &interfaces);
if (status)
{
*count = interfaces.size;
result = malloc(*count * sizeof(GDataType *));
pool = g_dex_format_get_pool(class->format);
for (i = 0; i < *count; i++)
result[i] = g_dex_pool_get_type_(pool, interfaces.list[i].type_idx);
g_object_unref(G_OBJECT(pool));
}
else
{
*count = 0;
result = NULL;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* instance = précise la nature des champs ciblés. *
* *
* Description : Dénombre les champs de classe chargés d'une classe donnée. *
* *
* Retour : Quantité de champs trouvés. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_dex_class_count_fields(const GDexClass *class, bool instance)
{
size_t result; /* Compte à retourner */
if (instance)
result = class->ifields_count;
else
result = class->sfields_count;
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* instance = précise la nature des champs ciblés. *
* index = indique l'indice du champ désiré. *
* *
* Description : Fournit un champ chargé correspondant à une classe donnée. *
* *
* Retour : Champ intégré ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
GDexField *g_dex_class_get_field(const GDexClass *class, bool instance, size_t index)
{
GDexField *result; /* Instance à renvoyer */
if (instance)
result = class->instance_fields[index];
else
result = class->static_fields[index];
if (result != NULL)
g_object_ref(G_OBJECT(result));
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* virtual = précise la nature des méthodes ciblées. *
* *
* Description : Dénombre les méthodes chargées d'un type donné. *
* *
* Retour : Quantité de méthodes trouvées. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_dex_class_count_methods(const GDexClass *class, bool virtual)
{
size_t result; /* Compte à retourner */
if (virtual)
result = class->vmethods_count;
else
result = class->dmethods_count;
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* virtual = précise la nature des méthodes ciblées. *
* index = indique l'indice de la méthode désirée. *
* *
* Description : Fournit une méthode chargée correspondant à un type donné. *
* *
* Retour : Méthode intégrée ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
GDexMethod *g_dex_class_get_method(const GDexClass *class, bool virtual, size_t index)
{
GDexMethod *result; /* Instance à renvoyer */
if (virtual)
{
if (index < class->vmethods_count)
result = class->virtual_methods[index];
else
result = NULL;
}
else
{
if (index < class->dmethods_count)
result = class->direct_methods[index];
else
result = NULL;
}
if (result != NULL)
g_object_ref(G_OBJECT(result));
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* symbols = liste de symboles complétée. [OUT] *
* count = taille de cette liste. [OUT] *
* *
* Description : Etablit une liste de tous les symboles d'une classe. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_dex_class_get_collect_symbols(const GDexClass *class, GBinSymbol ***symbols, size_t *count)
{
bool result; /* Bilan à retourner */
GDexPool *pool; /* Table de ressources */
GDataType *ctype; /* Type créé par la classe */
size_t slots_used; /* Compteur d'utilisations */
size_t i; /* Boucle de parcours */
GDexMethod *method; /* Méthode chargée */
GBinRoutine *routine; /* Version interne de méthode */
result = false;
/* Contexte des méthodes */
pool = g_dex_format_get_pool(class->format);
ctype = g_dex_pool_get_type_(pool, class->definition.class_idx);
g_object_unref(G_OBJECT(pool));
if (ctype == NULL) goto unknown_type;
/* Intégration des méthodes */
*symbols = realloc(*symbols, (*count + class->dmethods_count + class->vmethods_count) * sizeof(GBinSymbol *));
result = true;
slots_used = 0;
for (i = 0; i < class->dmethods_count; i++)
{
method = class->direct_methods[i];
if (g_dex_method_has_dex_body(method))
{
routine = g_dex_method_get_routine(method);
g_object_ref(G_OBJECT(ctype));
g_binary_routine_set_namespace(routine, ctype, strdup("."));
(*symbols)[*count + slots_used] = G_BIN_SYMBOL(routine);
slots_used++;
}
}
*count += slots_used;
slots_used = 0;
for (i = 0; i < class->vmethods_count; i++)
{
method = class->virtual_methods[i];
if (g_dex_method_has_dex_body(method))
{
routine = g_dex_method_get_routine(method);
g_object_ref(G_OBJECT(ctype));
g_binary_routine_set_namespace(routine, ctype, strdup("."));
(*symbols)[*count + slots_used] = G_BIN_SYMBOL(routine);
slots_used++;
}
}
*count += slots_used;
g_object_unref(G_OBJECT(ctype));
unknown_type:
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* format = format permettant d'obtenir une adresse complète. *
* *
* Description : Intègre la méthode en tant que portion de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_dex_class_include_as_portion(const GDexClass *class, GExeFormat *format)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < class->dmethods_count; i++)
g_dex_method_include_as_portion(class->direct_methods[i], format);
for (i = 0; i < class->vmethods_count; i++)
g_dex_method_include_as_portion(class->virtual_methods[i], format);
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* addr = adresse de la routine à retrouver. *
* *
* Description : Retrouve si possible la méthode associée à une adresse. *
* *
* Retour : Méthde retrouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GDexMethod *g_dex_class_find_method_by_address(const GDexClass *class, vmpa_t addr)
{
GDexMethod *result; /* Trouvaille à retourner */
#if 0
size_t i; /* Boucle de parcours */
phys_t offset; /* Emplacement de méthode */
#endif
result = NULL;
#if 0 /* FIXME */
/*
bool g_dex_method_get_offset(const GDexMethod *method, phys_t *offset)
if (!g_exe_format_translate_offset_into_vmpa(G_EXE_FORMAT(format), method->offset, &addr))
return;
*/
for (i = 0; i < class->dmethods_count && result == NULL; i++)
if (addr == (vmpa_t)g_dex_method_get_offset(class->direct_methods[i]))
result = class->direct_methods[i];
for (i = 0; i < class->vmethods_count && result == NULL; i++)
if (addr == (vmpa_t)g_dex_method_get_offset(class->virtual_methods[i]))
result = class->virtual_methods[i];
#endif
return result;
}
/******************************************************************************
* *
* Paramètres : class = informations chargées à consulter. *
* *
* Description : Retrouve si possible le nom du fichier source d'une classe. *
* *
* Retour : Nom du fichier trouvé ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_dex_class_get_source_file(const GDexClass *class)
{
const char *result; /* Trouvaille à renvoyer */
GDexPool *pool; /* Table de ressources */
pool = g_dex_format_get_pool(class->format);
result = g_dex_pool_get_string(pool, class->definition.source_file_idx, (bool []) { true }, NULL);
g_object_unref(G_OBJECT(pool));
return result;
}