diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java new file mode 100644 index 000000000..c90450130 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc.avm2.deobfuscation; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.action.ActionList; +import java.util.List; +import java.util.Map; + +/** + * + * AVM2 Deobfuscator removing single assigned local registers. + * + * Example: var a = true; var b = false; ... if(a){ ...ok }else{ not executed } + * + * @author JPEXS + */ +public class AVM2DeobfuscatorJumps extends AVM2DeobfuscatorSimple { + + //private final int executionLimit = 30000; + @Override + public void actionListParsed(ActionList actions, SWF swf) { + + } + + @Override + public void deobfuscate(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException { + + //body.getCode().markMappedOffsets(); + //removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + AVM2Code code = body.getCode(); + + boolean found; + do { + found = false; + Map> refs = body.getCode().visitCode(body); + loopi: + for (int i = 0; i < code.code.size(); i++) { + AVM2Instruction ins = code.code.get(i); + if (ins.definition instanceof JumpIns) { + long targetAddr = ins.offset + ins.operands[0] + ins.getBytes().length; + { + for (int r : refs.get(i)) { + if (r >= 0) { //Not Exception start/end + AVM2Instruction srcIns = code.code.get(r); + + if ((srcIns.definition instanceof JumpIns) || ((srcIns.definition instanceof IfTypeIns) && (r != i - 1))) { + { + int oldop = srcIns.operands[0]; + srcIns.operands[0] = (int) (targetAddr - (srcIns.offset + srcIns.getBytes().length)); + if (srcIns.operands[0] != oldop) { + found = true; + System.err.println("found"); + } + } + } + } + } + } + } + } + removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + } while (found); + + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorRegisters.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java similarity index 96% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorRegisters.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java index bf6005041..e4fe997cc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorRegisters.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java @@ -14,12 +14,14 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. */ -package com.jpexs.decompiler.flash.abc.avm2; +package com.jpexs.decompiler.flash.abc.avm2.deobfuscation; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.AVM2LocalData; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2GraphSource; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; @@ -139,13 +141,6 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { AVM2Instruction action = code.code.get(idx); instructionsProcessed++; - - /*if (action.definition instanceof GetLocalTypeIns) { - int regId = ((GetLocalTypeIns) action.definition).getRegisterId(action);//stack.peek().getResult().toString(); - if (!localData.localRegs.containsKey(regId)) { - break; - } - }*/ action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); InstructionDefinition def = action.definition; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorSimple.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java similarity index 94% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorSimple.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java index 9927570c4..397c659a1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorSimple.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java @@ -14,11 +14,14 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. */ -package com.jpexs.decompiler.flash.abc.avm2; +package com.jpexs.decompiler.flash.abc.avm2.deobfuscation; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.AVM2LocalData; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.FixItemCounterTranslateStack; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns; @@ -37,6 +40,11 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessThanIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.StrictEqualsIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; @@ -311,7 +319,14 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { EqualsIns.class, NotIns.class, IfTypeIns.class, - JumpIns.class + JumpIns.class, + EqualsIns.class, + LessEqualsIns.class, + GreaterEqualsIns.class, + GreaterThanIns.class, + LessThanIns.class, + StrictEqualsIns.class, + PopIns.class }; boolean ok = false; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index 1af07b3d3..956c30efd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -20,10 +20,11 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ABCInputStream; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.AVM2DeobfuscatorRegisters; -import com.jpexs.decompiler.flash.abc.avm2.AVM2DeobfuscatorSimple; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorRegisters; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorSimple; import com.jpexs.decompiler.flash.abc.avm2.CodeStats; import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorJumps; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.Traits; import com.jpexs.decompiler.flash.configuration.Configuration; @@ -203,7 +204,7 @@ public final class MethodBody implements Cloneable { if (exportMode != ScriptExportMode.AS) { getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); } else { - //if (!path.contains("@")) { + //if (!path.contains("RemoveAllPopup")) { if (!Configuration.decompile.get()) { writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); return; @@ -247,7 +248,7 @@ public final class MethodBody implements Cloneable { if (exportMode != ScriptExportMode.AS) { getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); } else { - //if (!path.contains("@")) { + //if (!path.contains("RemoveAllPopup")) { if (!Configuration.decompile.get()) { //writer.startMethod(this.method_info); writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); @@ -291,6 +292,8 @@ public final class MethodBody implements Cloneable { } else { new AVM2DeobfuscatorSimple().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b); new AVM2DeobfuscatorRegisters().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b); + new AVM2DeobfuscatorJumps().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java index 4003aa7a4..2090c8fbd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java @@ -49,7 +49,7 @@ public class MethodInfo { } } - public int[] param_types; + public int[] param_types = new int[]{}; public int ret_type; @@ -74,9 +74,9 @@ public class MethodInfo { public int flags; - public ValueKind[] optional; + public ValueKind[] optional = new ValueKind[0]; - public int[] paramNames; + public int[] paramNames = new int[0]; private MethodBody body; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java index ad8d72a54..f0167585b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -431,11 +431,12 @@ public class Graph { TranslateStack stack = new TranslateStack(path); List loops = new ArrayList<>(); getLoops(localData, heads.get(0), loops, null); - /*System.out.println(""); + /*1 + System.err.println(""); for (Loop el : loops) { - System.out.println(el); + System..println(el); } - System.out.println("");*/ + System.err.println("");*/ getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); /*System.err.println(""); for (Loop el : loops) { @@ -1378,7 +1379,7 @@ public class Graph { } if (el.phase != 1) { if (debugMode) { - //System.err.println("ignoring loop "+el); + System.err.println("ignoring loop " + el); } continue; } @@ -1418,18 +1419,9 @@ public class Graph { if (currentLoop != null) { currentLoop.phase = 0; } - /*switch (part.stopPartType) { - case AND_OR: - part.setAndOrStack(stack); //Save stack for later use - break; - - case COMMONPART: - part.setCommonPartStack(stack); //Save stack for later use - break; - - case NONE: - break; - }*/ + if (debugMode) { + System.err.println("Stopped on part " + part); + } return ret; } @@ -1562,19 +1554,19 @@ public class Graph { List nps; nps = part.nextParts; + boolean isEmpty = nps.get(0) == nps.get(1); + GraphPart next = getCommonPart(localData, nps, loops); TranslateStack trueStack = (TranslateStack) stack.clone(); TranslateStack falseStack = (TranslateStack) stack.clone(); trueStack.clear(); falseStack.clear(); - /*int trueStackSizeBefore = trueStack.size(); - int falseStackSizeBefore = falseStack.size(); - */ - boolean isEmpty = nps.get(0) == nps.get(1); if (isEmpty) { next = nps.get(0); } + boolean hasOntrue = nps.get(1) != next; + boolean hasOnFalse = nps.get(0) != next; List stopPart2 = new ArrayList<>(stopPart); @@ -1583,12 +1575,12 @@ public class Graph { } List onTrue = new ArrayList<>(); - if (!isEmpty) { + if (!isEmpty && hasOntrue) { onTrue = printGraph(visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); } List onFalse = new ArrayList<>(); - if (!isEmpty) { + if (!isEmpty && hasOnFalse) { onFalse = printGraph(visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); } //List out2 = new ArrayList<>(); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java index ff5e33f9a..9e2facc15 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java @@ -17,9 +17,14 @@ package com.jpexs.decompiler.flash; import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorJumps; import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.CodeFormatting; @@ -29,8 +34,10 @@ import com.jpexs.decompiler.graph.CompilationException; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; import java.util.concurrent.TimeoutException; +import org.testng.Assert; import static org.testng.Assert.fail; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; @@ -50,6 +57,39 @@ public class ActionScript3DeobfuscatorTest extends ActionStript2TestBase { swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); } + private String recompilePCode(String str) throws IOException, AVM2ParseException, InterruptedException { + str = "code\r\n" + + "getlocal_0\r\n" + + "pushscope\r\n" + + str + + "returnvoid\r\n"; + final ABC abc = new ABC(new ABCContainerTag() { + + @Override + public ABC getABC() { + return null; + } + + @Override + public SWF getSwf() { + return swf; + } + + @Override + public int compareTo(ABCContainerTag o) { + return 0; + } + }); + MethodBody b = new MethodBody(); + AVM2Code code = ASM3Parser.parse(new StringReader(str), abc.constants, null, b, new MethodInfo()); + b.setCode(code); + new AVM2DeobfuscatorJumps().deobfuscate("test", 0, true, 0, abc, abc.constants, null, new MethodInfo(), b); + HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false); + code.toASMSource(abc.constants, null, new MethodInfo(), new MethodBody(), ScriptExportMode.PCODE, writer); + String ret = writer.toString(); + return ret.substring(ret.lastIndexOf("code\r\n") + 6); + } + private String recompile(String str) throws AVM2ParseException, IOException, CompilationException, InterruptedException { str = "package { public class Test { public static function trace(s){ } public static function test(){ " + str + " } } }"; final ABC abc = new ABC(new ABCContainerTag() { @@ -182,6 +222,26 @@ public class ActionScript3DeobfuscatorTest extends ActionStript2TestBase { } } + @Test + public void testJumps() throws Exception { + String res = recompilePCode("pushbyte 3\r\n" + + "pushbyte 4\r\n" + + "ifeq a\r\n" //should change to ifeq c + + "jump b\r\n" //should not change + + "a:jump c\r\n" + + "c:pushbyte 4\r\n" + + "b:pushbyte 3\r\n"); + Assert.assertEquals(res, "getlocal_0\r\n" + + "pushscope\r\n" + + "pushbyte 3\r\n" + + "pushbyte 4\r\n" + + "ifeq ofs000e\r\n" + + "jump ofs0010\r\n" + + "ofs000e:pushbyte 4\r\n" + + "ofs0010:pushbyte 3\r\n" + + "returnvoid\r\n"); + } + // TODO: JPEXS @Test public void testNotRemoveParams() throws Exception { String res = recompile("function tst(p1,p2){"