/* Chrysalide - Outil d'analyse de fichiers binaires * pool.c - extraction des informations issues des tables globales * * Copyright (C) 2010-2017 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 "pool.h" #include #include #include #include #include #include #include #include "dex-int.h" #include "loading.h" /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * gid = groupe de travail impliqué. * status = barre de statut à tenir informée. * * * * Description : Charge en mémoire l'ensemble des chaînes du format DEX. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool load_all_dex_string_symbols(GDexFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ uint32_t count; /* Nombre d'éléments présents */ guint runs_count; /* Qté d'exécutions parallèles */ uint32_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t msg; /* Message de progression */ guint i; /* Boucle de parcours */ uint32_t begin; /* Début de bloc de traitement */ uint32_t end; /* Fin d'un bloc de traitement */ GDexLoading *loading; /* Tâche de chargement à lancer*/ result = true; /* Préparation du réceptacle */ count = count_strings_in_dex_pool(format); format->strings = (GBinSymbol **)calloc(count, sizeof(GBinSymbol *)); /* Lancement des chargements */ runs_count = get_max_online_threads(); run_size = count / runs_count; queue = get_work_queue(); msg = gtk_status_stack_add_activity(status, _("Loading all strings from the Dex pool..."), count); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = count; else end = begin + run_size; loading = g_dex_loading_new(format, begin, end, msg, (dex_loading_cb)get_string_symbol_from_dex_pool, &result); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loading), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * * * Description : Compte le nombre de chaînes de caractères dans une table DEX.* * * * Retour : Valeur positive ou nulle. * * * * Remarques : - * * * ******************************************************************************/ uint32_t count_strings_in_dex_pool(const GDexFormat *format) { uint32_t result; /* Quantité à retourner */ result = format->header.string_ids_size; return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index de la chaîne recherchée. * * range = éventuelle couverture à renseigner ou NULL. [OUT] * * * * Description : Extrait une chaîne de caractères d'une table DEX. * * * * Retour : Chaîne de caractères trouvée ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ const char *get_string_from_dex_pool(const GDexFormat *format, uint32_t index, mrange_t *range) { uint32_t count; /* Nombre d'éléments présents */ off_t pos; /* Tête de lecture */ vmpa2t addr; /* Tête de lecture générique */ string_id_item str_id; /* Identifiant de chaîne */ vmpa2t inter; /* Position intermédiaire */ string_data_item str_data; /* Description de chaîne */ phys_t diff; /* Avancée de tête de lecture */ count = count_strings_in_dex_pool(format); if (index >= count) return NULL; pos = format->header.string_ids_off + index * sizeof(string_id_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_string_id_item(format, &addr, &str_id)) return NULL; pos = str_id.string_data_off; init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_string_data_item(format, &addr, &inter, &str_data)) return NULL; if (range != NULL) { diff = compute_vmpa_diff(&inter, &addr); init_mrange(range, &inter, diff); } return (const char *)str_data.data; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index de la chaîne recherchée. * * * * Description : Extrait un symbole de chaîne d'une table DEX. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GBinSymbol *get_string_symbol_from_dex_pool(GDexFormat *format, uint32_t index) { GBinSymbol *result; /* Instance à retourner */ uint32_t count; /* Nombre d'éléments présents */ mrange_t range; /* Emplacement de la chaîne */ const char *string; /* Chaîne de caractères liée */ GBinFormat *base; /* Autre version du format */ GBinSymbol *new; /* Nouveau symbol créé */ bool inserted; /* Bilan d'une insertion */ result = NULL; count = count_strings_in_dex_pool(format); if (index >= count) goto gssfdp_error; if (format->strings[index] == NULL) { string = get_string_from_dex_pool(format, index, &range); if (string == NULL) goto gssfdp_error; base = G_BIN_FORMAT(format); new = g_string_symbol_new_read_only(base, &range, SET_MUTF_8); g_string_symbol_build_label(G_STR_SYMBOL(new), base); g_object_ref(G_OBJECT(new)); inserted = g_binary_format_add_symbol(base, new); if (inserted) format->strings[index] = new; else g_object_unref(G_OBJECT(new)); } result = format->strings[index]; if (result != NULL) g_object_ref(G_OBJECT(result)); gssfdp_error: return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * gid = groupe de travail impliqué. * status = barre de statut à tenir informée. * * * * Description : Charge en mémoire l'ensemble des types du format DEX. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool load_all_dex_types(GDexFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ uint32_t count; /* Nombre d'éléments présents */ guint runs_count; /* Qté d'exécutions parallèles */ uint32_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t msg; /* Message de progression */ guint i; /* Boucle de parcours */ uint32_t begin; /* Début de bloc de traitement */ uint32_t end; /* Fin d'un bloc de traitement */ GDexLoading *loading; /* Tâche de chargement à lancer*/ result = true; /* Préparation du réceptacle */ count = count_types_in_dex_pool(format); format->types = (GDataType **)calloc(count, sizeof(GDataType *)); /* Lancement des chargements */ runs_count = get_max_online_threads(); run_size = count / runs_count; queue = get_work_queue(); msg = gtk_status_stack_add_activity(status, _("Loading all types from the Dex pool..."), count); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = count; else end = begin + run_size; loading = g_dex_loading_new(format, begin, end, msg, (dex_loading_cb)get_type_from_dex_pool, &result); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loading), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * * * Description : Compte le nombre de types dans une table DEX. * * * * Retour : Valeur positive ou nulle. * * * * Remarques : - * * * ******************************************************************************/ uint32_t count_types_in_dex_pool(const GDexFormat *format) { uint32_t result; /* Quantité à retourner */ result = format->header.type_ids_size; return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index du type recherché. * * * * Description : Extrait une représentation de type d'une table DEX. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GDataType *get_type_from_dex_pool(GDexFormat *format, uint32_t index) { GDataType *result; /* Instance à retourner */ uint32_t count; /* Nombre d'éléments présents */ phys_t pos; /* Tête de lecture */ vmpa2t addr; /* Tête de lecture générique */ type_id_item type_id; /* Définition de la classe */ string_id_item str_id; /* Identifiant de chaîne */ string_data_item str_data; /* Description de chaîne */ result = NULL; count = count_types_in_dex_pool(format); if (index >= count) goto gtfdp_error; if (format->types[index] == NULL) { pos = format->header.type_ids_off + index * sizeof(type_id_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_type_id_item(format, &addr, &type_id)) goto gtfdp_error; pos = format->header.string_ids_off + type_id.descriptor_idx * sizeof(string_id_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_string_id_item(format, &addr, &str_id)) goto gtfdp_error; pos = str_id.string_data_off; init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_string_data_item(format, &addr, NULL, &str_data)) goto gtfdp_error; format->types[index] = g_binary_format_decode_type(G_BIN_FORMAT(format), (char *)str_data.data); } result = format->types[index]; if (result != NULL) g_object_ref(G_OBJECT(result)); gtfdp_error: return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * gid = groupe de travail impliqué. * * status = barre de statut à tenir informée. * * * * Description : Charge en mémoire l'ensemble des champs du format DEX. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool load_all_dex_fields(GDexFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ uint32_t count; /* Nombre d'éléments présents */ guint runs_count; /* Qté d'exécutions parallèles */ uint32_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t msg; /* Message de progression */ guint i; /* Boucle de parcours */ uint32_t begin; /* Début de bloc de traitement */ uint32_t end; /* Fin d'un bloc de traitement */ GDexLoading *loading; /* Tâche de chargement à lancer*/ result = true; /* Préparation du réceptacle */ count = count_fields_in_dex_pool(format); format->fields = (GBinVariable **)calloc(count, sizeof(GBinVariable *)); /* Lancement des chargements */ runs_count = get_max_online_threads(); run_size = count / runs_count; queue = get_work_queue(); msg = gtk_status_stack_add_activity(status, _("Loading all fields from the Dex pool..."), count); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = count; else end = begin + run_size; loading = g_dex_loading_new(format, begin, end, msg, (dex_loading_cb)get_field_from_dex_pool, &result); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loading), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * * * Description : Compte le nombre de champs dans une table DEX. * * * * Retour : Valeur positive ou nulle. * * * * Remarques : - * * * ******************************************************************************/ uint32_t count_fields_in_dex_pool(const GDexFormat *format) { uint32_t result; /* Quantité à retourner */ result = format->header.field_ids_size; return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index du champ recherché. * * * * Description : Extrait une représentation de champ d'une table DEX. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GBinVariable *get_field_from_dex_pool(GDexFormat *format, uint32_t index) { GBinVariable *result; /* Instance à retourner */ uint32_t count; /* Nombre d'éléments présents */ phys_t pos; /* Tête de lecture */ vmpa2t addr; /* Tête de lecture générique */ field_id_item field_id; /* Description du champ */ GDataType *type; /* Type du champ */ const char *name; /* Désignation humaine */ GBinVariable *field; /* Instance nouvelle à définir */ GDataType *owner; /* Propriétaire du champ */ result = NULL; count = count_fields_in_dex_pool(format); if (index >= count) goto gffdp_error; if (format->fields[index] == NULL) { pos = format->header.field_ids_off + index * sizeof(field_id_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_field_id_item(format, &addr, &field_id)) goto gffdp_error; type = get_type_from_dex_pool(format, field_id.type_idx); if (type == NULL) goto gffdp_error; name = get_string_from_dex_pool(format, field_id.name_idx, NULL); if (name == NULL) goto gffdp_bad_name; field = g_binary_variable_new(type); g_binary_variable_set_name(field, name); if (field_id.class_idx != NO_INDEX) { owner = get_type_from_dex_pool(format, field_id.class_idx); if (owner == NULL) goto gffdp_bad_owner; g_binary_variable_set_owner(field, owner); } format->fields[index] = field; } result = format->fields[index]; if (result != NULL) g_object_ref(G_OBJECT(result)); gffdp_error: return result; gffdp_bad_owner: g_object_unref(G_OBJECT(field)); gffdp_bad_name: g_object_unref(G_OBJECT(type)); return NULL; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * * * Description : Compte le nombre de prototypes dans une table DEX. * * * * Retour : Valeur positive ou nulle. * * * * Remarques : - * * * ******************************************************************************/ uint32_t count_prototypes_in_dex_pool(const GDexFormat *format) { uint32_t result; /* Quantité à retourner */ result = format->header.proto_ids_size; return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index de la routine recherchée. * * * * Description : Extrait une représentation de routine d'une table DEX. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GBinRoutine *get_prototype_from_dex_pool(GDexFormat *format, uint32_t index) { GBinRoutine *result; /* Instance à retourner */ uint32_t count; /* Nombre d'éléments présents */ phys_t pos; /* Tête de lecture */ vmpa2t addr; /* Tête de lecture générique */ proto_id_item proto_id; /* Prototype de routine */ GDataType *type; /* Type de retour */ type_list args; /* Liste des arguments */ uint32_t i; /* Boucle de parcours */ GBinVariable *arg; /* Argument reconstitué */ result = NULL; /** * Les prototypes sont personnalisés après chargement. * Donc on ne peut pas conserver de version globale comme pour * les autres éléments de la table des constantes. */ count = count_prototypes_in_dex_pool(format); if (index >= count) goto grfdp_error; pos = format->header.proto_ids_off + index * sizeof(proto_id_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_proto_id_item(format, &addr, &proto_id)) goto grfdp_error; /** * On choisit d'ignore le champ proto_id.shorty_idx : c'est un descripteur * qui doit correspondre au retour et aux paramètres précisés avec les * autres champs de la structure, donc l'information paraît redondante. */ /* Type de retour */ type = get_type_from_dex_pool(format, proto_id.return_type_idx); if (type == NULL) goto grfdp_error; result = g_binary_routine_new(); g_binary_routine_set_return_type(result, type); /* Liste des arguments, s'il y a */ pos = proto_id.parameters_off; if (pos > 0) { init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_type_list(format, &addr, &args)) goto grfdp_error; for (i = 0; i < args.size; i++) { type = get_type_from_dex_pool(format, args.list[i].type_idx); if (type == NULL) goto grfdp_error; arg = g_binary_variable_new(type); g_binary_routine_add_arg(result, arg); } } return result; grfdp_error: if (result != NULL) g_object_unref(G_OBJECT(result)); return NULL; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à compléter. * * gid = groupe de travail impliqué. * status = barre de statut à tenir informée. * * * * Description : Charge toutes les classes listées dans le contenu binaire. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool load_all_dex_methods(GDexFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ guint runs_count; /* Qté d'exécutions parallèles */ uint32_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t msg; /* Message de progression */ guint i; /* Boucle de parcours */ uint32_t begin; /* Début de bloc de traitement */ uint32_t end; /* Fin d'un bloc de traitement */ GDexLoading *loading; /* Tâche de chargement à lancer*/ /** * Il existe deux voies pour récupérer une méthode : * * - depuis 'method_id_item', qui précise classe d'appartenance, prototype * et nom. * * - depuis 'encoded_method', qui contient une définition 'method_id_item', * ainsi que des attributs propres à la méthode visée. * * Techniquement, il peut donc y avoir plusieurs variations d'un même * 'method_id_item' selon différents 'encoded_method'. * * Dans la pratique, c'est hautement improbable : une méthode ne peut pas * être privée et publique par exemple, ou renvoyer vers différents code. * * Donc on se permet d'associer une unique méthode par 'method_id_item', * et de précharger le tout. */ result = true; /* Préparation du réceptacle */ format->methods = (GDexMethod **)calloc(format->header.method_ids_size, sizeof(GDexMethod *)); /* Lancement des chargements */ runs_count = get_max_online_threads(); run_size = format->header.method_ids_size / runs_count; queue = get_work_queue(); msg = gtk_status_stack_add_activity(status, _("Loading all methods from the Dex pool..."), format->header.method_ids_size); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = format->header.method_ids_size; else end = begin + run_size; loading = g_dex_loading_new(format, begin, end, msg, (dex_loading_cb)get_method_from_dex_pool, &result); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loading), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * * * Description : Compte le nombre de méthodes dans une table DEX. * * * * Retour : Valeur positive ou nulle. * * * * Remarques : - * * * ******************************************************************************/ uint32_t count_methods_in_dex_pool(const GDexFormat *format) { uint32_t result; /* Quantité à retourner */ result = format->header.method_ids_size; return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index de la méthode recherchée. * * * * Description : Extrait une représentation de méthode d'une table DEX. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GDexMethod *get_method_from_dex_pool(GDexFormat *format, uint32_t index) { GDexMethod *result; /* Instance à retourner */ phys_t pos; /* Tête de lecture */ vmpa2t addr; /* Tête de lecture générique */ method_id_item method_id; /* Définition de la méthode */ result = NULL; if (index >= format->header.method_ids_size) goto gmfdp_error; if (format->methods[index] == NULL) { pos = format->header.method_ids_off + index * sizeof(method_id_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_method_id_item(format, &addr, &method_id)) goto gmfdp_error; format->methods[index] = g_dex_method_new_callable(format, &method_id); } result = format->methods[index]; if (result != NULL) g_object_ref(G_OBJECT(result)); gmfdp_error: return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à compléter. * * gid = groupe de travail impliqué. * status = barre de statut à tenir informée. * * * * Description : Charge toutes les classes listées dans le contenu binaire. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool load_all_dex_classes(GDexFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ guint runs_count; /* Qté d'exécutions parallèles */ uint32_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t msg; /* Message de progression */ guint i; /* Boucle de parcours */ uint32_t begin; /* Début de bloc de traitement */ uint32_t end; /* Fin d'un bloc de traitement */ GDexLoading *loading; /* Tâche de chargement à lancer*/ result = true; /* Préparation du réceptacle */ format->classes = (GDexClass **)calloc(format->header.class_defs_size, sizeof(GDexClass *)); /* Lancement des chargements */ runs_count = get_max_online_threads(); run_size = format->header.class_defs_size / runs_count; queue = get_work_queue(); msg = gtk_status_stack_add_activity(status, _("Loading all classes from the Dex pool..."), format->header.class_defs_size); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = format->header.class_defs_size; else end = begin + run_size; loading = g_dex_loading_new(format, begin, end, msg, (dex_loading_cb)get_class_from_dex_pool, &result); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loading), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); return result; } /****************************************************************************** * * * Paramètres : format = représentation interne du format DEX à consulter. * * index = index de la classe recherchée. * * * * Description : Extrait une représentation de classe d'une table DEX. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GDexClass *get_class_from_dex_pool(GDexFormat *format, uint32_t index) { GDexClass *result; /* Instance à retourner */ phys_t pos; /* Tête de lecture */ vmpa2t addr; /* Tête de lecture générique */ class_def_item class_def; /* Définition de la classe */ result = NULL; if (index >= format->header.class_defs_size) goto gcfdp_error; if (format->classes[index] == NULL) { pos = format->header.class_defs_off + index * sizeof(class_def_item); init_vmpa(&addr, pos, VMPA_NO_VIRTUAL); if (!read_dex_class_def_item(format, &addr, &class_def)) goto gcfdp_error; format->classes[index] = g_dex_class_new(format, &class_def); } result = format->classes[index]; if (result != NULL) g_object_ref(G_OBJECT(result)); gcfdp_error: return result; }