diff --git a/trunk/src/com/jpexs/asdec/abc/ABC.java b/trunk/src/com/jpexs/asdec/abc/ABC.java index a72ed8ca6..5ede0e26c 100644 --- a/trunk/src/com/jpexs/asdec/abc/ABC.java +++ b/trunk/src/com/jpexs/asdec/abc/ABC.java @@ -70,10 +70,18 @@ public class ABC { } } + public int removeTraps() { + int rem=0; + for(MethodBody body:bodies){ + rem+=body.removeTraps(constants); + } + return rem; + } + public int removeDeadCode() { int rem=0; for(MethodBody body:bodies){ - rem+=body.removeDeadCode(); + rem+=body.removeDeadCode(constants); } return rem; } @@ -579,9 +587,9 @@ public class ABC { } public static final String[] reservedWords = { "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", - "extends", "false", "finally", "for", "function", "if", "implements", "import", "in", "instanceof", - "interface", "internal", "is", "native", "new", "null", "package", "private", "protected", "public", - "return", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", + "extends", "false", "finally", "for", "function", "get","if", "implements", "import", "in", "instanceof", + "interface", "internal", "is", "native", "new", "null","override", "package", "private", "protected", "public", + "return", "set","super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", "with", "dynamic", "default", "final", "in"}; public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; public static final String validNextCharacters = validFirstCharacters + "0123456789"; diff --git a/trunk/src/com/jpexs/asdec/abc/avm2/AVM2Code.java b/trunk/src/com/jpexs/asdec/abc/avm2/AVM2Code.java index e88662a63..108f304f4 100644 --- a/trunk/src/com/jpexs/asdec/abc/avm2/AVM2Code.java +++ b/trunk/src/com/jpexs/asdec/abc/avm2/AVM2Code.java @@ -36,10 +36,13 @@ import com.jpexs.asdec.abc.avm2.instructions.other.*; import com.jpexs.asdec.abc.avm2.instructions.stack.*; import com.jpexs.asdec.abc.avm2.instructions.types.*; import com.jpexs.asdec.abc.avm2.instructions.xml.*; +import com.jpexs.asdec.abc.avm2.parser.ASM3Parser; +import com.jpexs.asdec.abc.avm2.parser.ParseException; import com.jpexs.asdec.abc.avm2.treemodel.*; import com.jpexs.asdec.abc.avm2.treemodel.clauses.*; import com.jpexs.asdec.abc.avm2.treemodel.operations.AndTreeItem; import com.jpexs.asdec.abc.avm2.treemodel.operations.OrTreeItem; +import com.jpexs.asdec.abc.gui.DialogMissingSymbolHandler; import com.jpexs.asdec.abc.types.ABCException; import com.jpexs.asdec.abc.types.MethodBody; import com.jpexs.asdec.abc.types.MethodInfo; @@ -729,8 +732,24 @@ public class AVM2Code { ret += "exceptiontarget " + e + ":"; } } - ret += Highlighting.hilighOffset("", ofs) + ins.toStringNoAddress(constants, new ArrayList()) + "\n"; + if (ins.replaceWith != null) { + for (AVM2Instruction ins2 : ins.replaceWith) { + if (ins2.isIgnored()) { + continue; + } + if (ins2.definition instanceof JumpIns) { + ret += "jump ofs" + Helper.formatAddress(pos2adr(ins2.operands[0])) + "\n"; + } else { + ret += Highlighting.hilighOffset("", ofs) + ins2.toStringNoAddress(constants, new ArrayList()) + "\n"; + } + } + } else { + if (!ins.isIgnored()) { + ret += Highlighting.hilighOffset("", ofs) + ins.toStringNoAddress(constants, new ArrayList()) + "\n"; + } + } ofs += ins.getBytes().length; + } return ret; @@ -767,6 +786,10 @@ public class AVM2Code { return posCache.get(pos).intValue(); } + public void invalidateCache() { + cacheActual = false; + } + private static String listToString(List stack, ConstantPool constants, HashMap localRegNames, List fullyQualifiedNames) { String ret = ""; for (int d = 0; d < stack.size(); d++) { @@ -978,11 +1001,14 @@ public class AVM2Code { return pos2adr(fixIPAfterDebugLine(adr2pos(addr))); } - private ConvertOutput toSource(boolean isStatic, int classIndex, java.util.HashMap localRegs, Stack stack, Stack scopeStack, ABC abc, ConstantPool constants, MethodInfo method_info[], MethodBody body, int start, int end, HashMap localRegNames, List fullyQualifiedNames) throws ConvertException { + private ConvertOutput toSource(boolean isStatic, int classIndex, java.util.HashMap localRegs, Stack stack, Stack scopeStack, ABC abc, ConstantPool constants, MethodInfo method_info[], MethodBody body, int start, int end, HashMap localRegNames, List fullyQualifiedNames, boolean visited[]) throws ConvertException { boolean debugMode = DEBUG_MODE; if (debugMode) { System.out.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); } + if (visited == null) { + visited = new boolean[code.size()]; + } //if(true) return ""; toSourceCount++; if (toSourceLimit > 0) { @@ -1061,7 +1087,7 @@ public class AVM2Code { if (swins.operands.length >= 3) { if (swins.operands[0] == swins.getBytes().length) { if (adr2pos(pos2adr(f) + swins.operands[2]) < finStart) { - finallyCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, finStart, f - 1, localRegNames, fullyQualifiedNames).output; + finallyCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, finStart, f - 1, localRegNames, fullyQualifiedNames, visited).output; returnPos = f + 1; break; } @@ -1086,10 +1112,10 @@ public class AVM2Code { } Stack substack = new Stack(); substack.add(new ExceptionTreeItem(catchedExceptions.get(e))); - catchedCommands.add(toSource(isStatic, classIndex, localRegs, substack, new Stack(), abc, constants, method_info, body, adr2pos(fixAddrAfterDebugLine(catchedExceptions.get(e).target)), eendpos, localRegNames, fullyQualifiedNames).output); + catchedCommands.add(toSource(isStatic, classIndex, localRegs, substack, new Stack(), abc, constants, method_info, body, adr2pos(fixAddrAfterDebugLine(catchedExceptions.get(e).target)), eendpos, localRegNames, fullyQualifiedNames, visited).output); } - List tryCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip, endpos - 1, localRegNames, fullyQualifiedNames).output; + List tryCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip, endpos - 1, localRegNames, fullyQualifiedNames, visited).output; output.add(new TryTreeItem(tryCommands, catchedExceptions, catchedCommands, finallyCommands)); @@ -1107,6 +1133,11 @@ public class AVM2Code { unknownJumps.remove(new Integer(ip)); throw new UnknownJumpException(stack, ip, output); } + if (visited[ip]) { + Logger.getLogger(AVM2Code.class.getName()).warning("Code already visited"); + break; + } + visited[ip] = true; AVM2Instruction ins = code.get(ip); if ((ip + 8 < code.size())) { //return in finally clause @@ -1152,13 +1183,12 @@ public class AVM2Code { } if (ins.definition instanceof JumpIns) { //Ifs with multiple conditions + int secondAddr = addr + ins.getBytes().length; + int jumpAddr = secondAddr + ins.operands[0]; + int jumpPos = adr2pos(jumpAddr); if (ins.operands[0] == 0) { ip++; } else if (ins.operands[0] > 0) { - int secondAddr = addr + ins.getBytes().length; - int jumpAddr = secondAddr + ins.operands[0]; - int jumpPos = adr2pos(jumpAddr);// - if (finallyJumps.contains(jumpPos)) { if (code.get(ip + 1).definition instanceof LabelIns) { if (code.get(ip + 2).definition instanceof PopIns) { @@ -1254,7 +1284,7 @@ public class AVM2Code { throw new ConvertException("Unknown pattern: no setlocal before lookupswitch", switchPos); } loopList.add(new Loop(ip, switchPos + 1)); - Stack substack = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, jumpPos, evalTo - 1, localRegNames, fullyQualifiedNames).stack; + Stack substack = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, jumpPos, evalTo - 1, localRegNames, fullyQualifiedNames, visited).stack; TreeItem switchedValue = substack.pop(); //output.add("loop" + (switchPos + 1) + ":"); int switchBreak = switchPos + 1; @@ -1289,7 +1319,7 @@ public class AVM2Code { if (evalTo > -1) { - substack = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, curPos, evalTo - 1, localRegNames, fullyQualifiedNames).stack; + substack = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, curPos, evalTo - 1, localRegNames, fullyQualifiedNames, visited).stack; casesList.add(substack.pop()); } int substart = adr2pos(code.get(switchPos).operands[2 + casePos] + pos2adr(switchPos)); @@ -1301,7 +1331,7 @@ public class AVM2Code { if (evalTo == -1) { subend--; } - List commands = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, substart, subend, localRegNames, fullyQualifiedNames).output; + List commands = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, substart, subend, localRegNames, fullyQualifiedNames, visited).output; if ((evalTo == -1) && (casePos + 1 < code.get(switchPos).operands.length - 2)) { if (commands.size() == 1) { commands.remove(0); @@ -1345,7 +1375,7 @@ public class AVM2Code { loopList.add(currentLoop); - ConvertOutput co = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, jumpPos, adr2pos(afterBackJumpAddr) - 2, localRegNames, fullyQualifiedNames); + ConvertOutput co = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, jumpPos, adr2pos(afterBackJumpAddr) - 2, localRegNames, fullyQualifiedNames, visited); Stack substack = co.stack; backJumpIns.definition.translate(isStatic, classIndex, localRegs, substack, scopeStack, constants, backJumpIns, method_info, output, body, abc, localRegNames, fullyQualifiedNames); @@ -1354,7 +1384,7 @@ public class AVM2Code { boolean isFor = false; List finalExpression = new ArrayList(); try { - subins = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, adr2pos(secondAddr) + 1/*label*/, jumpPos - 1, localRegNames, fullyQualifiedNames).output; + subins = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, adr2pos(secondAddr) + 1/*label*/, jumpPos - 1, localRegNames, fullyQualifiedNames, visited).output; } catch (UnknownJumpException uje) { if ((uje.ip >= start) && (uje.ip <= end)) { currentLoop.loopContinue = uje.ip; @@ -1379,7 +1409,7 @@ public class AVM2Code { } } } - finalExpression = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, uje.ip, jumpPos - 1, localRegNames, fullyQualifiedNames).output; + finalExpression = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, uje.ip, jumpPos - 1, localRegNames, fullyQualifiedNames, visited).output; isFor = true; } else { throw new ConvertException("Unknown pattern: jump to nowhere", ip); @@ -1533,7 +1563,7 @@ public class AVM2Code { if (((GetLocalTypeIns) code.get(t).definition).getRegisterId(code.get(t)) == reg) { if (code.get(t + 1).definition instanceof KillIns) { if (code.get(t + 1).operands[0] == reg) { - ConvertOutput assignment = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip + 2, t - 1, localRegNames, fullyQualifiedNames); + ConvertOutput assignment = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip + 2, t - 1, localRegNames, fullyQualifiedNames, visited); stack.push(assignment.output.remove(assignment.output.size() - 1)); ip = t + 2; continue iploop; @@ -1568,9 +1598,9 @@ public class AVM2Code { addr = addr + ins.getBytes().length + insAfter.getBytes().length + insAfter.operands[0]; nextPos = adr2pos(addr) - 1; if (isAnd) { - stack.add(new AndTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames).stack.pop())); + stack.add(new AndTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames, visited).stack.pop())); } else { - stack.add(new OrTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames).stack.pop())); + stack.add(new OrTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames, visited).stack.pop())); } ins = code.get(nextPos + 1); ip = nextPos + 1; @@ -1627,13 +1657,13 @@ public class AVM2Code { } } } - ConvertOutput onTrue = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, ip + 1, targetIns - 1 - ((hasElse || hasReturn) ? 1 : 0), localRegNames, fullyQualifiedNames); + ConvertOutput onTrue = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, ip + 1, targetIns - 1 - ((hasElse || hasReturn) ? 1 : 0), localRegNames, fullyQualifiedNames, visited); ip = targetIns; ConvertOutput onFalse = new ConvertOutput(new Stack(), new ArrayList()); if (hasElse) { int finalAddr = targetAddr + code.get(targetIns - 1).operands[0]; int finalIns = adr2pos(finalAddr); - onFalse = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, targetIns, finalIns - 1, localRegNames, fullyQualifiedNames); + onFalse = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, targetIns, finalIns - 1, localRegNames, fullyQualifiedNames, visited); ip = finalIns; } if ((onTrue.stack.size() > 0) && (onFalse != null) && (onFalse.stack.size() > 0)) { @@ -1730,7 +1760,7 @@ public class AVM2Code { ignoredIns = new ArrayList(); HashMap localRegs = new HashMap(); try { - return toSource(isStatic, classIndex, localRegs, new Stack(), new Stack(), abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames).output; + return toSource(isStatic, classIndex, localRegs, new Stack(), new Stack(), abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames, null).output; } catch (ConvertException ex) { return new ArrayList(); } @@ -1819,7 +1849,7 @@ public class AVM2Code { } try { - list = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames).output; + list = toSource(isStatic, classIndex, localRegs, new Stack(), scopeStack, abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames, null).output; if (initTraits != null) { for (int i = 0; i < list.size(); i++) { TreeItem ti = list.get(i); @@ -2101,7 +2131,7 @@ public class AVM2Code { } code.remove(pos); - + invalidateCache(); } public void insertInstruction(int pos, AVM2Instruction instruction) { @@ -2145,67 +2175,16 @@ public class AVM2Code { code.add(pos, instruction); } - private void removeFreeBlocks(ConstantPool constants, MethodBody body) throws ConvertException { - List offsets = new ArrayList(); - for (AVM2Instruction ins : code) { - offsets.addAll(ins.getOffsets()); - } - for (ABCException ex : body.exceptions) { - offsets.add((long) ex.start); - offsets.add((long) ex.end); - offsets.add((long) ex.target); - } - int clearedCount = 0; - loopip: - for (int ip = 0; ip < code.size(); ip++) { - AVM2Instruction ins = code.get(ip); - if (ins.definition instanceof JumpIns) { - int secondAddr = pos2adr(ip + 1); - int jumpAddr = secondAddr + ins.operands[0]; - int jumpPos = adr2pos(jumpAddr); - if (jumpPos <= ip) { - continue; - } - if (jumpPos > code.size()) { - continue; - } - for (int k = ip + 1; k < jumpPos; k++) { - if (offsets.contains((long) pos2adr(k))) { - continue loopip; - } - } - for (int k = ip; k < jumpPos; k++) { - removeInstruction(ip, body); - clearedCount++; - } - offsets.clear(); - for (AVM2Instruction ins2 : code) { - offsets.addAll(ins2.getOffsets()); - } - for (ABCException ex : body.exceptions) { - offsets.add((long) ex.start); - offsets.add((long) ex.end); - offsets.add((long) ex.target); - } - ip--; - //ip=jumpPos; - } - } - if (clearedCount > 0) { - //System.out.println("Cleared " + clearedCount + " lines of code TO:"); - //System.out.println(toASMSource(constants)); - //System.exit(1); - } - } - - public void clearSecureSWF(ConstantPool constants, MethodBody body) throws ConvertException { + + public int removePushTrueFalseTraps(ConstantPool constants, MethodBody body) throws ConvertException { + removeDeadCode(constants, body); + boolean isSecure = true; if (code.size() > 4) { AVM2Instruction first = code.get(0); AVM2Instruction second = code.get(1); boolean firstValue = false; boolean secondValue = false; - boolean isSecure = true; if (first.definition instanceof PushFalseIns) { firstValue = false; } else if (first.definition instanceof PushTrueIns) { @@ -2222,16 +2201,30 @@ public class AVM2Code { isSecure = false; } if (isSecure) { - AVM2Instruction third = code.get(2); int pos = 2; + AVM2Instruction third = code.get(pos); if (third.definition instanceof SwapIns) { pos++; boolean dup = firstValue; firstValue = secondValue; secondValue = dup; + third.ignored = true; + } + while (third.definition instanceof JumpIns) { + pos = adr2pos(pos2adr(pos) + third.getBytes().length + third.operands[0]); + third = code.get(pos); } AVM2Instruction firstSet = code.get(pos); - AVM2Instruction secondSet = code.get(pos + 1); + while (firstSet.definition instanceof JumpIns) { + pos = adr2pos(pos2adr(pos) + firstSet.getBytes().length + firstSet.operands[0]); + firstSet = code.get(pos); + } + pos++; + AVM2Instruction secondSet = code.get(pos); + while (secondSet.definition instanceof JumpIns) { + pos = adr2pos(pos2adr(pos) + secondSet.getBytes().length + secondSet.operands[0]); + secondSet = code.get(pos); + } int trueIndex = -1; int falseIndex = -1; if (firstSet.definition instanceof SetLocalTypeIns) { @@ -2247,24 +2240,19 @@ public class AVM2Code { if (isSecure) { if (secondSet.definition instanceof SetLocalTypeIns) { if (firstValue == true) { - trueIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(firstSet); + trueIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(secondSet); } if (firstValue == false) { - falseIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(firstSet); + falseIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(secondSet); } - - //Yes, secure - pos += 2; - for (int i = 0; i < pos; i++) { - code.get(i).ignored = true; - //removeInstruction(0, body); - } - System.out.println("trueIndex:" + trueIndex); - System.out.println("falseIndex:" + falseIndex); + secondSet.ignored = true; + firstSet.ignored = true; + first.ignored = true; + second.ignored = true; boolean found; do { found = false; - for (int ip = pos; ip < code.size(); ip++) { + for (int ip = 0; ip < code.size(); ip++) { if (code.get(ip).ignored) { continue; } @@ -2275,10 +2263,10 @@ public class AVM2Code { Stack myStack = new Stack(); do { AVM2Instruction ins = code.get(ip); - if (ins.ignored) { - ip++; - continue; - } else if (ins.definition instanceof GetLocalTypeIns) { + /*if (ins.ignored) { + ip++; + continue; + } else*/ if (ins.definition instanceof GetLocalTypeIns) { regIndex = ((GetLocalTypeIns) ins.definition).getRegisterId(ins); if (regIndex == trueIndex) { myStack.push(true); @@ -2319,21 +2307,19 @@ public class AVM2Code { } else if (ins.definition instanceof JumpIns) { ip = adr2pos(pos2adr(ip + 1) + code.get(ip).operands[0]); } else { + ip++; } } while (myStack.size() > 0); - /*for(int rem=code.size();rem>=0;rem--){ - if(code.get(rem).ignored){ - code.remove(rem); - } - } */ break; } } } } while (found); + removeIgnored(body); + removeDeadCode(constants, body); } else { //isSecure = false; } @@ -2342,10 +2328,13 @@ public class AVM2Code { } } } + int ret=isSecure ? 1 : 0; + ret+=visitCodeTrap(body, new int[code.size()]); + removeIgnored(body); + return ret; } - public void clearCode(ConstantPool constants, MethodBody body) throws ConvertException { - + public int removePushByteTraps(ConstantPool constants, MethodBody body) throws ConvertException { if (code.size() > 3) { if (code.get(0).definition instanceof PushByteIns) { if (code.get(1).definition instanceof PushByteIns) { @@ -2356,16 +2345,25 @@ public class AVM2Code { for (int i = 0; i < targetPos; i++) { removeInstruction(0, body); } + return 1; } } } } } + return 0; + } - removeFreeBlocks(constants, body); - //clearSecureSWF(constants, body); - - + public int removeTraps(ConstantPool constants, MethodBody body) { + int ret = 0; + try { + ret += removePushByteTraps(constants, body); + ret += removePushTrueFalseTraps(constants, body); + } catch (ConvertException ex) { + ex.printStackTrace(); + } + restoreControlFlow(constants, body); + return ret; } private void handleRegister(CodeStats stats, int reg) { @@ -2464,19 +2462,30 @@ public class AVM2Code { return stats; } - private void visitCode(int ip, boolean visited[]) { - while ((ip < visited.length) && (!visited[ip])) { - visited[ip] = true; + private void visitCode(int ip, int visited[]) { + while (ip < visited.length) { + visited[ip]++; + if (visited[ip] > 1) { + break; + } AVM2Instruction ins = code.get(ip); + if (ins.definition instanceof ThrowIns) { + break; + } + if (ins.definition instanceof ReturnValueIns) { + break; + } + if (ins.definition instanceof ReturnVoidIns) { + break; + } if (ins.definition instanceof LookupSwitchIns) { - try { - for(int i=2;i 1) { + break; + } + AVM2Instruction ins = code.get(ip); + if (ins.definition instanceof ThrowIns) { + break; + } + if (ins.definition instanceof ReturnValueIns) { + break; + } + if (ins.definition instanceof ReturnVoidIns) { + break; + } + if (ins.definition instanceof LookupSwitchIns) { + try { + for (int i = 2; i < ins.operands.length; i++) { + ret+=visitCodeTrap(adr2pos(pos2adr(ip) + ins.operands[i]), visited,prev); + } + ip = adr2pos(pos2adr(ip) + ins.operands[0]); + prev=ins; + continue; + } catch (ConvertException ex) { + } + } + if (ins.definition instanceof JumpIns) { + try { + ip = adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]); + prev=ins; + continue; + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } + } else if (ins.definition instanceof IfTypeIns) { + if ((prev != null) && ins.definition instanceof IfTrueIns) { + if (prev.definition instanceof PushTrueIns) { + prev.ignored = true; + ins.definition = new JumpIns(); + ip--; + ret++; + continue; + } else if (prev.definition instanceof PushFalseIns) { + prev.ignored = true; + ins.ignored = true; + ret++; + continue; + } + } + if ((prev != null) && ins.definition instanceof IfFalseIns) { + if (prev.definition instanceof PushFalseIns) { + prev.ignored = true; + ins.definition = new JumpIns(); + ip--; + ret++; + continue; + } else if (prev.definition instanceof PushTrueIns) { + prev.ignored = true; + ins.ignored = true; + ret++; + continue; + } + } + try { + ret+=visitCodeTrap(adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]), visited,prev); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } + } + ip++; + prev=ins; + }; + return ret; + } + + private int visitCodeTrap(MethodBody body, int visited[]) { + int ret=0; + for (int i = 0; i < visited.length; i++) { + visited[i] = 0; + } + ret+=visitCodeTrap(0, visited,null); + for (ABCException e : body.exceptions) { + try { + ret+=visitCodeTrap(adr2pos(e.start), visited,null); + ret+=visitCodeTrap(adr2pos(e.target), visited,null); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } + } + return ret; + } + + public void restoreControlFlow(int ip, int visited[], int visited2[], HashMap> appended) throws ConvertException { + List buf = new ArrayList(); + boolean cont = false; + int continueip = 0; + AVM2Instruction prev = null; + for (; ip < visited.length; ip++) { + AVM2Instruction ins = code.get(ip); + if (visited2[ip] > 0) { + break; + } + visited2[ip]++; + if (visited[ip] > 1) { + if (cont) { + buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{ip}, new byte[0])); + } + cont = false; + } + if (ins.definition instanceof LookupSwitchIns) { + + if (cont) { + buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{ip}, new byte[0])); + } + cont = false; + restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[0]), visited, visited2, appended); + for (int i = 2; i < ins.operands.length; i++) { + restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[i]), visited, visited2, appended); + } + break; + } + if (ins.definition instanceof JumpIns) { + int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]); + if ((newip < visited.length) && (visited[newip] == 1)) { + if (!cont) { + continueip = ip; + buf = new ArrayList(); + appended.put(continueip, buf); + } + cont = true; + } else { + if (cont) { + buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{newip}, new byte[0])); + } + cont = false; + } + ip = newip - 1; + } else if (ins.definition instanceof IfTypeIns) { + int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]); + if (cont) { + buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{ip}, new byte[0])); + } + cont = false; + restoreControlFlow(newip, visited, visited2, appended); + } else if ((ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ThrowIns)) { + if (cont) { + buf.add(ins); + } + break; + } else if (cont) { + buf.add(ins); + } + prev = ins; + } + + } + + public void restoreControlFlow(ConstantPool constants, MethodBody body) { + try { + int visited[] = new int[code.size()]; + int visited2[] = new int[code.size()]; + visitCode(body, visited); + HashMap> appended = new HashMap>(); + restoreControlFlow(0, visited, visited2, appended); + for (ABCException e : body.exceptions) { + try { + restoreControlFlow(adr2pos(e.start), visited, visited2, appended); + restoreControlFlow(adr2pos(e.target), visited, visited2, appended); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } + } + for (int ip : appended.keySet()) { + code.get(ip).replaceWith = appended.get(ip); + } + } catch (ConvertException cex) { + } + try { + String src = Highlighting.stripHilights(toASMSource(constants, body)); + FileOutputStream fos = new FileOutputStream("src.txt"); + fos.write(src.getBytes()); + fos.close(); + AVM2Code acode = ASM3Parser.parse(new ByteArrayInputStream(src.getBytes()), constants, null, body); + this.code = acode.code; + } catch (IOException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } catch (ParseException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } + invalidateCache(); + removeDeadCode(constants, body); + } + + private void removeIgnored(MethodBody body) { + for (int rem = code.size() - 1; rem >= 0; rem--) { + if (code.get(rem).ignored) { + removeInstruction(rem, body); + } + } + } + + public int removeDeadCode(ConstantPool constants, MethodBody body) { + int visited[] = new int[code.size()]; + + visitCode(body, visited); int cnt = 0; for (int i = visited.length - 1; i >= 0; i--) { - if (!visited[i]) { + if (visited[i] == 0) { removeInstruction(i, body); cnt++; } @@ -2526,7 +2743,7 @@ public class AVM2Code { cnt++; } } - } + } return cnt; } } diff --git a/trunk/src/com/jpexs/asdec/abc/avm2/instructions/AVM2Instruction.java b/trunk/src/com/jpexs/asdec/abc/avm2/instructions/AVM2Instruction.java index 9b4e2d19e..0fc3ae3ea 100644 --- a/trunk/src/com/jpexs/asdec/abc/avm2/instructions/AVM2Instruction.java +++ b/trunk/src/com/jpexs/asdec/abc/avm2/instructions/AVM2Instruction.java @@ -207,6 +207,12 @@ public class AVM2Instruction { return " ;" + comment; } + public boolean isIgnored() { + return ignored; + } + + + public String toString(ConstantPool constants, List fullyQualifiedNames) { String s = Helper.formatAddress(offset) + " " + Helper.padSpaceRight(Helper.byteArrToString(getBytes()), 30) + definition.instructionName; s += getParams(constants, fullyQualifiedNames) + getComment(); @@ -218,4 +224,6 @@ public class AVM2Instruction { s += getParams(constants, fullyQualifiedNames) + getComment(); return s; } + + public List replaceWith; } diff --git a/trunk/src/com/jpexs/asdec/abc/gui/ABCPanel.java b/trunk/src/com/jpexs/asdec/abc/gui/ABCPanel.java index 2fc633833..053367257 100644 --- a/trunk/src/com/jpexs/asdec/abc/gui/ABCPanel.java +++ b/trunk/src/com/jpexs/asdec/abc/gui/ABCPanel.java @@ -147,7 +147,7 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener { oneList.add(list.get(index)); this.abc = list.get(index).abc; classTree.setDoABCTags(oneList); - } + } updateConstList(); } @@ -319,7 +319,9 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener { } public void reload() { - switchAbc(listIndex - 1); + switchAbc(listIndex); + decompiledTextArea.clearScriptCache(); + decompiledTextArea.reloadClass(); } public void itemStateChanged(ItemEvent e) { diff --git a/trunk/src/com/jpexs/asdec/abc/gui/ASMSourceEditorPane.java b/trunk/src/com/jpexs/asdec/abc/gui/ASMSourceEditorPane.java index 3acf0967b..69ab72ab9 100644 --- a/trunk/src/com/jpexs/asdec/abc/gui/ASMSourceEditorPane.java +++ b/trunk/src/com/jpexs/asdec/abc/gui/ASMSourceEditorPane.java @@ -74,7 +74,6 @@ public class ASMSourceEditorPane extends LineMarkedEditorPane implements CaretLi public void graph() { Graph gr = new Graph(abc.bodies[bodyIndex].code); - //(new GraphTreeFrame(gr)).setVisible(true); (new GraphFrame(gr, "")).setVisible(true); } diff --git a/trunk/src/com/jpexs/asdec/abc/gui/DecompiledEditorPane.java b/trunk/src/com/jpexs/asdec/abc/gui/DecompiledEditorPane.java index ff4f33ec2..7a8adb213 100644 --- a/trunk/src/com/jpexs/asdec/abc/gui/DecompiledEditorPane.java +++ b/trunk/src/com/jpexs/asdec/abc/gui/DecompiledEditorPane.java @@ -245,8 +245,12 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL } private List abcList; - public void setScript(ScriptInfo script, ABC abc, List abcList) { - setText("//Please wait..."); + public void clearScriptCache() + { + bufferedClasses.clear(); + } + + public void setScript(ScriptInfo script, ABC abc, List abcList) { if (script == null) { highlights = new ArrayList(); traitHighlights = new ArrayList(); @@ -254,6 +258,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL this.script = null; return; } + setText("//Please wait..."); String hilightedCode; if (!bufferedClasses.containsKey(script)) { @@ -279,11 +284,13 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL } public void reloadClass() { + int ci=classIndex; if (bufferedClasses.containsKey(script)) { bufferedClasses.remove(script); } setScript(script, abc, abcList); setNoTrait(); + setClassIndex(classIndex); } public int getClassIndex() { diff --git a/trunk/src/com/jpexs/asdec/abc/gui/DetailPanel.java b/trunk/src/com/jpexs/asdec/abc/gui/DetailPanel.java index 01c905830..243ceccae 100644 --- a/trunk/src/com/jpexs/asdec/abc/gui/DetailPanel.java +++ b/trunk/src/com/jpexs/asdec/abc/gui/DetailPanel.java @@ -127,9 +127,7 @@ public class DetailPanel extends JPanel implements ActionListener { if (cardMap.get(selectedCard) instanceof TraitDetail) { if (((TraitDetail) cardMap.get(selectedCard)).save()) { int lasttrait = abcPanel.decompiledTextArea.lastTraitIndex; - int lastclass = abcPanel.decompiledTextArea.getClassIndex(); - abcPanel.decompiledTextArea.reloadClass(); - abcPanel.decompiledTextArea.setClassIndex(lastclass); + abcPanel.decompiledTextArea.reloadClass(); abcPanel.decompiledTextArea.gotoTrait(lasttrait); JOptionPane.showMessageDialog(this, "Trait Successfully saved"); } diff --git a/trunk/src/com/jpexs/asdec/abc/gui/GraphFrame.java b/trunk/src/com/jpexs/asdec/abc/gui/GraphFrame.java index bcf215129..cc0d33ef4 100644 --- a/trunk/src/com/jpexs/asdec/abc/gui/GraphFrame.java +++ b/trunk/src/com/jpexs/asdec/abc/gui/GraphFrame.java @@ -21,7 +21,10 @@ import com.jpexs.asdec.abc.avm2.flowgraph.GraphPart; import com.jpexs.asdec.gui.View; import java.awt.*; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -42,33 +45,40 @@ public class GraphFrame extends JFrame { public GraphPanel(Graph graph) { this.graph = graph; - setPreferredSize(new Dimension((BLOCK_WIDTH + SPACE_HORIZONTAL) * getPartWidth(graph.head, new ArrayList()), (BLOCK_HEIGHT + SPACE_VERTICAL) * getPartHeight(graph.head, new ArrayList()))); + setPreferredSize(new Dimension((BLOCK_WIDTH + SPACE_HORIZONTAL) * getPartWidth(graph.head, new HashSet()), (BLOCK_HEIGHT + SPACE_VERTICAL) * getPartHeight(graph.head, new ArrayList()))); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.black); - paintPart(g, graph.head, 0, getPartWidth(graph.head, new ArrayList()) * (BLOCK_WIDTH + SPACE_HORIZONTAL) / 2, new ArrayList()); + paintPart(g, graph.head, 0, getPartWidth(graph.head, new HashSet()) * (BLOCK_WIDTH + SPACE_HORIZONTAL) / 2, new HashMap()); } - private void paintPart(Graphics g, GraphPart part, int y, int x, List used) { - List l = new ArrayList(); - l.addAll(used); - int totalWidthParts = getPartWidth(part, l); + private void paintPart(Graphics g, GraphPart part, int y, int x, HashMap used) { + HashMap l = new HashMap(); + l.putAll(used); + HashSet hs=new HashSet(); + hs.addAll(l.keySet()); + int totalWidthParts = getPartWidth(part, hs); int totalWidth = totalWidthParts * (BLOCK_WIDTH + SPACE_HORIZONTAL); - g.drawRect(x - BLOCK_WIDTH / 2 - SPACE_HORIZONTAL / 2, y, BLOCK_WIDTH, BLOCK_HEIGHT); - g.drawString(part.toString(), x - BLOCK_WIDTH / 2, y + BLOCK_HEIGHT); - if (used.contains(part)) { + + if (used.containsKey(part)) { + g.setColor(Color.black); + Point p=used.get(part); + g.drawLine(x, y, p.x, p.y); return; } - used.add(part); + g.drawRect(x - BLOCK_WIDTH / 2 - SPACE_HORIZONTAL / 2, y, BLOCK_WIDTH, BLOCK_HEIGHT); + g.drawString(part.toString(), x - BLOCK_WIDTH / 2, y + BLOCK_HEIGHT); + + used.put(part,new Point(x,y)); if (part.nextParts.size() > 0) { int cx = x - totalWidth / 2; for (int p = 0; p < part.nextParts.size(); p++) { - l = new ArrayList(); - l.addAll(used); - int cellWidth = getPartWidth(part.nextParts.get(p), l) * (BLOCK_WIDTH + SPACE_HORIZONTAL); + HashSet k = new HashSet(); + k.addAll(used.keySet()); + int cellWidth = getPartWidth(part.nextParts.get(p), k) * (BLOCK_WIDTH + SPACE_HORIZONTAL); g.setColor(Color.black); g.drawLine(x, y + BLOCK_HEIGHT, cx + cellWidth / 2, y + BLOCK_HEIGHT + SPACE_VERTICAL); paintPart(g, part.nextParts.get(p), y + BLOCK_HEIGHT + SPACE_VERTICAL, cx + cellWidth / 2, used); @@ -94,7 +104,7 @@ public class GraphFrame extends JFrame { return 1 + maxH; } - private int getPartWidth(GraphPart part, List used) { + private int getPartWidth(GraphPart part, HashSet used) { if (used.contains(part)) { return 1; diff --git a/trunk/src/com/jpexs/asdec/abc/gui/MethodCodePanel.java b/trunk/src/com/jpexs/asdec/abc/gui/MethodCodePanel.java index 8e94d6c6c..c7bceecde 100644 --- a/trunk/src/com/jpexs/asdec/abc/gui/MethodCodePanel.java +++ b/trunk/src/com/jpexs/asdec/abc/gui/MethodCodePanel.java @@ -85,7 +85,7 @@ public class MethodCodePanel extends JPanel implements ActionListener { execButton.setActionCommand("EXEC"); execButton.addActionListener(this); - //buttonsPan.add(graphButton); + buttonsPanel.add(graphButton); // buttonsPanel.add(saveButton); // buttonsPan.add(execButton); diff --git a/trunk/src/com/jpexs/asdec/abc/types/MethodBody.java b/trunk/src/com/jpexs/asdec/abc/types/MethodBody.java index f4ffacc71..5a1036420 100644 --- a/trunk/src/com/jpexs/asdec/abc/types/MethodBody.java +++ b/trunk/src/com/jpexs/asdec/abc/types/MethodBody.java @@ -48,8 +48,12 @@ public class MethodBody implements Cloneable { return s; } - public int removeDeadCode(){ - return code.removeDeadCode(this); + public int removeDeadCode(ConstantPool constants){ + return code.removeDeadCode(constants,this); + } + + public int removeTraps(ConstantPool constants){ + return code.removeTraps(constants, this); } public HashMap getLocalRegNames(ABC abc) { diff --git a/trunk/src/com/jpexs/asdec/gui/MainFrame.java b/trunk/src/com/jpexs/asdec/gui/MainFrame.java index be13fe36b..5abbd39a4 100644 --- a/trunk/src/com/jpexs/asdec/gui/MainFrame.java +++ b/trunk/src/com/jpexs/asdec/gui/MainFrame.java @@ -236,11 +236,20 @@ public class MainFrame extends JFrame implements ActionListener { miRemoveDeadCodeAll.setActionCommand("REMOVEDEADCODEALL"); miRemoveDeadCodeAll.addActionListener(this); + JMenuItem miTraps = new JMenuItem("Remove traps"); + miTraps.setActionCommand("REMOVETRAPS"); + miTraps.addActionListener(this); + + JMenuItem miTrapsAll = new JMenuItem("Remove all traps"); + miTrapsAll.setActionCommand("REMOVETRAPSALL"); + miTrapsAll.addActionListener(this); menuDeobfuscation.add(miSubLimiter); menuDeobfuscation.add(miRenameIdentifiers); menuDeobfuscation.add(miRemoveDeadCode); menuDeobfuscation.add(miRemoveDeadCodeAll); + menuDeobfuscation.add(miTraps); + menuDeobfuscation.add(miTrapsAll); JMenu menuTools = new JMenu("Tools"); @@ -594,6 +603,10 @@ public class MainFrame extends JFrame implements ActionListener { return ret; } + public boolean confirmExperimental() { + return JOptionPane.showConfirmDialog(null, "Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; + } + public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("EXIT")) { setVisible(false); @@ -724,34 +737,64 @@ public class MainFrame extends JFrame implements ActionListener { } } + if (e.getActionCommand().startsWith("REMOVETRAPS")) { + Main.startWork("Removing traps..."); + final boolean all = e.getActionCommand().endsWith("ALL"); + if ((!all) || confirmExperimental()) { + new SwingWorker() { + @Override + protected Object doInBackground() throws Exception { + int cnt = 0; + if (all) { + for (DoABCTag tag : abcPanel.list) { + cnt += tag.abc.removeTraps(); + } + } else { + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + if (bi != -1) { + cnt += abcPanel.abc.bodies[bi].removeTraps(abcPanel.abc.constants); + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc); + } + Main.stopWork(); + JOptionPane.showMessageDialog(null, "Traps removed: " + cnt); + abcPanel.reload(); + return true; + } + }.execute(); + } + } + if (e.getActionCommand().startsWith("REMOVEDEADCODE")) { Main.startWork("Removing dead code..."); final boolean all = e.getActionCommand().endsWith("ALL"); - new SwingWorker() { - @Override - protected Object doInBackground() throws Exception { - int cnt = 0; - if (all) { - for (DoABCTag tag : abcPanel.list) { - cnt += tag.abc.removeDeadCode(); + if ((!all) || confirmExperimental()) { + new SwingWorker() { + @Override + protected Object doInBackground() throws Exception { + int cnt = 0; + if (all) { + for (DoABCTag tag : abcPanel.list) { + cnt += tag.abc.removeDeadCode(); + } + } else { + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + if (bi != -1) { + cnt += abcPanel.abc.bodies[bi].removeDeadCode(abcPanel.abc.constants); + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc); } - }else{ - int bi=abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - if(bi!=-1){ - cnt += abcPanel.abc.bodies[bi].removeDeadCode(); - } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc); + Main.stopWork(); + JOptionPane.showMessageDialog(null, "Instructions removed: " + cnt); + abcPanel.reload(); + return true; } - Main.stopWork(); - JOptionPane.showMessageDialog(null, "Instructions removed: " + cnt); - abcPanel.reload(); - return true; - } - }.execute(); + }.execute(); + } } if (e.getActionCommand().equals("RENAMEIDENTIFIERS")) { - if (JOptionPane.showConfirmDialog(null, "Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { + if (confirmExperimental()) { Main.startWork("Renaming identifiers..."); new SwingWorker() {