//------------------------------------------------ //--- 010 Editor v2.1 Binary Template // // File: CLASS.bt // Authors: Kevin O. Grover // E-mail: kevin@kevingrover.net // Version: 1.5 // Purpose: Parse Java Class (JVM) files. // Category: Programming // File Mask: *.class // ID Bytes: CA FE BA BE // History: // 1.5 2021-06-17 licho: fix: read_AF should use `bit and` // 1.4 2017-05-16 DO: Skip next constant after CONSTANT_Long or CONSTANT_Double as per spec. // 1.3 2016-07-11 T. Strazzere: Add missing constant types // 1.2 2016-02-12 SweetScape Software: Updated header for repository submission. // 1.1 2014-08-13 kog: Updated for Java 7/8 Version number (and future) // 1.0 2007-02-26 kog: Original Version // // This template was written to the "The Java Virtual Machine // Specification", Second Edition, by Tim Lindholm, Frank Yellin. // It was later updated to include the provisio in that spec (and future // specs). // // The details used to write this Template came mainly from Chapter 4: // "The class File Format". // // You can get the document from the Oracle Java Pages or Wikipedia: // http://docs.oracle.com/javase/specs/ // http://docs.oracle.com/javase/specs/jvms/se7/jvms7.pdf // http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf // http://en.wikipedia.org/wiki/Class_file //------------------------------------------------ BigEndian(); // Types used in the JVM Spec. // (To make this code as close to the text in the spec as possible.) typedef uint32 u4; typedef uint16 u2; typedef ubyte u1; typedef enum eACCESS_FLAGS { ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package. ACC_PRIVATE = 0x0002, // Declared private; usable only within the defining class. ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses. ACC_STATIC = 0x0008, // Declared static. ACC_FINAL = 0x0010, // Declared final; no subclasses allowed. ACC_SUPER = 0x0020, // Treat superclass methods specially when invoked by the invokespecial instruction. ACC_VOLATILE = 0x0040, // Declared volatile; cannot be cached. ACC_TRANSIENT = 0x0080, // Declared transient; not written or read by a persistent object manager. ACC_NATIVE = 0x0100, // Declared native; implemented in a language other than Java. ACC_INTERFACE = 0x0200, // Is an interface, not a class. ACC_ABSTRACT = 0x0400, // Declared abstract; may not be instantiated. ACC_STRICT = 0x0800 // Declared strictfp; floating-point mode is FP-strict } AF ; string read_AF (local AF &af) { local string s = ""; local int commaNeeded = 0; local AF i = 1; SPrintf (s, "%d: ", af); // Iterate over all possible values the flags // if the given bit is set, add it's text representation to the // return string. // NOTE: There's probably a better way to do this. (More portable?) while (i < ACC_STRICT) { if (af & i) { if (commaNeeded) { s += ", "; } s += EnumToString(i); commaNeeded = 1; } i = i << 1; } return s; } // Constants for cp_info.tag ... typedef enum eCP_CONST { CONSTANT_Utf8 = 1, CONSTANT_Integer = 3, CONSTANT_Float = 4, CONSTANT_Long = 5, CONSTANT_Double = 6, CONSTANT_Class = 7, CONSTANT_String = 8, CONSTANT_Fieldref = 9, CONSTANT_Methodref = 10, CONSTANT_InterfaceMethodref = 11, CONSTANT_NameAndType = 12, CONSTANT_MethodHandle_info = 15, CONSTANT_MethodType = 16, CONSTANT_InvokeDynamic = 18 } CP_CONST; // Variable to zero the size of the next cp_info (see below). // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.5 local int ignoreNextIndex = 0; // Constant Pool Table Structure // NOTE: These are NOT constant sized items. Although referenced // like an array in the main structure, you can not use position // to directly infer a byte offset because you do not know the // sizes of the items preceeding it. typedef struct { if(ignoreNextIndex == 0) { CP_CONST tag; switch(tag) { case CONSTANT_Class: u2 name_index; break; case CONSTANT_Methodref: case CONSTANT_Fieldref: case CONSTANT_InterfaceMethodref: u2 class_index; u2 name_and_type_index; break; case CONSTANT_Utf8: u2 length; if(length > 0) { char bytes[length]; } break; case CONSTANT_Integer: case CONSTANT_Float: u4 bytes; break; case CONSTANT_Long: case CONSTANT_Double: u4 high_bytes; u4 low_bytes; // Ignore the next cp_info entry by making it empty. ignoreNextIndex = 1; break; case CONSTANT_String: u2 string_index; break; case CONSTANT_NameAndType: u2 name_index; u2 descriptor_index; break; case CONSTANT_MethodHandle_info: u1 reference_kind; u2 reference_index; break; case CONSTANT_MethodType: u2 descriptor_index; break; case CONSTANT_InvokeDynamic: u2 bootstrap_method_attr_index; u2 name_and_type_index; break; default: local string s; SPrintf(s, "Unknown 'constant_pool' tag: %d", tag); Warning(s); return -3; } } else { ignoreNextIndex = 0; } } cp_info ; /** * Reader for 'cp_info' * @param cp_info Constant Pool info structure */ string read_cp_info (local cp_info &i) { local string s = ""; s = EnumToString(i.tag); if (i.tag == CONSTANT_Utf8 && i.length > 0) { s += ": " + i.bytes; } return s; } typedef struct { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; } attribute_info ; typedef struct { AF access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } field_info ; typedef struct { AF access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } method_info ; // MAIN Structure typedef struct { u4 magic ; if (magic != 0xCAFEBABE) { // Check that 'magic' is correct Warning("Incorrect Magic Number. This is not a JVM Class. Template Terminated."); return -1; } u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; AF access_flags; u2 this_class; // Reference into constant_pool table if (this_class < 1 || this_class > constant_pool_count-1) { local string s; SPrintf(s,"Bad 'this_class' reference (%d). It should be in the range [1..%d]", this_class, constant_pool_count-1); Warning(s); return -2; } u2 super_class; // Reference into constant_pool table if (super_class < 1 || super_class > constant_pool_count-1) { local string s; SPrintf(s,"Bad 'super_class' reference (%d). It should be in the range [1..%d]", super_class, constant_pool_count-1); Warning(s); return -2; } u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; } JVMClass ; /** * Reader for 'JVMClass' * Returns the JVM Version: major.minor and the Java version (if it can * calculate it from the JVM Version. * @param j JVMClass The JVM Class Structure * @return string The version information */ string read_JVMClass (local JVMClass &j) { // JVM Version (as stored in the class file) local string s; SPrintf (s, "JVM class v%d.%d", j.major_version, j.minor_version); // Java Runtime Version. From the JVM Spec for class files local int major = 1; local int minor = 0; if (j.major_version == 45) { if (j.minor_version >= 3) minor = 1; } else { if (j.major_version >= 46) { minor = j.major_version - 44; } else { // Unknown: it's either newer or older than expected major = 0; minor = 0; Warning("Unexpected JVM major version number."); } } if (major || minor) { local string jre_v; SPrintf(jre_v, " (Java %d.%d)", major, minor); s += jre_v; } else { s += " (Unknown Java Version)"; } return s; } // ***************************************** // MAIN - Allocate data here... // ***************************************** struct JVMClass jc;