Skip to content

Commit

Permalink
Added #472 as External Plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Konloch committed Sep 29, 2024
1 parent d0b075c commit 62ade7c
Showing 1 changed file with 342 additions and 0 deletions.
342 changes: 342 additions & 0 deletions plugins/java/ClassParser.java
Original file line number Diff line number Diff line change
@@ -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<ClassNode> 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<String> cpList = new ArrayList<String>();

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<String> 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;
}
}

0 comments on commit 62ade7c

Please sign in to comment.