Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plugin ClassParser #472

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package the.bytecode.club.bytecodeviewer.classparser;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;

public class ClassFileParser {

private final byte[] classBytes;
private final ConstantParser cpParser;

public ClassFileParser(byte[] classBytes){
this.classBytes = classBytes;
cpParser = new ConstantParser(classBytes, parseConstantPoolCount());
}

public String parseMagicValue(){
try(DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(classBytes))) {
int magicValue = dataInputStream.readInt();
return "0x" + Integer.toHexString(magicValue).toUpperCase();
} catch (IOException e) {
e.printStackTrace();
}
return "none";
}

public ClassVersion parseVersionClass(){
try(DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(classBytes))) {
dataInputStream.skipBytes(4);
int minor = dataInputStream.readUnsignedShort();
int major = dataInputStream.readUnsignedShort();
return ClassVersion.check(major, minor);
} catch (IOException e) {
e.printStackTrace();
}
return ClassVersion.UNKNOWN;
}

public Date parseClassModificationDate() {
try (DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(classBytes))) {
dataInputStream.skipBytes(8);
long modificationTime = dataInputStream.readInt() * 1000L;
return new Date(modificationTime);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

public int parseConstantPoolCount(){
try(DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(classBytes))) {
dataInputStream.skipBytes(8);
return dataInputStream.readUnsignedShort();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}

public String getHash(String algorithm) {
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(classBytes);
byte[] digest = md.digest();
return convertToHex(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "null";
}

private String convertToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}

public ConstantParser getConstantPool(){
return cpParser;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package the.bytecode.club.bytecodeviewer.classparser;

public enum ClassVersion {

UNKNOWN(0, 0),

JAVA_1_1(45, 3),
JAVA_1_2(46, 0),
JAVA_1_3(47, 0),
JAVA_1_4(48, 0),
JAVA_5(49, 0),
JAVA_6(50, 0),
JAVA_7(51, 0),
JAVA_8(52, 0),
JAVA_9(53, 0),
JAVA_10(54, 0),
JAVA_11(55, 0),
JAVA_12(56, 0),
JAVA_13(57, 0),
JAVA_14(58, 0),
JAVA_15(59, 0),
JAVA_16(60, 0),
JAVA_17(61, 0);

public final int major;
public final int minor;

ClassVersion(int major, int minor) {
this.major = major;
this.minor = minor;
}

public static ClassVersion check(int major, int minor) {
for (ClassVersion v : ClassVersion.values()) {
if (v.major == major && v.minor == minor) {
return v;
}
}
return UNKNOWN;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package the.bytecode.club.bytecodeviewer.classparser;

import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class ConstantParser {

private final byte[] classBytes;
private final int constantPoolCount;

private final List<String> cpList = new ArrayList<>();

public ConstantParser(byte[] classBytes, int constantPoolCount){
this.classBytes = classBytes;
this.constantPoolCount = constantPoolCount;
}

public void parseConstantPool(){
ByteBuffer buffer = ByteBuffer.wrap(classBytes);
buffer.position(10);

for (int i = 1; i < constantPoolCount; i++) {
int tag = buffer.get() & 0xFF;
switch(tag){
case ConstantType.CONSTANT_Utf8:
int length = buffer.getShort() & 0xFFFF;
if (buffer.remaining() < length) {
throw new BufferUnderflowException();
}
byte[] bytes = new byte[length];
buffer.get(bytes);
String string = new String(bytes);
cpList.add("[" + i + "] CONSTANT_Utf8: " + string);
break;
case ConstantType.CONSTANT_Integer:
int value = buffer.getInt();
cpList.add("[" + i + "] CONSTANT_Integer: " + value);
break;
case ConstantType.CONSTANT_Float:
float floatValue = buffer.getFloat();
cpList.add("[" + i + "] CONSTANT_Float: " + floatValue);
break;
case ConstantType.CONSTANT_Long:
long longValue = buffer.getLong();
cpList.add("[" + i + "] CONSTANT_Long: " + longValue);
i++;
break;
case ConstantType.CONSTANT_Double:
double doubleValue = buffer.getDouble();
cpList.add("[" + i + "] CONSTANT_Double: " + doubleValue);
i++;
break;
case ConstantType.CONSTANT_Class:
int nameIndex = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_Class: #" + nameIndex);
break;
case ConstantType.CONSTANT_String:
int stringIndex = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_String: #" + stringIndex);
break;
case ConstantType.CONSTANT_Fieldref:
int classIndex = buffer.getShort() & 0xFFFF;
int nameAndTypeIndex = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_Fieldref: #" + classIndex + ".#" + nameAndTypeIndex);
break;
case ConstantType.CONSTANT_Methodref:
int classIndex1 = buffer.getShort() & 0xFFFF;
int nameAndTypeIndex1 = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_Methodref: #" + classIndex1 + ".#" + nameAndTypeIndex1);
break;
case ConstantType.CONSTANT_InterfaceMethodref:
int classIndex2 = buffer.getShort() & 0xFFFF;
int nameAndTypeIndex2 = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_InterfaceMethodref: #" + classIndex2 + ".#" + nameAndTypeIndex2);
break;
case ConstantType.CONSTANT_NameAndType:
int nameIndex1 = buffer.getShort() & 0xFFFF;
int descriptorIndex = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_NameAndType: #" + nameIndex1 + ":#" + descriptorIndex);
break;
case ConstantType.CONSTANT_MethodHandle:
int referenceKind = buffer.get() & 0xFF;
int referenceIndex = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_MethodHandle: " + referenceKind + ":#" + referenceIndex);
break;
case ConstantType.CONSTANT_MethodType:
int descriptorIndex1 = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_MethodType: #" + descriptorIndex1);
break;
case ConstantType.CONSTANT_InvokeDynamic:
int bootstrapMethodAttrIndex = buffer.getShort() & 0xFFFF;
int nameAndTypeIndex3 = buffer.getShort() & 0xFFFF;
cpList.add("[" + i + "] CONSTANT_InvokeDynamic: #" + bootstrapMethodAttrIndex + ":#" + nameAndTypeIndex3);
break;
default:
throw new IllegalArgumentException("Unknown constant pool tag " + tag);
}
}
}

public List<String> getCpList() {
return cpList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package the.bytecode.club.bytecodeviewer.classparser;

public interface ConstantType {

byte CONSTANT_Utf8 = 1;
byte CONSTANT_Class = 7;
byte CONSTANT_Fieldref = 9;
byte CONSTANT_Methodref = 10;
byte CONSTANT_InterfaceMethodref = 11;
byte CONSTANT_String = 8;
byte CONSTANT_Integer = 3;
byte CONSTANT_Float = 4;
byte CONSTANT_Long = 5;
byte CONSTANT_Double = 6;
byte CONSTANT_NameAndType = 12;
byte CONSTANT_MethodHandle = 15;
byte CONSTANT_MethodType = 16;
byte CONSTANT_InvokeDynamic = 18;
}