From 62ade7c877c7482a6359a28bc50aeec13fe99b54 Mon Sep 17 00:00:00 2001 From: Konloch Date: Sun, 29 Sep 2024 09:27:18 -0600 Subject: [PATCH] Added #472 as External Plugin --- plugins/java/ClassParser.java | 342 ++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 plugins/java/ClassParser.java diff --git a/plugins/java/ClassParser.java b/plugins/java/ClassParser.java new file mode 100644 index 000000000..66c8c06b4 --- /dev/null +++ b/plugins/java/ClassParser.java @@ -0,0 +1,342 @@ +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; +import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.api.Plugin; +import the.bytecode.club.bytecodeviewer.api.PluginConsole; +import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Class Parser + * + * @author Damir37 + */ +public class ClassParser extends Plugin +{ + + private PluginConsole pluginConsole; + + @Override + public void execute(List list) + { + + ClassNode c = BytecodeViewer.getCurrentlyOpenedClassNode(); + + ClassFileParser classFileParser = new ClassFileParser(classNodeToByte(c)); + + pluginConsole = new PluginConsole("ClassParser"); + pluginConsole.setVisible(true); + + print("Parsing class: " + c.name + ".class"); + print("MAGIC VALUE: " + classFileParser.parseMagicValue()); + print("Class version: " + classFileParser.parseVersionClass()); + print("Constant pool count: " + classFileParser.parseConstantPoolCount() + + " If not all constants were parsed, most likely the constant is not used in the bytecode."); + print("Then use the javap utility to view the constant pool of a class file."); + print("Last modified class: " + classFileParser.parseClassModificationDate()); + print("Hash sum class md5: " + classFileParser.getHash("MD5")); + print("Hash sum class sha1: " + classFileParser.getHash("SHA-1")); + print("Hash sum class sha256: " + classFileParser.getHash("SHA-256")); + print("Hash sum class sha512: " + classFileParser.getHash("SHA-512")); + print("Constant pool ->"); + + classFileParser.getConstantPool().parseConstantPool(); + + if (classFileParser.getConstantPool().getCpList() != null && !classFileParser.getConstantPool().getCpList().isEmpty()) + { + for (String s : classFileParser.getConstantPool().getCpList()) + { + print(s); + } + } + } + + private byte[] classNodeToByte(ClassNode classNode) + { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + classNode.accept(cw); + return cw.toByteArray(); + } + + public void print(String text) + { + pluginConsole.appendText(text); + pluginConsole.repaint(); + } + + private static class ClassFileParser + { + private final ByteBuffer buffer; + private final ConstantParser cpParser; + + public ClassFileParser(byte[] classBytes) + { + this.buffer = ByteBuffer.wrap(classBytes); + cpParser = new ConstantParser(buffer, parseConstantPoolCount()); + } + + public String parseMagicValue() + { + buffer.position(0); + int magicValue = buffer.getInt(); + return "0x" + Integer.toHexString(magicValue).toUpperCase(); + } + + public ClassVersion parseVersionClass() + { + buffer.position(4); + int minor = buffer.getShort() & 0xFFFF; + int major = buffer.getShort() & 0xFFFF; + return ClassVersion.check(major, minor); + } + + public Date parseClassModificationDate() + { + buffer.position(8); + long modificationTime = buffer.getInt() & 0xFFFFFFFFL; + return new Date(modificationTime * 1000L); + } + + public int parseConstantPoolCount() + { + buffer.position(8); + return buffer.getShort() & 0xFFFF; + } + + public String getHash(String algorithm) + { + try + { + MessageDigest md = MessageDigest.getInstance(algorithm); + md.update(buffer.array()); + 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; + } + } + + private 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), + AVA_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), + JAVA_18(62, 0), + JAVA_19(63, 0), + JAVA_20(64, 0), + JAVA_21(65, 0), + JAVA_22(66, 0), + JAVA_23(67, 0), + JAVA_24(68, 0), + JAVA_25(69, 0), + JAVA_26(70, 0), + JAVA_27(71, 0), + JAVA_28(72, 0), + JAVA_29(73, 0), + JAVA_30(74, 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; + } + } + + private static class ConstantParser + { + private final ByteBuffer buffer; + private final int constantPoolCount; + private final List cpList = new ArrayList(); + + public ConstantParser(ByteBuffer buffer, int constantPoolCount) + { + this.buffer = buffer; + this.constantPoolCount = constantPoolCount; + } + + public void parseConstantPool() + { + 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; + 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: + case ConstantType.CONSTANT_Methodref: + case ConstantType.CONSTANT_InterfaceMethodref: + int classIndex = buffer.getShort() & 0xFFFF; + int nameAndTypeIndex = buffer.getShort() & 0xFFFF; + cpList.add("[" + i + "] CONSTANT_" + getRefTypeName(tag) + ": #" + classIndex + ".#" + nameAndTypeIndex); + 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); + } + } + } + + private String getRefTypeName(int tag) + { + switch (tag) + { + case ConstantType.CONSTANT_Fieldref: + return "Fieldref"; + case ConstantType.CONSTANT_Methodref: + return "Methodref"; + case ConstantType.CONSTANT_InterfaceMethodref: + return "InterfaceMethodref"; + default: + return "Unknown"; + } + } + + public List getCpList() + { + return cpList; + } + } + + private interface ConstantType + { + public static final byte CONSTANT_Utf8 = 1; + public static final byte CONSTANT_Class = 7; + public static final byte CONSTANT_Fieldref = 9; + public static final byte CONSTANT_Methodref = 10; + public static final byte CONSTANT_InterfaceMethodref = 11; + public static final byte CONSTANT_String = 8; + public static final byte CONSTANT_Integer = 3; + public static final byte CONSTANT_Float = 4; + public static final byte CONSTANT_Long = 5; + public static final byte CONSTANT_Double = 6; + public static final byte CONSTANT_NameAndType = 12; + public static final byte CONSTANT_MethodHandle = 15; + public static final byte CONSTANT_MethodType = 16; + public static final byte CONSTANT_InvokeDynamic = 18; + } +}