/* Chrysalide - Outil d'analyse de fichiers binaires * pool.c - lecture du réservoir de constantes * * Copyright (C) 2018 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 "java-int.h" #include "../../common/endianness.h" #include "../../common/extstr.h" /* Charge les propriétés d'une constante du réservoir. */ bool load_java_pool_entry(GJavaFormat *, constant_pool_entry *, off_t *); /* Fournit une entrée donnée du réservoir de constantes. */ const constant_pool_entry *get_java_pool_entry(const GJavaFormat *, uint16_t, ConstantPoolTag); /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * pos = point de lecture à faire évoluer. [OUT] * * * * Description : Charge le réservoir de constantes d'un binaire Java. * * * * Retour : true si l'opération s'est bien déroulée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool load_java_pool(GJavaFormat *format, off_t *pos) { bool result; /* Bilan à remonter */ uint16_t count; /* Nombre d'éléments présents */ uint16_t i; /* Boucle de parcours */ result = false/*read_u16(&count, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG)*/; #if 0 printf("Alloc %hu entries (result=%d)\n", count, result); format->header.pool_len = count - 1; format->header.pool = (constant_pool_entry *)calloc(count - 1, sizeof(constant_pool_entry)); for (i = 1; i < count && result; i++) { result = load_java_pool_entry(format, &format->header.pool[i - 1], pos); if (format->header.pool[i - 1].tag == CONSTANT_LONG || format->header.pool[i - 1].tag == CONSTANT_DOUBLE) { i++; /* On n'est jamais trop prudent */ if (i < count) format->header.pool[i - 1].tag = CONSTANT_EMPTY; } } #endif return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à vider. * * * * Description : Décharge le réservoir de constantes d'un binaire Java. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void unload_java_pool(GJavaFormat *format) { uint16_t i; /* Boucle de parcours */ for (i = 0; i < format->header.pool_len; i++) switch (format->header.pool[i].tag) { case CONSTANT_EMPTY: case CONSTANT_CLASS: case CONSTANT_FIELD_REF: case CONSTANT_METHOD_REF: case CONSTANT_INTERFACE_METHOD_REF: case CONSTANT_STRING: case CONSTANT_INTEGER: case CONSTANT_FLOAT: case CONSTANT_LONG: case CONSTANT_DOUBLE: case CONSTANT_NAME_AND_TYPE: break; case CONSTANT_UTF8: free(format->header.pool[i].info.utf8.bytes); break; } free(format->header.pool); } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * entry = élément à spécifier. [OUT] * * pos = point de lecture à faire évoluer. [OUT] * * * * Description : Charge les propriétés d'une constante du réservoir. * * * * Retour : true si l'opération s'est bien déroulée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool load_java_pool_entry(GJavaFormat *format, constant_pool_entry *entry, off_t *pos) { bool result; /* Bilan à retourner */ uint8_t tag; /* Type de l'élément */ uint32_t low_bytes; /* Octets de poids faible */ uint32_t high_bytes; /* Octets de poids fort */ uint64_t bits; /* Nombre lu sur 64 bits */ int sign; /* Signe du nombre lu */ int exponent; /* Exposant du nombre lu */ uint64_t mantissa32; /* Mantisse du nombre lu 32b */ uint64_t mantissa64; /* Mantisse du nombre lu 64b */ uint16_t length; /* Taille d'une chaîne */ result = false/*read_u8(&tag, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG)*/; #if 0 entry->tag = tag; switch (entry->tag) { case CONSTANT_CLASS: result = read_u16(&entry->info.class.name_index, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); break; case CONSTANT_FIELD_REF: case CONSTANT_METHOD_REF: case CONSTANT_INTERFACE_METHOD_REF: result = read_u16(&entry->info.ref.class_index, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); result &= read_u16(&entry->info.ref.name_and_type_index, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); break; case CONSTANT_STRING: result = read_u16(&entry->info.string.string_index, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); break; case CONSTANT_INTEGER: result = read_u32(&entry->info.int_val.val, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); break; case CONSTANT_FLOAT: result = read_u32(&low_bytes, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); if (result) { if (low_bytes == 0x7f800000) entry->info.float_val.val = INFINITY; else if (low_bytes == 0xff800000) entry->info.float_val.val = /* -1* */INFINITY; else if ((low_bytes >= 0x7f800001 && low_bytes <= 0x7fffffff) || (low_bytes >= 0xff800001 && low_bytes <= 0xffffffff)) entry->info.float_val.val = NAN; else if (low_bytes == 0x00000000 || low_bytes == 0x80000000) entry->info.float_val.val = 0; else { sign = (low_bytes & 0x80000000) ? -1 : 1; exponent = (low_bytes >> 23) & 0xff; mantissa32 = (exponent == 0 ? (low_bytes & 0x7fffff) << 1 : (low_bytes & 0x7fffff) | 0x800000); entry->info.float_val.val = pow(2, (exponent - 150)); entry->info.float_val.val *= mantissa32; entry->info.float_val.val *= sign; } } break; case CONSTANT_LONG: result = read_u32(&high_bytes, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); result &= read_u32(&low_bytes, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); if (result) { entry->info.double_val.val = (uint64_t)high_bytes << 32; entry->info.double_val.val += low_bytes; } break; case CONSTANT_DOUBLE: result = read_u32(&high_bytes, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); result &= read_u32(&low_bytes, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); if (result) { bits = (uint64_t)high_bytes << 32 | (uint64_t)low_bytes; if (bits == 0x7ff0000000000000ll) entry->info.double_val.val = INFINITY; else if (bits == 0xfff0000000000000ll) entry->info.double_val.val = /* -1* */INFINITY; else if ((bits >= 0x7ff0000000000001ll && bits <= 0x7fffffffffffffffll) || (bits >= 0xfff0000000000001ll && bits <= 0xffffffffffffffffll)) entry->info.double_val.val = NAN; else if (bits == 0x0000000000000000ll || bits == 0x8000000000000000ll) entry->info.double_val.val = 0; else { sign = ((bits >> 63) == 0) ? 1 : -1; exponent = (bits >> 52) & 0x7ffl; mantissa64 = (exponent == 0 ? (bits & 0xfffffffffffffll) << 1 : (bits & 0xfffffffffffffll) | 0x10000000000000ll); entry->info.double_val.val = pow(2, (exponent - 1075)); entry->info.double_val.val *= mantissa64; entry->info.double_val.val *= sign; } } break; case CONSTANT_NAME_AND_TYPE: result = read_u16(&entry->info.name_type.name_index, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); result &= read_u16(&entry->info.name_type.descriptor_index, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); break; case CONSTANT_UTF8: result = read_u16(&length, G_BIN_FORMAT(format)->content, pos, G_BIN_FORMAT(format)->length, SRE_BIG); if (result) { entry->info.utf8.bytes = (char *)calloc(length + 1, sizeof(char)); memcpy(entry->info.utf8.bytes, &G_BIN_FORMAT(format)->content[*pos], length); *pos += length; } break; default: result = false; break; } #endif return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * index = indice de l'élément dont la valeur est à recupérer. * * expected = type de l'élément à trouver à l'indice donné. * * * * Description : Fournit une entrée donnée du réservoir de constantes. * * * * Retour : Entrée du réservoir de constantes ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ const constant_pool_entry *get_java_pool_entry(const GJavaFormat *format, uint16_t index, ConstantPoolTag expected) { const constant_pool_entry *result; /* Entrée à retourner */ constant_pool_entry *entry; /* Entrée du réservoir visée */ result = NULL; if (/*index < 0 && FIXME */index <= format->header.pool_len); { entry = &format->header.pool[index - 1]; if (entry->tag == expected) result = entry; } return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * index = indice de l'élément de la table à recupérer. * * expected = type de l'élément à trouver à l'indice donné. * * * * Description : Construit une version humaine de référence. * * * * Retour : Référence construite ou NULL en cas de problème. * * * * Remarques : - * * * ******************************************************************************/ char *build_reference_from_java_pool(const GJavaFormat *format, uint16_t index, JavaRefType expected) { char *result; /* Chaîne humaine à retourner */ const constant_pool_entry *entry; /* Entrée du réservoir visée 1 */ const constant_pool_entry *subentry; /* Entrée du réservoir visée 2 */ const char *tmp; /* Copie de chaîne intouchable */ result = NULL; switch (expected) { case JRT_FIELD: entry = get_java_pool_entry(format, index, CONSTANT_FIELD_REF); break; case JRT_METHOD: entry = get_java_pool_entry(format, index, CONSTANT_METHOD_REF); break; case JRT_INTERFACE_METHOD: entry = get_java_pool_entry(format, index, CONSTANT_INTERFACE_METHOD_REF); break; default: entry = NULL; break; } if (entry == NULL) goto brfjp_error; /* Lieu parent où trouver la référence */ subentry = get_java_pool_entry(format, entry->info.ref.class_index, CONSTANT_CLASS); if (subentry == NULL) goto brfjp_error; if (!get_java_pool_ut8_string(format, subentry->info.class.name_index, &tmp)) goto brfjp_error; result = strdup(tmp); /* Champ proprement dit */ subentry = get_java_pool_entry(format, entry->info.ref.name_and_type_index, CONSTANT_NAME_AND_TYPE); if (subentry == NULL) goto brfjp_error; if (!get_java_pool_ut8_string(format, subentry->info.name_type.name_index, &tmp)) goto brfjp_error; result = stradd(result, "."); result = stradd(result, tmp); /* Petites retouches finales */ result = strrpl(result, "/", "."); result = strrpl(result, "<", "<"); result = strrpl(result, ">", ">"); return result; brfjp_error: if (result != NULL) free(result); return NULL; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * index = indice de l'élément dont la valeur est à recupérer. * * str = adresse où placer la chaîne de caractères trouvée. * * * * Description : Recherche une chaîne de caractères dans le réservoir. * * * * Retour : true si l'opération s'est bien déroulée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool get_java_pool_ut8_string(const GJavaFormat *format, uint16_t index, const char **str) { bool result; /* Bilan à renvoyer */ const constant_pool_entry *entry; /* Entrée du réservoir visée */ entry = get_java_pool_entry(format, index, CONSTANT_UTF8); result = (entry != NULL); if (result) (*str) = entry->info.utf8.bytes; return result; }