-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
342 additions
and
0 deletions.
There are no files selected for viewing
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
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; | ||
} | ||
} |