/* Inspector.java by Mark D. LaDue */ /* June 24, 1997 */ /* Copyright (c) 1997 Mark D. LaDue You may study, use, modify, and distribute this example for any purpose. This example is provided WITHOUT WARRANTY either expressed or implied. */ /* This Java application analyzes the entries in the constant pool and locates the code arrays in a Java class file. Each entry in the constant pool yields the following information: Index Tag Reference(s)/Value(s) ----- --- --------------------- where "Index" is its position within the class file's constant pool, "Tag" is the official tag number for that type of entry, and "Reference(s)/Value(s)" contains the constant pool information according to the entry's type. (See Lindholm and Yellin's "The Java Virtual Machine Specification" for details.) For each code array in the class file, its starting byte, its total length, and the name of the method in which it occurs are given. Combining this information with the information yielded by the humble "javap" utility gives one sufficient information to hack the code arrays in Java class files. */ import java.io.*; class Inspector { public static void main(String[] argv) { int fpointer = 8; // Where are we in the class file? int cp_entries = 1; // How big is the constant pool? int Code_entry = 1; // Where is the entry that denotes "Code"? int num_interfaces = 0; // How many interfaces does it use? int num_fields = 0; // How many fields are there? int num_f_attributes = 0; // How many attributes does a field have? int num_methods = 0; // How many methods are there? int num_m_attributes = 0; // How many attributes does a method have? int[] tags; // Tags for the constant pool entries int[] read_ints1; // References for some constant pool entries int[] read_ints2; // References for some constant pool entries long[] read_longs; // Values for some constant pool entries float[] read_floats; // Values for some constant pool entries double[] read_doubles; // Values for some constant pool entries StringBuffer[] read_strings; // Strings in some constant pool entries int[] method_index; long[] code_start; long[] code_length; // How on earth do I use this thing? if (argv.length != 1) { System.out.println("Try \"java Inspector class_file.class\""); System.exit(1); } // Start by opening the file for reading try { RandomAccessFile victim = new RandomAccessFile(argv[0], "r"); // Skip the magic number and versions and start looking at the class file victim.seek(fpointer); // Determine how many entries there are in the constant pool cp_entries = victim.readUnsignedShort(); fpointer += 2; // Set up the arrays of useful information about the constant pool entries tags = new int[cp_entries]; read_ints1 = new int[cp_entries]; read_ints2 = new int[cp_entries]; read_longs = new long[cp_entries]; read_floats = new float[cp_entries]; read_doubles = new double[cp_entries]; read_strings = new StringBuffer[cp_entries]; //Initialize these arrays for (int cnt = 0; cnt < cp_entries; cnt++) { tags[cnt] = -1; read_ints1[cnt] = -1; read_ints2[cnt] = -1; read_longs[cnt] = -1; read_floats[cnt] = -1; read_doubles[cnt] = -1; read_strings[cnt] = new StringBuffer(); } // Look at each entry in the constant pool and save the information in it for (int i = 1; i < cp_entries; i++) { tags[i] = victim.readUnsignedByte(); fpointer++; int skipper = 0; int start = 0; int test_int = 0; switch (tags[i]) { case 3: read_ints1[i] = victim.readInt(); fpointer += 4; break; case 4: read_floats[i] = victim.readFloat(); fpointer += 4; break; case 5: read_longs[i] = victim.readLong(); fpointer += 8; i++; break; case 6: read_doubles[i] = victim.readDouble(); fpointer += 8; i++; break; case 7: case 8: read_ints1[i] = victim.readUnsignedShort(); fpointer += 2; break; case 9: case 10: case 11: case 12: read_ints1[i] = victim.readUnsignedShort(); fpointer += 2; victim.seek(fpointer); read_ints2[i] = victim.readUnsignedShort(); fpointer += 2; break; // This is the critical case - determine an entry in the constant pool where // the string "Code" is found so we can later identify the code attributes // for the class's methods case 1: skipper = victim.readUnsignedShort(); start = fpointer; fpointer += 2; victim.seek(fpointer); for (int cnt = 0; cnt < skipper; cnt++) { int next = victim.readUnsignedByte(); switch (next) { case 9: read_strings[i].append("\\" + "t"); break; case 10: read_strings[i].append("\\" + "n"); break; case 11: read_strings[i].append("\\" + "v"); break; case 13: read_strings[i].append("\\" + "r"); break; default: read_strings[i].append((char)next); break; } victim.seek(++fpointer); } victim.seek(start); if (skipper == 4) { fpointer = start + 2; victim.seek(fpointer); test_int = victim.readInt(); if (test_int == 1131373669) {Code_entry = i;} fpointer = fpointer + skipper; } else {fpointer = start + skipper + 2;} break; } victim.seek(fpointer); } // Skip ahead and see how many interfaces the class implements fpointer += 6; victim.seek(fpointer); num_interfaces = victim.readUnsignedShort(); // Bypass the interface information fpointer = fpointer + 2*(num_interfaces) + 2; victim.seek(fpointer); // Determine the number of fields num_fields = victim.readUnsignedShort(); // Bypass the field information fpointer += 2; victim.seek(fpointer); for (int j=0; j