70 likes | 314 Views
Java Decompiler. Decompiler. Go from executable bytecode to the source code Bytecode has an M->N correspondence Partial structure destruction Executable has worse! Not all bytecode has corresponding source code. int flags = source.readUnsignedShort(); output.setFlags(flags);
E N D
Decompiler • Go from executable bytecode to the source code • Bytecode has an M->N correspondence • Partial structure destruction • Executable has worse! • Not all bytecode has corresponding source code
int flags = source.readUnsignedShort(); output.setFlags(flags); int this_id = source.readUnsignedShort(); String name = output.getClassName(this_id); output.setName(name); int super_id = source.readUnsignedShort(); String superName = null; if (super_id != 0) superName = output.getClassName(super_id); output.setSuperClass(superName); int count = source.readUnsignedShort(); String interfaces[] = new String[count]; for (int i=0;i<count;i++) { int id = source.readUnsignedShort(); interfaces[i] = output.getClassName(id); } output.setInterfaces(interfaces); current = ClassLevel.METADATA; Class Parsing
private String FieldType() { switch (peek()) { case 'B': case 'C': case 'D': case 'F': case 'I': case 'S': case 'Z': return BaseType(); case 'L': return ClassType(); case '[': return ArrayType(); } throw new ParseException(); } private String ClassType() { StringBuilder sb = new StringBuilder(); if (next() != 'L') throw new ParseException(); while (peek() != ';') { char c = next(); sb.append(c == '/' ? '.' : c); } next(); // consume ; return sb.toString(); } Signature Parsing
while (i < bytecode.length) { int index = i++; BytecodeInstruction expr; switch(bytecode[index] & 0xff) { case 0x02: //iconst_m1 case 0x03: //iconst_0 expr = new LoadConstantInstruction(int.class, bytecode[index]-0x03); break; case 0x99: //ifeq expr = new IfInstruction("eq",index+((bytecode[i++] << 8 & 0xff) | (bytecode[i++] & 0xff)), false); break; case 0xb6: //invokevirtual expr = new InvokeInstruction(info, (bytecode[i++] << 8 & 0xff) | (bytecode[i++] & 0xff), false, true); break; default: Logger.error("Illegal opcode: "+Integer.toHexString(bytecode[index] & 0xff)); expr = null; } if (expr != null) { CodeGraph.Node node = new Node(expr); if (lastNode != null) { if (last.usesNext()) lastNode.addChild(node); } lastNode = node; last = expr; map.put(index, node); } } Bytecode detection
public final class OptClassLoader extends ClassLoader { public final Class<?> loadClass(String name, boolean resolve) { Annotation[] annotations = result.getAnnotations(); for (Annotation a : annotations) { Class<? extends Annotation> c = a.annotationType(); if (c.getName().equals("util.Option")) { addOption((Option)a); } else if (c.getName().equals("util.Options")) { Option[] defs = (Option[])c.getDeclaredMethod("value").invoke(a); for (Option o : defs) addOption(o); } } return result; } private void addOption(Option o) { String name = o.name(); Class<?> type = o.type(); String def = o.def(); OptionManager.addOption(name,type,def); } } Runtime annotation processing
File: abstract class Foobar implements java.io.Serializable { public abstract <T, E extends Throwable> T foobar(T bar) throws E; } Output: /* FILE: test.java */ abstract class Foobar implements java.io.Serializable { Foobar() { 1012673: load java.lang.Object, 0-> { 7901508 } 7901508: invokenonvirtual java.lang.Object.<init>-> { 25771774 } 25771774: return (none)-> { } } public abstract <T extends java.lang.Object, E extends java.lang.Throwable> T foobar(T {VAR0}) throws E; } Sample output