package amidst.bytedata; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.util.Stack; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ByteClass { public static class AccessFlags { public static int PUBLIC = 0x01, PRIVATE = 0x02, PROTECTED = 0x04, STATIC = 0x08, FINAL = 0x10, VOLATILE = 0x40, TRANSIENT = 0x80; } /*ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package. ACC_PRIVATE 0x0002 Declared private; usable only within the defining class. ACC_PROTECTED 0x0004 Declared protected; may be accessed within subclasses. ACC_STATIC 0x0008 Declared static. ACC_FINAL 0x0010 Declared final; no further assignment after initialization. ACC_VOLATILE 0x0040 Declared volatile; cannot be cached. ACC_TRANSIENT 0x0080 Declared transient; not written or read by a persistent object manager. */ public class Field { public int accessFlags; public Field() { } }; private byte[] data; private boolean isValidClass; public int minorVersion; public int majorVersion; private int cpSize; private DataInputStream stream; private ClassConstant<?>[] constants; private int[] constantTypes; private String name; public int accessFlags; private Vector<ClassConstant<Integer>> stringIndices; private Vector<ReferenceIndex> methodIndices; private Vector<String[]> methods, properties, constructors; private Vector<Float> floatConstants; private Vector<Long> longConstants; private Vector<String> utfConstants; public Field[] fields; public int methodCount; // (Method count includes constructors) public int constructorCount; public ByteClass(String name, byte[] classData) { this.name = name; methods = new Vector<String[]>(); properties = new Vector<String[]>(); constructors = new Vector<String[]>(); floatConstants = new Vector<Float>(); longConstants = new Vector<Long>(); methodIndices = new Vector<ReferenceIndex>(); stringIndices = new Vector<ClassConstant<Integer>>(); utfConstants = new Vector<String>(); try { data = classData; stream = new DataInputStream(new ByteArrayInputStream(data)); isValidClass = stream.readInt() == 0xCAFEBABE; if (isValidClass) { minorVersion = stream.readUnsignedShort(); majorVersion = stream.readUnsignedShort(); cpSize = stream.readUnsignedShort(); cpSize--; constants = new ClassConstant<?>[cpSize]; constantTypes = new int[cpSize]; long offset = 10; for (int q = 0; q < cpSize; q++) { byte tag = stream.readByte(); offset++; constantTypes[q] = tag; switch (tag) { case 1: //String int len = stream.readUnsignedShort(); String strVal = ""; for (int i = 0; i < len; i++) strVal += (char)stream.readByte(); constants[q] = new ClassConstant<String>(tag, offset, strVal); utfConstants.add(strVal); offset += 2 + len; break; case 3: //Int constants[q] = new ClassConstant<Integer>(tag, offset, stream.readInt()); offset += 4; break; case 4: //Float float cFloat = stream.readFloat(); constants[q] = new ClassConstant<Float>(tag, offset, cFloat); floatConstants.add(cFloat); offset += 4; break; case 5: //Long long cLong = stream.readLong(); constants[q] = new ClassConstant<Long>(tag, offset, cLong); longConstants.add(cLong); offset += 8; q++; break; case 6: //Double constants[q] = new ClassConstant<Double>(tag, offset, stream.readDouble()); offset += 8; q++; break; case 7: //Class reference constants[q] = new ClassConstant<Integer>(tag, offset, stream.readUnsignedShort()); offset += 2; break; case 8: //String reference ClassConstant<Integer> strRef = new ClassConstant<Integer>(tag, offset, stream.readUnsignedShort()); constants[q] = strRef; stringIndices.add(strRef); offset += 2; break; case 9: //Field reference constants[q] = new ClassConstant<ReferenceIndex>(tag, offset, new ReferenceIndex(stream.readUnsignedShort(), stream.readUnsignedShort())); offset += 4; break; case 10: //Method reference constants[q] = new ClassConstant<ReferenceIndex>(tag, offset, new ReferenceIndex(stream.readUnsignedShort(), stream.readUnsignedShort())); offset += 4; break; case 11: //Interface method reference constants[q] = new ClassConstant<ReferenceIndex>(tag, offset, new ReferenceIndex(stream.readUnsignedShort(), stream.readUnsignedShort())); offset += 4; break; case 12: //Name and type descriptor constants[q] = new ClassConstant<ReferenceIndex>(tag, offset, new ReferenceIndex(stream.readUnsignedShort(), stream.readUnsignedShort())); offset += 4; break; } } //Access Flags accessFlags = stream.readUnsignedShort(); //This class stream.skip(2); //Super class stream.skip(2); //Interfaces int iCount = stream.readUnsignedShort(); stream.skip(iCount*2); //Fields fields = new Field[stream.readUnsignedShort()]; for (int i = 0; i < fields.length; i++) { fields[i] = new Field(); fields[i].accessFlags = stream.readUnsignedShort(); stream.skip(4); int attributeInfoCount = stream.readUnsignedShort(); for (int q = 0; q < attributeInfoCount; q++) { stream.skip(2); int attributeCount = stream.readInt(); for (int z = 0; z < attributeCount; z++) stream.skip(1); } } //Methods methodCount = stream.readUnsignedShort(); for (int i = 0; i < methodCount; i++) { stream.skip(2); int nameIndex = stream.readUnsignedShort(); methodIndices.add(new ReferenceIndex(nameIndex, stream.readUnsignedShort())); if (((String)constants[nameIndex - 1].get()).contains("<init>")) constructorCount++; int attributeInfoCount = stream.readUnsignedShort(); for (int q = 0; q < attributeInfoCount; q++) { stream.skip(2); int attributeCount = stream.readInt(); for (int z = 0; z < attributeCount; z++) stream.skip(1); } } //Attributes stream.close(); } } catch (Exception e) { e.printStackTrace(); System.exit(0); } } public boolean searchForString(String str) { for (ClassConstant<Integer> i : stringIndices) { if (((String)constants[i.get() - 1].get()).contains(str)) return true; } return false; } public boolean searchForUtf(String str) { for (String text : utfConstants) { if (text.equals(str)) return true; } return false; } public byte[] getData() { return data; } @Override public String toString() { return "[ByteClass " + name + "]"; } public String searchByReturnType(String type) { for (ReferenceIndex ref : methodIndices) { String refType = (String)constants[ref.val2-1].get(); //Log.i("L" + type + " = " + refType); if (("L" + type + ";").equals(refType.substring(refType.indexOf(')') + 1))) return (String)constants[ref.val1-1].get(); } return null; } public void addMethod(String method, String name) { methods.add(new String[] {method, name}); } public String getClassName() { return name; } public Vector<String[]> getMethods() { return methods; } public void addProperty(String property, String name) { properties.add(new String[] { property, name}); } public Vector<String[]> getProperties() { return properties; } public void addConstructor(String constructor, String name) { constructors.add(new String[] {constructor, name}); } public Vector<String[]> getConstructors() { return constructors; } public boolean isInterface() { return (accessFlags & 0x0200) == 0x0200; } public boolean isFinal() { return (accessFlags & 0x0010) == 0x0010; } public String getArguementsForConstructor(int ID) { int i = 0; for (ReferenceIndex ref : methodIndices) { String name = (String)constants[ref.val1-1].get(); if (name.equals("<init>")) { if (i==ID) { String args = (String)constants[ref.val2-1].get(); return toArguementString(args); } i++; } } return ""; } public static String toArguementString(String eArgs) { String[] args = readArguements(eArgs); String out = "("; for (int i = 0; i < args.length ;i++) { out += args[i] + ((i == args.length - 1)?"":","); } out += ")"; return out; } public static String[] readArguements(String eArgs) { //Log.i(eArgs); String args = eArgs.substring(1); String[] argSplit = args.split("\\)"); args = argSplit[0]; Stack<String> argStack = new Stack<String>(); Pattern argRegex = Pattern.compile("([\\[]+)?([BCDFIJSZ]|L[^;]+)"); Pattern objectRegex = Pattern.compile("^([\\[]+)?[LBCDFIJSZ]"); Matcher matcher = argRegex.matcher(args); while (matcher.find()) { String arg =args.substring(matcher.start(), matcher.end()); Matcher objectMatcher = objectRegex.matcher(arg); if (objectMatcher.find()) { String replaceWith = ""; switch (arg.charAt(objectMatcher.end()-1)) { case 'B': replaceWith = "byte"; break; case 'C': replaceWith = "char"; break; case 'D': replaceWith = "double"; break; case 'F': replaceWith = "float"; break; case 'I': replaceWith = "int"; break; case 'J': replaceWith = "long"; break; case 'S': replaceWith = "short"; break; case 'Z': replaceWith = "boolean"; break; } arg = arg.substring(0, Math.max(0, objectMatcher.end()-1)) + replaceWith + arg.substring(Math.min(objectMatcher.end(), arg.length())); } argStack.push(arg); } String[] argArray = new String[argStack.size()]; for (int i = 0; i < argArray.length; i++) { argArray[argArray.length - 1 - i] = argStack.pop(); } return argArray; } public boolean searchForFloat(float f) { for (Float cFloat : floatConstants) { if (cFloat.floatValue() == f) { return true; } } return false; } public boolean searchForLong(long l) { for (Long cLong : longConstants) { if (cLong.longValue() == l) { return true; } } return false; } }