Last active
August 29, 2015 14:16
-
-
Save roblabla/396fac8159eba449ae55 to your computer and use it in GitHub Desktop.
jarfile reader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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