/* * Nastroj pro vypis vsech udaju ulozenych v constant poolu v souborech .class * Verze 2.0: - pridan vypis priznaku tridy/rozhrani * Verze 3.0: - pridan vypis nazvu tridy a prime nadtridy * * Pavel Tisnovsky 2011 * * Preklad; * gcc -ansi -Wall decompiler.c */ #include #include #include /* Odkomentujte pro zakaz tisku retezcovych konstant */ /*#define DONT_PRINT_STRING_ENTRIES*/ /* * Typy zaznamu ulozenych v constant poolu. * Viz dokumentace k JVM. */ enum { CONSTANT_Class = 7, CONSTANT_Fieldref = 9, CONSTANT_Methodref = 10, CONSTANT_InterfaceMethodref = 11, CONSTANT_String = 8, CONSTANT_Integer = 3, CONSTANT_Float = 4, CONSTANT_Long = 5, CONSTANT_Double = 6, CONSTANT_NameAndType = 12, CONSTANT_Utf8 = 1 }; /* * Priznaky tridy ci rozhrani. */ enum { ACC_PUBLIC = 0x0001, /* verejna trida/rozhrani */ ACC_FINAL = 0x0010, /* finalni trida */ ACC_SUPER = 0x0020, /* semantika instrukce invokespecial */ ACC_INTERFACE = 0x0200, /* rozliseni trida/rozhrani */ ACC_ABSTRACT = 0x0400, /* abstraktni trida */ ACC_SYNTHETIC = 0x1000, /* synteticka trida */ ACC_ANNOTATION = 0x2000, /* anotace */ ACC_ENUM = 0x4000, /* vycet */ }; /* * Struktura reprezentujici jeden zaznam v constant poolu. * (nektere polozky by bylo mozne sloucit a zmensit tak pametove naroky za cenu vetsi necitelnosti) */ typedef struct { uint8_t tag; /* typ zaznamu */ uint16_t class_index; /* odkaz na jmeno tridy */ uint16_t name_type_index; /* odkaz na typ metody/atributu... */ uint16_t name_index; /* odkaz na jmeno metody/atributu... */ uint16_t descriptor_index; /* odkaz na identifikaci typu */ uint16_t string_length; /* delka retezcoveho literalu */ char *string; /* retezec (v programu se ukoncuje nulou, v constant poolu ma zadanou delku) */ union /* konstanta o velikosti 4 bajty */ { /* zjednoduseni pres unii, aby bylo mozne jednoduse provadet prevody */ uint32_t read_value; /* platne pro little endian procesory!!! */ int32_t int_value; float float_value; } four_bytes; union /* konstanta o velikosti 8 bajtu */ { /* zjednoduseni pres unii, aby bylo mozne jednoduse provadet prevody */ struct { uint32_t lower_word; /* platne pro little endian procesory */ uint32_t higher_word; } read_values; int64_t long_value; double double_value; } eight_bytes; } PoolEntry; /* * Pole obsahujici cely constant pool. */ PoolEntry* pool_entries = NULL; int constant_pool_count; /* * Funkce zavolana v pripade, ze pri cteni ze souboru dojde k chybe. */ void read_error(FILE *fin) { printf("Chyba pri cteni bajtu na offsetu %ld\n", ftell(fin)); if (fclose(fin)) { puts("... a uzavreni souboru taky selhalo!"); } exit(1); } /* * Precteni jednoho bajtu ze souboru s kontrolou, zda nedoslo k chybe. */ uint8_t read_byte(FILE *fin) { int i = fgetc(fin); if (i == EOF) { read_error(fin); } return i; } /* * Precteni dvou bajtu ze souboru s kontrolou, zda nedoslo k chybe. * V souborech .class se pouziva poradi bajtu typu big endian. */ uint16_t read_two_bytes(FILE *fin) { int i1 = read_byte(fin); int i2 = read_byte(fin); return (i1 << 8) | (i2); } /* * Precteni ctyr bajtu ze souboru s kontrolou, zda nedoslo k chybe. * V souborech .class se pouziva poradi bajtu typu big endian. */ uint32_t read_four_bytes(FILE *fin) { int i1 = read_byte(fin); int i2 = read_byte(fin); int i3 = read_byte(fin); int i4 = read_byte(fin); return (i1 << 24) | (i2 << 16) | (i3 << 8) | (i4); } /* * Otevreni souboru .class se zakladni kontrolou, zda nedoslo k chybe. */ FILE* open_class_file(const char *file_name) { FILE *fin; fin = fopen(file_name, "rb"); if (!fin) { puts("Chyba pri otevirani souboru"); exit(1); } return fin; } /* * Nacteni a zobrazeni magicke konstanty. */ void process_magic_number(FILE *fin) { uint32_t magic = read_four_bytes(fin); printf("Magicka konstanta: %08x\n", magic); if (magic != 0xcafebabe) { puts("Spatne magicka konstanta (jedna se skutecne o .class?)"); fclose(fin); exit(1); } } /* * Nacteni a zobrazeni verze .class souboru. */ void process_class_version(FILE *fin) { uint16_t minor_version; uint16_t major_version; minor_version = read_two_bytes(fin); major_version = read_two_bytes(fin); printf("Cislo verze: %d\n", major_version); printf("Cislo podverze: %d\n", minor_version); } /* * Nacteni jednoho zaznamu z constant poolu. Funkce vraci 1 nebo 2 * podle toho, zda ma byt dalsi zaznam umisten ihned za prectenym * zaznamem, nebo ma byt za prectenym zaznamem "mezera" (to kvuli * osmibajtovym konstantam, ktere "genialne" obsazuji dva sloty) */ int read_constant_pool_entry(FILE *fin, PoolEntry *pool_entry) { uint8_t tag; uint32_t length; uint32_t i; tag = read_byte(fin); /* nejdulezitejsi informace - typ zaznamu */ pool_entry->tag = tag; switch (tag) { case CONSTANT_Class: /* tento zaznam obsahuje jednu referenci */ pool_entry->class_index = read_two_bytes(fin); break; case CONSTANT_Fieldref: /* tento zaznam obsahuje dve reference */ case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: pool_entry->class_index = read_two_bytes(fin); pool_entry->name_type_index = read_two_bytes(fin); break; case CONSTANT_String: /* tento tento zaznam obsahuje jednu referenci */ pool_entry->class_index = read_two_bytes(fin); break; case CONSTANT_Integer: /* tento zaznam obsahuje ctyrbajtovou konstantu */ case CONSTANT_Float: pool_entry->four_bytes.read_value = read_four_bytes(fin); break; case CONSTANT_Long: /* tento zaznam obsahuje osmibajtovou konstantu */ case CONSTANT_Double: /* navic musi byt za zaznamem jedno volne misto */ pool_entry->eight_bytes.read_values.higher_word = read_four_bytes(fin); pool_entry->eight_bytes.read_values.lower_word = read_four_bytes(fin); return 2; break; case CONSTANT_NameAndType: /* tento zaznam obsahuje dve reference */ pool_entry->name_index = read_two_bytes(fin); pool_entry->descriptor_index = read_two_bytes(fin); break; case CONSTANT_Utf8: /* string v kodovani UTF-8 */ length = read_two_bytes(fin); pool_entry->string_length = length; pool_entry->string = (char*)malloc(sizeof(char) * (length+1)); for (i = 0; i < length; i++) /* nacteme cely retezec... */ { pool_entry->string[i] = read_byte(fin); } pool_entry->string[i] = 0; /* ... a ukoncime ho nulou pro snadny tisk v printf */ break; default: printf("Neznamy tag precteny z const. poolu: %d\n", tag); break; } return 1; } /* * Tisk zaznamu typu "Class". */ void print_class_info(PoolEntry *pool_entry) { /* pouziti reference na jmeno tridy */ char *class_name = pool_entries[pool_entry->class_index-1].string; printf("Class %3d %s", pool_entry->class_index, class_name); } /* * Tisk zaznamu typu "Field reference" */ void print_field_ref(PoolEntry *pool_entry) { int class_index = pool_entry->class_index; int name_type_index = pool_entry->name_type_index; /* ziskani jmena tridy pres jednu referenci */ char *class_name = pool_entries[pool_entries[class_index - 1].class_index-1].string; /* ziskani typu a jmena atributu pres dve reference */ char *name = pool_entries[pool_entries[name_type_index - 1].name_index-1].string; char *descriptor = pool_entries[pool_entries[name_type_index - 1].descriptor_index-1].string; printf("FieldRef %3d %3d %s.%s:%s", class_index, name_type_index, class_name, name, descriptor); } /* * Tisk zaznamu typu "Method reference" */ void print_method_ref(PoolEntry *pool_entry) { int class_index = pool_entry->class_index; int name_type_index = pool_entry->name_type_index; /* ziskani jmena tridy pres jednu referenci */ char *class_name = pool_entries[pool_entries[class_index - 1].class_index-1].string; /* ziskani typu a jmena atributu pres dve reference */ char *name = pool_entries[pool_entries[name_type_index - 1].name_index-1].string; char *descriptor = pool_entries[pool_entries[name_type_index - 1].descriptor_index-1].string; printf("MethodRef %3d %3d %s.%s%s", class_index, name_type_index, class_name, name, descriptor); } /* * Tisk zaznamu typu "Interface method reference" */ void print_interface_method_ref(PoolEntry *pool_entry) { int class_index = pool_entry->class_index; int name_type_index = pool_entry->name_type_index; /* ziskani jmena tridy pres jednu referenci */ char *class_name = pool_entries[pool_entries[class_index - 1].class_index-1].string; /* ziskani typu a jmena atributu pres dve reference */ char *name = pool_entries[pool_entries[name_type_index - 1].name_index-1].string; char *descriptor = pool_entries[pool_entries[name_type_index - 1].descriptor_index-1].string; printf("IfaceMethodRef %3d %3d %s.%s%s", class_index, name_type_index, class_name, name, descriptor); } /* * Tisk zaznamu typu "String" */ void print_string_const(PoolEntry *pool_entry) { /* pouziti reference na obsah retezce */ char *string = pool_entries[pool_entry->class_index-1].string; printf("String const %3d \"%s\"", pool_entry->class_index, string); } /* * Tisk zaznamu typu "Integer constant" */ void print_integer_const(PoolEntry *pool_entry) { printf("%-26s %d", "Integer", pool_entry->four_bytes.int_value); } /* * Tisk zaznamu typu "Float constant" */ void print_float_const(PoolEntry *pool_entry) { printf("%-26s %f", "Float", pool_entry->four_bytes.float_value); } /* * Tisk zaznamu typu "Long constant" */ void print_long_const(PoolEntry *pool_entry) { printf("%-26s %lld", "Long", pool_entry->eight_bytes.long_value); } /* * Tisk zaznamu typu "Double constant" */ void print_double_const(PoolEntry *pool_entry) { printf("%-26s %f", "Double", pool_entry->eight_bytes.double_value); } /* * Tisk zaznamu typu "Type and name" */ void print_type_and_name(PoolEntry *pool_entry) { /* zjistit oba odkazy */ int name_index = pool_entry->name_index; int descriptor_index = pool_entry->descriptor_index; /* z odkazu ziskat oba retezce */ char *name_string = pool_entries[name_index-1].string; char *descriptor_string = pool_entries[descriptor_index-1].string; printf("Name and type %3d %3d %s %s", descriptor_index, name_index, descriptor_string, name_string); } /* * Tisk zaznamu typu "UTF-8 string" */ void print_utf8_string(PoolEntry *pool_entry) { printf("String \"%s\"", pool_entry->string); } /* * Tisk hodnoty jednoho zaznamu precteneho z constant poolu. */ void print_constant_pool_entry(int item_index, PoolEntry *pool_entry) { #ifdef DONT_PRINT_STRING_ENTRIES if (pool_entry->tag != CONSTANT_Utf8) { #endif printf("%3d %3d ", item_index+1, pool_entry->tag); #ifdef DONT_PRINT_STRING_ENTRIES } #endif switch (pool_entry->tag) { case CONSTANT_Class: print_class_info(pool_entry); break; case CONSTANT_Fieldref: print_field_ref(pool_entry); break; case CONSTANT_Methodref: print_method_ref(pool_entry); break; case CONSTANT_InterfaceMethodref: print_interface_method_ref(pool_entry); break; case CONSTANT_String: print_string_const(pool_entry); break; case CONSTANT_Integer: print_integer_const(pool_entry); break; case CONSTANT_Float: print_float_const(pool_entry); break; case CONSTANT_Long: print_long_const(pool_entry); break; case CONSTANT_Double: print_double_const(pool_entry); break; case CONSTANT_NameAndType: print_type_and_name(pool_entry); break; case CONSTANT_Utf8: #ifndef DONT_PRINT_STRING_ENTRIES print_utf8_string(pool_entry); #endif break; default: printf("Neznamy tag"); break; } #ifdef DONT_PRINT_STRING_ENTRIES if (pool_entry->tag != CONSTANT_Utf8) { #endif putchar('\n'); #ifdef DONT_PRINT_STRING_ENTRIES } #endif } /* * Alokace pameti pro ulozeni informaci prectenych z constant poolu s inicializaci. */ void create_constant_pool(int length) { int i; /* alokace pameti pro cele dynamicke pole */ pool_entries = (PoolEntry*) malloc(length * sizeof(PoolEntry)); /* inicializace vsech dulezitych polozek ve strukture */ for (i = 0; i < length; i++) { PoolEntry *pool_entry = pool_entries + i; pool_entry->tag = 0; pool_entry->class_index = 0; pool_entry->name_type_index = 0; pool_entry->name_index = 0; pool_entry->descriptor_index = 0; pool_entry->string_length = 0; pool_entry->string = NULL; } } /* * Dealokace pameti s informacemi prectenymi z constant poolu. */ void delete_constant_pool(int length) { int i; /* dulezite je dealokovat vsechny retezce */ for (i = 0; i < length; i++) { PoolEntry *pool_entry = pool_entries + i; if (pool_entry->string) { free(pool_entry->string); } } /* a nasledne vlastni pole */ free(pool_entries); } /* * Nacteni vsech zaznamu z constant poolu a nasledne tisk jeho obsahu. * Tuto operaci je nutne rozdelit na dve casti, jinak by nebylo mozne * provadet "referencovani" mezi jednotlivymi polozkami (jedna se mnohdy * o dopredne reference). */ void process_constant_pool(FILE *fin) { int i; /* pocet polozek v constant poolu */ constant_pool_count = read_two_bytes(fin); constant_pool_count--; printf("\nVelikost const. poolu: %d prvku\n", constant_pool_count); /* alokace pameti + inicializace */ create_constant_pool(constant_pool_count); /* nacteni prvku je nutne provadet ve while, kvuli ne zcela * inteligentnimu zpusobu ulozeni konstant typu long a double. */ i = 0; while (i < constant_pool_count) { i += read_constant_pool_entry(fin, pool_entries+i); } /* tisk polozek */ for (i = 0; i < constant_pool_count; i++) { print_constant_pool_entry(i, pool_entries+i); } } /* * Nacteni a vypis atributu tridy ci rozhrani. */ void process_class_flags(FILE *fin) { uint16_t flags; flags = read_two_bytes(fin); printf("\nClass/interface attributes: 0x%04x\n", flags); if (flags & ACC_PUBLIC) puts(" ACC_PUBLIC"); if (flags & ACC_FINAL) puts(" ACC_FINAL"); if (flags & ACC_SUPER) puts(" ACC_SUPER"); if (flags & ACC_INTERFACE) puts(" ACC_INTERFACE"); if (flags & ACC_ABSTRACT) puts(" ACC_ABSTRACT"); if (flags & ACC_SYNTHETIC) puts(" ACC_SYNTHETIC"); if (flags & ACC_ANNOTATION) puts(" ACC_ANNOTATION"); if (flags & ACC_ENUM) puts(" ACC_ENUM"); } /* * Nacteni a vypis jmena tridy. */ void process_class_name(FILE *fin) { /* nacist index z bajtkodu */ uint16_t index = read_two_bytes(fin); /* v cecku se indexuje od 0, v constant poolu od 1 */ index--; printf("\nClass name is stored in constant pool #%d\n", index); PoolEntry pool_entry = pool_entries[index]; print_class_info(&pool_entry); } /* * Nacteni a vypis jmena nadrazene tridy. */ void process_superclass_name(FILE *fin) { /* nacist index z bajtkodu */ uint16_t index = read_two_bytes(fin); /* test na specialni pripad */ if (index == 0) { printf("\nIt's a class without super class!\n"); } else { /* v cecku se indexuje od 0, v constant poolu od 1 */ index--; printf("\nSuper class name is stored in constant pool #%d\n", index); PoolEntry pool_entry = pool_entries[index]; print_class_info(&pool_entry); } } /* * Zpracovani constant poolu ulozeneho v souboru .class */ void process_whole_class(FILE *fin) { process_magic_number(fin); process_class_version(fin); process_constant_pool(fin); process_class_flags(fin); process_class_name(fin); process_superclass_name(fin); /* rucni GC :-) */ delete_constant_pool(constant_pool_count); } /* * Vstup do programu. * V prvnim argumentu je ocekavano jmeno .class souboru */ int main(int argc, char** argv) { FILE *fin; if (argc != 2) { puts("Pouziti: a.out trida.class"); return 1; } fin = open_class_file(argv[1]); process_whole_class(fin); if (fclose(fin)) { puts("Chyba pri zavirani souboru"); return 1; } return 0; } /* finito */