diff --git a/src/main/java/info/sigterm/deob/Deob.java b/src/main/java/info/sigterm/deob/Deob.java index 65f2a1a451..629dd26b34 100644 --- a/src/main/java/info/sigterm/deob/Deob.java +++ b/src/main/java/info/sigterm/deob/Deob.java @@ -52,10 +52,6 @@ public class Deob // remove jump obfuscation new Jumps().run(group); - //group.buildClassGraph(); - //group.buildInstructionGraph(); - //group.buildCallGraph(); - saveJar(group, args[1]); } diff --git a/src/main/java/info/sigterm/deob/Method.java b/src/main/java/info/sigterm/deob/Method.java index fdb4f4805d..1b8f54194b 100644 --- a/src/main/java/info/sigterm/deob/Method.java +++ b/src/main/java/info/sigterm/deob/Method.java @@ -11,6 +11,7 @@ import info.sigterm.deob.execution.Execution; import info.sigterm.deob.execution.Frame; import info.sigterm.deob.execution.InstructionContext; import info.sigterm.deob.pool.NameAndType; +import info.sigterm.deob.pool.PoolEntry; import info.sigterm.deob.signature.Signature; import java.io.DataInputStream; @@ -72,11 +73,13 @@ public class Method Method caller = n.from; // find frames on the caller + boolean found = false; for (Frame f : execution.processedFrames) if (f.getMethod() == caller) for (InstructionContext ins : f.getInstructions()) if (ins.getInstruction() == n.ins) // this instruction invokes the function we're removing a parameter from { + found = true; if (done.contains(ins.getInstruction())) continue; @@ -88,8 +91,54 @@ public class Method done.add(ins.getInstruction()); } + if (found == false) + { + System.err.println("Method " + caller.getName() + " in " + caller.getMethods().getClassFile().getName() + " calls " + this.getName() + " in " + this.getMethods().getClassFile().getName() + ", but was unable to find any execution frame doing this"); + assert false; + } } + // this double checks that all calls to this have been located + for (ClassFile cf : methods.getClassFile().getGroup().getClasses()) + for (Method m : cf.getMethods().getMethods()) + { + Code c = m.getCode(); + if (c == null) + continue; + + for (Instruction i : c.getInstructions().getInstructions()) + { + if (i instanceof InvokeInstruction) + { + InvokeInstruction ii = (InvokeInstruction) i; + PoolEntry pool = ii.getMethod(); + + if (pool instanceof info.sigterm.deob.pool.Method) + { + info.sigterm.deob.pool.Method pm = (info.sigterm.deob.pool.Method) pool; + + if (pm.getClassEntry().getName().equals(this.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(this.getNameAndType()) && !done.contains(i)) + { + // for some reason this wasn't removed above? + System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution"); + //assert false; + } + } + else if (pool instanceof info.sigterm.deob.pool.InterfaceMethod) + { + info.sigterm.deob.pool.InterfaceMethod pm = (info.sigterm.deob.pool.InterfaceMethod) pool; + + if (pm.getClassEntry().getName().equals(this.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(this.getNameAndType()) && !done.contains(i)) + { + // for some reason this wasn't removed above? + System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution"); + //assert false; + } + } + } + } + } + // adjust lvt indexes to get rid of idx in the method for (Instruction ins : new ArrayList<>(getCode().getInstructions().getInstructions())) { diff --git a/src/main/java/info/sigterm/deob/attributes/code/instruction/types/InvokeInstruction.java b/src/main/java/info/sigterm/deob/attributes/code/instruction/types/InvokeInstruction.java index 9bc46f5fa7..4a70481b4c 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instruction/types/InvokeInstruction.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instruction/types/InvokeInstruction.java @@ -1,6 +1,11 @@ package info.sigterm.deob.attributes.code.instruction.types; +import info.sigterm.deob.Method; +import info.sigterm.deob.pool.PoolEntry; + public interface InvokeInstruction { public void removeParameter(int idx); + + public PoolEntry getMethod(); } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/AThrow.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/AThrow.java index d18f7853b0..96fb451720 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/AThrow.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/AThrow.java @@ -6,7 +6,11 @@ import info.sigterm.deob.attributes.code.Instructions; import info.sigterm.deob.execution.Frame; import info.sigterm.deob.execution.InstructionContext; import info.sigterm.deob.execution.Stack; +import info.sigterm.deob.execution.StackContext; +import info.sigterm.deob.execution.Type; + import java.io.IOException; +import java.util.List; public class AThrow extends Instruction { @@ -21,13 +25,38 @@ public class AThrow extends Instruction InstructionContext ins = new InstructionContext(this, frame); Stack stack = frame.getStack(); - // XXX this actually clears the stack and puts only the value on, after jumping to the handler - //StackContext value = stack.pop(); - //ins.pop(value); + // get exception + StackContext exception = stack.pop(); + ins.pop(exception); + // Clear stack + while (stack.getSize() > 0) + { + StackContext value = stack.pop(); + ins.pop(value); + } + + // push exception back + exception = new StackContext(ins, exception.getType()); + stack.push(exception); + + // jump to instruction handlers that can catch exceptions here + for (info.sigterm.deob.attributes.code.Exception e : this.getInstructions().getCode().getExceptions().getExceptions()) + { + Instruction start = e.getStart(), + end = e.getEnd(); + + // [start, end) + if (this.getPc() >= start.getPc() && this.getPc() < end.getPc()) + { + Frame f = frame.dup(); + f.jumpAbsolute(e.getHandler().getPc()); + } + } + frame.addInstructionContext(ins); - frame.throwException(null);//value.getType()); + frame.stop(); } @Override diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/CheckCast.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/CheckCast.java index 477a315269..fed814ee04 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/CheckCast.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/CheckCast.java @@ -36,17 +36,35 @@ public class CheckCast extends Instruction @Override public void execute(Frame frame) - { - Frame other = frame.dup(); - Stack stack = other.getStack(); - - InstructionContext ins = new InstructionContext(this, other); - - StackContext what = stack.pop(); - - ins.pop(what); - - other.throwException(new Type("java.lang.ClassCastException")); + { + // jump to instruction handlers that can catch exceptions here + for (info.sigterm.deob.attributes.code.Exception e : this.getInstructions().getCode().getExceptions().getExceptions()) + { + Instruction start = e.getStart(), + end = e.getEnd(); + + // [start, end) + if (this.getPc() >= start.getPc() && this.getPc() < end.getPc()) + { + Frame f = frame.dup(); + Stack stack = f.getStack(); + + InstructionContext ins = new InstructionContext(this, f); + + while (stack.getSize() > 0) + { + StackContext what = stack.pop(); + ins.pop(what); + } + + // push exception back + StackContext exception = new StackContext(ins, new Type("java/lang/Exception")); + stack.push(exception); + + f.addInstructionContext(ins); + + f.jumpAbsolute(e.getHandler().getPc()); + } + } } - } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java index a88ca3177b..c397144d57 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java @@ -56,6 +56,8 @@ public class If0 extends Instruction implements JumpingInstruction ins.pop(one); + frame.addInstructionContext(ins); + Frame other = frame.dup(); other.jump(offset); } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeInterface.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeInterface.java index 3607f591bd..ea947bf0d4 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeInterface.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeInterface.java @@ -13,6 +13,7 @@ import info.sigterm.deob.execution.Type; import info.sigterm.deob.pool.InterfaceMethod; import info.sigterm.deob.pool.Method; import info.sigterm.deob.pool.NameAndType; +import info.sigterm.deob.pool.PoolEntry; import info.sigterm.deob.signature.Signature; import java.io.DataInputStream; @@ -77,6 +78,8 @@ public class InvokeInterface extends Instruction implements InvokeInstruction StackContext object = stack.pop(); ins.pop(object); + handleExceptions(frame); + if (!method.getNameAndType().isVoid()) { StackContext ctx = new StackContext(ins, new Type(method.getNameAndType().getDescriptor().getReturnValue()).toStackType()); @@ -85,6 +88,32 @@ public class InvokeInterface extends Instruction implements InvokeInstruction frame.addInstructionContext(ins); } + + private void handleExceptions(Frame frame) + { + // jump to instruction handlers that can catch exceptions here + for (info.sigterm.deob.attributes.code.Exception e : this.getInstructions().getCode().getExceptions().getExceptions()) + { + Instruction start = e.getStart(), + end = e.getEnd(); + + // [start, end) + if (this.getPc() >= start.getPc() && this.getPc() < end.getPc()) + { + Frame f = frame.dup(); + Stack stack = f.getStack(); + + while (stack.getSize() > 0) + stack.pop(); + + InstructionContext ins = new InstructionContext(this, f); + StackContext ctx = new StackContext(ins, new Type("java/lang/Exception")); + stack.push(ctx); + + f.jumpAbsolute(e.getHandler().getPc()); + } + } + } @Override public void removeParameter(int idx) @@ -99,4 +128,10 @@ public class InvokeInterface extends Instruction implements InvokeInstruction // create new method pool object method = new InterfaceMethod(method.getPool(), clazz, new NameAndType(nat.getPool(), nat.getName(), sig)); } + + @Override + public PoolEntry getMethod() + { + return method; + } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeSpecial.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeSpecial.java index 83cefef645..06b6a20e53 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeSpecial.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeSpecial.java @@ -12,6 +12,7 @@ import info.sigterm.deob.execution.StackContext; import info.sigterm.deob.execution.Type; import info.sigterm.deob.pool.Method; import info.sigterm.deob.pool.NameAndType; +import info.sigterm.deob.pool.PoolEntry; import info.sigterm.deob.signature.Signature; import java.io.DataInputStream; @@ -72,6 +73,8 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction StackContext object = stack.pop(); ins.pop(object); + handleExceptions(frame); + if (!method.getNameAndType().isVoid()) { StackContext ctx = new StackContext(ins, new Type(method.getNameAndType().getDescriptor().getReturnValue()).toStackType()); @@ -80,6 +83,32 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction frame.addInstructionContext(ins); } + + private void handleExceptions(Frame frame) + { + // jump to instruction handlers that can catch exceptions here + for (info.sigterm.deob.attributes.code.Exception e : this.getInstructions().getCode().getExceptions().getExceptions()) + { + Instruction start = e.getStart(), + end = e.getEnd(); + + // [start, end) + if (this.getPc() >= start.getPc() && this.getPc() < end.getPc()) + { + Frame f = frame.dup(); + Stack stack = f.getStack(); + + while (stack.getSize() > 0) + stack.pop(); + + InstructionContext ins = new InstructionContext(this, f); + StackContext ctx = new StackContext(ins, new Type("java/lang/Exception")); + stack.push(ctx); + + f.jumpAbsolute(e.getHandler().getPc()); + } + } + } @Override public String getDesc(Frame frame) @@ -100,4 +129,10 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction // create new method pool object method = new Method(method.getPool(), clazz, new NameAndType(nat.getPool(), nat.getName(), sig)); } + + @Override + public PoolEntry getMethod() + { + return method; + } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeStatic.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeStatic.java index 476e7a5430..c35c6faecf 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeStatic.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeStatic.java @@ -12,6 +12,7 @@ import info.sigterm.deob.execution.StackContext; import info.sigterm.deob.execution.Type; import info.sigterm.deob.pool.Method; import info.sigterm.deob.pool.NameAndType; +import info.sigterm.deob.pool.PoolEntry; import info.sigterm.deob.signature.Signature; import java.io.DataInputStream; @@ -69,6 +70,8 @@ public class InvokeStatic extends Instruction implements InvokeInstruction ins.pop(arg); } + handleExceptions(frame); + if (!method.getNameAndType().isVoid()) { StackContext ctx = new StackContext(ins, new Type(method.getNameAndType().getDescriptor().getReturnValue()).toStackType()); @@ -78,6 +81,32 @@ public class InvokeStatic extends Instruction implements InvokeInstruction frame.addInstructionContext(ins); } + private void handleExceptions(Frame frame) + { + // jump to instruction handlers that can catch exceptions here + for (info.sigterm.deob.attributes.code.Exception e : this.getInstructions().getCode().getExceptions().getExceptions()) + { + Instruction start = e.getStart(), + end = e.getEnd(); + + // [start, end) + if (this.getPc() >= start.getPc() && this.getPc() < end.getPc()) + { + Frame f = frame.dup(); + Stack stack = f.getStack(); + + while (stack.getSize() > 0) + stack.pop(); + + InstructionContext ins = new InstructionContext(this, f); + StackContext ctx = new StackContext(ins, new Type("java/lang/Exception")); + stack.push(ctx); + + f.jumpAbsolute(e.getHandler().getPc()); + } + } + } + @Override public String getDesc(Frame frame) { @@ -97,4 +126,10 @@ public class InvokeStatic extends Instruction implements InvokeInstruction // create new method pool object method = new Method(method.getPool(), clazz, new NameAndType(nat.getPool(), nat.getName(), sig)); } + + @Override + public PoolEntry getMethod() + { + return method; + } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeVirtual.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeVirtual.java index 50395df8ac..65322f424d 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeVirtual.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/InvokeVirtual.java @@ -12,6 +12,7 @@ import info.sigterm.deob.execution.StackContext; import info.sigterm.deob.execution.Type; import info.sigterm.deob.pool.Method; import info.sigterm.deob.pool.NameAndType; +import info.sigterm.deob.pool.PoolEntry; import info.sigterm.deob.signature.Signature; import java.io.DataInputStream; @@ -73,6 +74,8 @@ public class InvokeVirtual extends Instruction implements InvokeInstruction StackContext object = stack.pop(); ins.pop(object); + handleExceptions(frame); + if (!method.getNameAndType().isVoid()) { StackContext ctx = new StackContext(ins, new Type(method.getNameAndType().getDescriptor().getReturnValue()).toStackType()); @@ -82,6 +85,32 @@ public class InvokeVirtual extends Instruction implements InvokeInstruction frame.addInstructionContext(ins); } + private void handleExceptions(Frame frame) + { + // jump to instruction handlers that can catch exceptions here + for (info.sigterm.deob.attributes.code.Exception e : this.getInstructions().getCode().getExceptions().getExceptions()) + { + Instruction start = e.getStart(), + end = e.getEnd(); + + // [start, end) + if (this.getPc() >= start.getPc() && this.getPc() < end.getPc()) + { + Frame f = frame.dup(); + Stack stack = f.getStack(); + + while (stack.getSize() > 0) + stack.pop(); + + InstructionContext ins = new InstructionContext(this, f); + StackContext ctx = new StackContext(ins, new Type("java/lang/Exception")); + stack.push(ctx); + + f.jumpAbsolute(e.getHandler().getPc()); + } + } + } + @Override public void removeParameter(int idx) { @@ -95,4 +124,10 @@ public class InvokeVirtual extends Instruction implements InvokeInstruction // create new method pool object method = new Method(method.getPool(), clazz, new NameAndType(nat.getPool(), nat.getName(), sig)); } + + @Override + public PoolEntry getMethod() + { + return method; + } } diff --git a/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java b/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java index 13d88c3556..32ecb2b4b5 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java @@ -21,6 +21,9 @@ public class UnusedParameters int count = 0; int collide = 0; int overrides = 0; + + group.buildCallGraph(); // method.removeParameter uses the callgraph + for (ClassFile cf : group.getClasses()) { for (Method m : cf.getMethods().getMethods()) diff --git a/src/main/java/info/sigterm/deob/execution/Execution.java b/src/main/java/info/sigterm/deob/execution/Execution.java index f2b471196f..54e4efaf90 100644 --- a/src/main/java/info/sigterm/deob/execution/Execution.java +++ b/src/main/java/info/sigterm/deob/execution/Execution.java @@ -3,6 +3,7 @@ package info.sigterm.deob.execution; import info.sigterm.deob.ClassFile; import info.sigterm.deob.ClassGroup; import info.sigterm.deob.Method; +import info.sigterm.deob.attributes.code.Exceptions; import java.util.ArrayList; import java.util.List; @@ -26,8 +27,10 @@ public class Execution { if (method.getCode() == null) continue; + Frame f = new Frame(this, method); frames.add(f); + fcount += this.runFrames(); ++count; } diff --git a/src/main/java/info/sigterm/deob/execution/Frame.java b/src/main/java/info/sigterm/deob/execution/Frame.java index a2dd4c427e..2fb477b319 100644 --- a/src/main/java/info/sigterm/deob/execution/Frame.java +++ b/src/main/java/info/sigterm/deob/execution/Frame.java @@ -23,7 +23,7 @@ public class Frame private Stack stack; private Variables variables; private List instructions = new ArrayList<>(); // instructions executed in this frame - private Map visited; // shared + private Map> visited; // shared public Frame(Execution execution, Method method) { @@ -73,11 +73,6 @@ public class Frame executing = false; } - public void throwException(Type type) - { - executing = false; // XXX - } - public Method getMethod() { return method; @@ -154,17 +149,33 @@ public class Frame private void doJump(Instruction from, Instruction to) { - visited.put(from, to); + List l = visited.get(from); + if (l == null) + { + List l2 = new ArrayList<>(); + l2.add(to); + visited.put(from, l2); + } + else + { + l.add(to); + } } private boolean hasJumped(Instruction from, Instruction to) { - Instruction i = visited.get(from); - if (from instanceof TableSwitch || from instanceof LookupSwitch) // XXX magic instructions which jump to multiple different places - if (i != null) - return true; - assert i == null || i == to; - return i == to; + List i = visited.get(from); + if (i != null && i.contains(to)) + return true; + + if (i == null) + { + i = new ArrayList<>(); + visited.put(from, i); + } + + i.add(to); + return false; } public void jump(int offset)