Skip to content

Instantly share code, notes, and snippets.

@roblabla
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roblabla/396fac8159eba449ae55 to your computer and use it in GitHub Desktop.
Save roblabla/396fac8159eba449ae55 to your computer and use it in GitHub Desktop.
jarfile reader
var Protocols = require('../../index');
var fs = require("fs");
var yargs = require("yargs");
// Because the JVM is completely f***'d up, we can't simply use a normal 'array'.
// Pretty much, tag.value 5 & 6 requires you to increment the index by 2. Why ?
// Only god knows. Leads to super-dirty code.
var readConstantPool = function(buffer, offset, fieldInfo, context) {
var results = {
size: 0,
value: []
};
// First read constantPool count
var constantPoolCount = this._readField(buffer, offset, { type: "ushort" }, context);
if (constantPoolCount == null || constantPoolCount.error)
return constantPoolCount;
offset += constantPoolCount.size;
results.size += constantPoolCount.size;
var count = constantPoolCount.value;
for (var i = 0; i < count - 1; i++) {
// Read tag
var subRoot = { super: context };
var tag = this._readField(buffer, offset, { name: "tag", type: "ubyte" }, subRoot);
if (tag == null || tag.error) return tag;
offset += tag.size;
results.size += tag.size;
// Read info
var info = this._readField(buffer, offset, { type: "constantPoolInfo", typeArgs: { compareTo: "tag" } }, subRoot);
if (info == null || info.error) return info;
offset += info.size;
results.size += info.size;
results.value[i] = { tag: tag.value, info: info.value };
// If tag is 5/6, then we need to "skip" a field.
if (tag.value == 5 || tag.value == 6)
i++;
}
return results;
}
// TODO : Writer and SizeOf for the above datatype.
// Define constantPool datatype
var constantPool = Protocols.createType(readConstantPool, null, null);
// Define the top-level Class type.
var classFile = Protocols.extendType(Protocols.datatypes.struct, { fields: [
{ name: "magic", type: "buffer", typeArgs: { count: 4 } },
{ name: "minorVersion", type: "ushort" },
{ name: "majorVersion", type: "ushort" },
{ name: "constantPool", type: "constantPool" },
{ name: "accessFlags", type: "ushort" },
{ name: "thisClass", type: "ushort" },
{ name: "superClass", type: "ushort" },
{ name: "interfaces", type: "array", typeArgs: { type: "ushort", countType: "ushort" } },
{ name: "fields", type: "array", typeArgs: { type: "fieldInfo", countType: "ushort" } },
{ name: "methods", type: "array", typeArgs: { type: "methodInfo", countType: "ushort" } },
{ name: "attributes", type: "array", typeArgs: { type: "attributeInfo", countType: "ushort" } }
]});
var constantPoolInfo = Protocols.extendType(Protocols.datatypes.switch, { fields: {
7: [
{ name: "nameIndex", type: "ushort" }
],
9: [
{ name: "classIndex", type: "ushort" },
{ name: "nameAndTypeIndex", type: "ushort" }
],
10: [
{ name: "classIndex", type: "ushort" },
{ name: "nameAndTypeIndex", type: "ushort" }
],
11: [
{ name: "classIndex", type: "ushort" },
{ name: "nameAndTypeIndex", type: "ushort" }
],
8: [
{ name: "stringIndex", type: "ushort" }
],
3: [
{ name: "bytes", type: "int" }
],
4: [
{ name: "bytes", type: "float" }
],
5: [
{ name: "bytes", type: "buffer", typeArgs: { count: 8 } } // TODO
],
6: [
{ name: "bytes", type: "buffer", typeArgs: { count: 8 } }
],
12: [
{ name: "nameIndex", type: "ushort" },
{ name: "descriptorIndex", type: "ushort" }
],
1: [
{ name: "string", type: "string", typeArgs: { countType: "ushort" } }
],
15: [
{ name: "referenceKind", type: "byte" },
{ name: "referenceIndex", type: "ushort" }
],
16: [
{ name: "descriptorIndex", type: "ushort" }
],
18: [
{ name: "bootstrapMethodAttrIndex", type: "ushort" },
{ name: "nameAndTypeIndex", type: "ushort" }
]
}});
var fieldInfo = Protocols.extendType(Protocols.datatypes.struct, { fields: [
{ name: "accessFlags", type: "ushort" },
{ name: "nameIndex", type: "ushort" },
{ name: "descriptorIndex", type: "ushort" },
{ name: "attributes", type: "array", typeArgs: { countType: "ushort", type: "attributeInfo" } }
]});
var methodInfo = Protocols.extendType(Protocols.datatypes.struct, { fields: [
{ name: "accessFlags", type: "ushort" },
{ name: "nameIndex", type: "ushort" },
{ name: "descriptorIndex", type: "ushort" },
{ name: "attributes", type: "array", typeArgs: { countType: "ushort", type: "attributeInfo" } }
]});
var attributeInfo = Protocols.extendType(Protocols.datatypes.struct, { fields: [
{ name: "nameIndex", type: "ushort" },
{ name: "length", type: "count", typeArgs: { type: "uint", sizeOf: "info" } },
{ name: "info", type: "attributeType", typeArgs: { compareTo: function(fields) {
fields = fields["super"];
}}},
]});
var attributeType = Protocols.extendType(Protocols.datatypes.switch, { fields: {
"ConstantValue": [
{ name: "constantValueIndex", type: "ushort" }
],
"Code": [
{ name: "maxStack", type: "ushort" },
{ name: "maxLocals", type: "ushort" },
{ name: "code", type: "buffer", typeArgs: { countType: "uint" } },
{ name: "exceptionTable", type: "array", typeArgs: { countType: "ushort",
type: "struct", typeArgs: { fields: [
{ name: "startPC", type: "ushort" },
{ name: "endPC", type: "ushort" },
{ name: "handlerPC", type: "ushort" },
{ name: "catchType", type: "ushort" }
]}}},
{ name: "attributes", type: "array", typeArgs: { countType: "ushort", type: "attributeInfo" } }
],
"StackMapTable": [
{ name: "entries", type: "array", typeArgs: { countType: "ushort", type: "struct", typeArgs: { fields: [
{ name: "frameType", type: "ubyte" },
{ name: "stackMapFrame", type: "range", typeArgs: { compareTo: "frameType", fields: [
{ from: 0, to: 63, fields: [] },
{ from: 64, to: 127, fields: [
{ name: "stack", type: "verificationTypeInfo" }
]},
{ from: 247, to: 247, fields: [
{ name: "offsetDelta", type: "ushort" },
{ name: "stack", type: "verificationTypeInfo" }
]},
{ from: 248, to: 250, fields: [
{ name: "offsetDelta", type: "ushort" }
]},
{ from: 251, to: 251, fields: [
{ name: "offsetDelta", type: "ushort" }
]},
{ from: 252, to: 254, fields: [
{ name: "offsetDelta", type: "ushort" },
{ name: "locals", type: "array", typeArgs: { count: function(fields) {
return fields["super"]["frameType"] - 251;
}, type: "verificationTypeInfo" }}
]},
{ from: 255, to : 255, fields: [
{ name: "offsetDelta", type: "ushort" },
{ name: "locals", type: "array", typeArgs: { countType: "ushort", type: "verificationTypeInfo" } },
{ name: "stack", type: "array", typeArgs: { countType: "ushort", type: "verificationTypeInfo" } },
]},
]}}, // TODO : Anonymous types
]}}}
],
"Exceptions": [
{ name: "exceptionIndexTable", type: "array", typeArgs: { countType: "ushort", type: "ushort" } }
],
"InnerClasses": [
{ name: "classes", type: "array", typeArgs: { countType: "ushort",
type: "struct", typeArgs: { fields: [
{ name: "innerClassInfoIndex", type: "ushort" },
{ name: "outerClassInfoIndex", type: "ushort" },
{ name: "innerNameIndex", type: "ushort" },
{ name: "innerClassAccessFlags", type: "ushort" }
]}}}
],
"EnclosingMethod": [
{ name: "classIndex", type: "ushort" },
{ name: "methodIndex", type: "ushort" }
],
"Synthetic": [],
"Signature": [
{ name: "signatureIndex", type: "ushort" }
],
"SourceFile": [
{ name: "sourceFileIndex", type: "ushort" }
],
"SourceDebugExtension": [
{ name: "debugExtension", type: "buffer", typeArgs: { count: "length" } }
],
"LineNumberTable": [
{ name: "lineNumberTable", type: "array", typeArgs: { countType: "ushort",
type: "struct", typeArgs: { fields: [
{ name: "startPC", type: "ushort" },
{ name: "lineNumber", type :"ushort" }
]}}}
],
"LocalVariableTable": [
{ name: "localVariableTable", type: "array", typeArgs: { countType: "ushort", // TODO : A bit of syntastic suggar for this would do it a lot of good.
type: "struct", typeArgs: { fields: [
{ name: "startPC", type: "ushort" },
{ name: "length", type: "ushort" },
{ name: "nameIndex", type: "ushort" },
{ name: "descriptorIndex", type: "ushort" },
{ name: "index", type: "ushort" }
]}}}
],
"LocalVariableTypeTable": [
{ name: "localVariableTypeTable", type: "array", typeArgs: { countType: "ushort", // TODO : A bit of syntastic suggar for this would do it a lot of good.
type: "struct", typeArgs: { fields: [
{ name: "startPC", type: "ushort" },
{ name: "length", type: "ushort" },
{ name: "nameIndex", type: "ushort" },
{ name: "signatureIndex", type: "ushort" },
{ name: "index", type: "ushort" }
]}}}
],
"Deprecated": [],
"RuntimeVisibleAnnotations": [
],
"RuntimeInvisibleAnnotations": [
],
"RuntimeVisibleParameterAnnotations": [
],
"RuntimeInvisibleParameterAnnotations": [
],
"AnnotationDefault": [
],
"BootstrapMethods": [
]
}, default: { type: "buffer", typeArgs: { count: "length" } }
});
var verificationTypeInfo = Protocols.extendType(Protocols.datatypes.struct, {
fields: [
{ name: "tag", type: "ubyte" },
{ anon: true, name: "info", type: "range", typeArgs: [
{ from: 0, to: 6, fields: [] },
{ from: 7, to: 7, fields: [{ name: "cpoolIndex", type: "ushort" }]},
{ from: 8, to: 8, fields: [{ name: "offset", type: "ushort" }]},
]}
]
});
var javaproto = Protocols.create();
javaproto.addType("constantPoolInfo", constantPoolInfo);
javaproto.addType("constantPool", constantPool);
javaproto.addType("fieldInfo", fieldInfo);
javaproto.addType("methodInfo", methodInfo);
javaproto.addType("attributeInfo", attributeInfo);
javaproto.addType("attributeType", attributeType);
javaproto.addType("javaclass", classFile);
var argv = yargs.usage("Usage : $0 [classfile]").demand(1).argv;
fs.createReadStream(argv._[0]).pipe(javaproto.createReader({ type: "javaclass" })).on('packet', function(data) {
console.log("Magic Number : " + data.magic.toString('hex'));
console.log("Version : " + data.majorVersion + "." + data.minorVersion);
console.log("Constant Pool Entries : " + data.constantPool.length);
console.log("Access Flags : " + getAccessFlags(data.accessFlags));
console.log("Class Name : " + data.constantPool[data.constantPool[data.thisClass - 1].info.nameIndex - 1].info.string);
console.log("Parent Class : " + data.constantPool[data.constantPool[data.superClass - 1].info.nameIndex - 1].info.string); // info.string... Need to implement anon type soon.
})
.on('error', function(err) {
console.log(err.fieldName);
console.log(err.stack);
});
function getAccessFlags(accessFlags) {
var flagStr = "";
function append(str) {
if (flagStr == "")
flagStr += str;
else
flagStr += " " + str;
}
if (accessFlags & 0x0001)
append("public");
if (accessFlags & 0x0010)
append("final");
if (accessFlags & 0x0020)
append("super");
if (accessFlags & 0x0200)
append("interface");
if (accessFlags & 0x0400)
append("abstract");
if (accessFlags & 0x1000)
append("synthetic");
if (accessFlags & 0x2000)
append("annotation");
if (accessFlags & 0x4000)
append("enum");
return flagStr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment