//------------------------------------------------
//--- 010 Editor v2.1 Binary Template
//
// File: CLASS.bt
// Authors: Kevin O. Grover
// E-mail: kevin@kevingrover.net
// Version: 1.4
// Purpose: Parse Java Class (JVM) files.
// Category: Programming
// File Mask: *.class
// ID Bytes: CA FE BA BE
// History:
// 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;