From 3d9e2b76ed3ec3b9536bfecfc87578d4422b7e5f Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sat, 20 Sep 2014 19:23:04 +0200 Subject: [PATCH] AS2 deobfuscation fixes --- .../jpexs/decompiler/flash/action/Action.java | 46 +++++++++++++++++-- .../decompiler/flash/action/ActionList.java | 13 ++++++ .../deobfuscation/ActionDeobfuscator.java | 37 ++++++++++++--- .../flash/ActionScript2DeobfuscatorTest.java | 21 ++++----- 4 files changed, 94 insertions(+), 23 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java index 9e8e63995..c88995114 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action; import com.jpexs.decompiler.flash.AppResources; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.model.ActionItem; import com.jpexs.decompiler.flash.action.model.ConstantPool; @@ -54,7 +55,9 @@ import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.ecma.Null; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.flash.tags.base.ASMSource; @@ -391,6 +394,23 @@ public class Action implements GraphSourceItem { } } + /** + * Converts list of actions to ASM source + * + * @param listeners + * @param address + * @param list List of actions + * @param version SWF version + * @param exportMode PCode or hex? + * @return source ASM + * + */ + public static String actionsToString(List listeners, long address, ActionList list, int version, ScriptExportMode exportMode) { + HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); + actionsToString(listeners, address, list, version, exportMode, writer); + return writer.toString(); + } + /** * Converts list of actions to ASM source * @@ -671,6 +691,21 @@ public class Action implements GraphSourceItem { return actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, version, staticOperation, path); } + /** + * Converts list of actions to ActionScript source code + * + * @param asm + * @param actions List of actions + * @param path + * @return source + * @throws java.lang.InterruptedException + */ + public static String actionsToSource(final ASMSource asm, final List actions, final String path) throws InterruptedException { + HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); + actionsToSource(asm, actions, path, writer); + return writer.toString(); + } + /** * Converts list of actions to ActionScript source code * @@ -685,12 +720,13 @@ public class Action implements GraphSourceItem { List tree = null; Throwable convertException = null; int timeout = Configuration.decompilationTimeoutSingleMethod.get(); + final int version = asm == null ? SWF.DEFAULT_VERSION : asm.getSwf().version; try { tree = CancellableWorker.call(new Callable>() { @Override public List call() throws Exception { int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - List tree = actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, asm.getSwf().version, staticOperation, path); + List tree = actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, version, staticOperation, path); Graph.graphToString(tree, new NulWriter(), new LocalData()); return tree; } @@ -707,7 +743,9 @@ public class Action implements GraphSourceItem { } writer.continueMeasure(); - asm.getActionSourcePrefix(writer); + if (asm != null) { + asm.getActionSourcePrefix(writer); + } if (convertException == null) { Graph.graphToString(tree, writer, new LocalData()); } else if (convertException instanceof TimeoutException) { @@ -717,7 +755,9 @@ public class Action implements GraphSourceItem { Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error in: " + path, convertException); Helper.appendErrorComment(writer, convertException); } - asm.getActionSourceSuffix(writer); + if (asm != null) { + asm.getActionSourceSuffix(writer); + } } /** diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionList.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionList.java index d82b8231c..b29c116a2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionList.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionList.java @@ -186,4 +186,17 @@ public class ActionList extends ArrayList { } } + @Override + public String toString() { + return Action.actionsToString(new ArrayList(), 0, this, SWF.DEFAULT_VERSION, ScriptExportMode.PCODE); + } + + public String toSource() { + try { + return Action.actionsToSource(null, this, ""); + } catch (InterruptedException ex) { + Logger.getLogger(ActionList.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscator.java index 4d755d0dd..540d28be0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscator.java @@ -49,11 +49,15 @@ import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDecrement; import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; import com.jpexs.decompiler.flash.action.swf5.ActionModulo; import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; import com.jpexs.decompiler.flash.action.swf5.ActionReturn; +import com.jpexs.decompiler.flash.action.swf6.ActionGreater; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.helpers.SWFDecompilerListener; import com.jpexs.decompiler.graph.Graph; @@ -215,9 +219,10 @@ public class ActionDeobfuscator implements SWFDecompilerListener { return false; } + ActionConstantPool cPool = getConstantPool(actions); for (int i = 0; i < actions.size(); i++) { ExecutionResult result = new ExecutionResult(); - executeActions(actions, i, actions.size() - 1, result, fakeFunctions); + executeActions(actions, i, actions.size() - 1, cPool, result, fakeFunctions); if (result.idx != -1) { int newIstructionCount = 1; // jump @@ -285,12 +290,26 @@ public class ActionDeobfuscator implements SWFDecompilerListener { return false; } - private void executeActions(ActionList actions, int idx, int endIdx, ExecutionResult result, Map fakeFunctions) { + private ActionConstantPool getConstantPool(ActionList actions) { + ActionConstantPool cPool = null; + for (Action action : actions) { + if (action instanceof ActionConstantPool) { + if (cPool != null) { + // there are multiple constant pools + return null; + } + cPool = (ActionConstantPool) action; + } + } + return cPool; + } + + private void executeActions(ActionList actions, int idx, int endIdx, ActionConstantPool constantPool, ExecutionResult result, Map fakeFunctions) { List output = new ArrayList<>(); ActionLocalData localData = new ActionLocalData(); FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack(); int instructionsProcessed = 0; - ActionConstantPool constantPool = null; + ActionConstantPool lastConstantPool = null; try { while (true) { @@ -311,7 +330,7 @@ public class ActionDeobfuscator implements SWFDecompilerListener { } System.out.println();*/ if (action instanceof ActionConstantPool) { - constantPool = (ActionConstantPool) action; + lastConstantPool = (ActionConstantPool) action; } if (action instanceof ActionDefineLocal) { @@ -348,6 +367,8 @@ public class ActionDeobfuscator implements SWFDecompilerListener { || action instanceof ActionPushDuplicate || action instanceof ActionAdd || action instanceof ActionAdd2 + || action instanceof ActionIncrement + || action instanceof ActionDecrement || action instanceof ActionSubtract || action instanceof ActionModulo || action instanceof ActionMultiply @@ -361,6 +382,8 @@ public class ActionDeobfuscator implements SWFDecompilerListener { || action instanceof ActionGetVariable || action instanceof ActionSetVariable || action instanceof ActionEquals + || action instanceof ActionEquals2 + || action instanceof ActionGreater || action instanceof ActionNot || action instanceof ActionIf || action instanceof ActionConstantPool @@ -374,7 +397,7 @@ public class ActionDeobfuscator implements SWFDecompilerListener { ActionPush push = (ActionPush) action; boolean ok = true; for (Object value : push.values) { - if (value instanceof ConstantIndex || value instanceof RegisterNumber) { + if ((constantPool == null && value instanceof ConstantIndex) || value instanceof RegisterNumber) { ok = false; break; } @@ -418,7 +441,7 @@ public class ActionDeobfuscator implements SWFDecompilerListener { if (/*localData.variables.size() == 1 && */stack.allItemsFixed() || action instanceof ActionEnd) { result.idx = idx == actions.size() ? idx - 1 : idx; result.instructionsProcessed = instructionsProcessed; - result.constantPool = constantPool; + result.constantPool = lastConstantPool; result.variables.clear(); for (String variableName : localData.variables.keySet()) { Object value = localData.variables.get(variableName).getResult(); @@ -458,7 +481,7 @@ public class ActionDeobfuscator implements SWFDecompilerListener { ExecutionResult result = new ExecutionResult(); List lastActions = actions.getContainerLastActions(action); int lastActionIdx = actions.indexOf(lastActions.get(0)); - executeActions(actions, i + 1, lastActionIdx, result, null); + executeActions(actions, i + 1, lastActionIdx, null, result, null); if (result.resultValue != null) { results.put(def.functionName, result.resultValue); for (int j = i; j <= lastActionIdx; j++) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2DeobfuscatorTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2DeobfuscatorTest.java index 437938f72..6eb2bf6db 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2DeobfuscatorTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2DeobfuscatorTest.java @@ -60,7 +60,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { List actions = par.actionsFromString(str); byte[] hex = Action.actionsToBytes(actions, true, SWF.DEFAULT_VERSION); ActionList list = ActionListReader.readActionListTimeout(new ArrayList(), new SWFInputStream(swf,hex), SWF.DEFAULT_VERSION, 0, hex.length, ""); - Action.actionsToSource(getFirstActionTag(), list,"", writer); + Action.actionsToSource(null, list, "", writer); return writer.toString(); } @@ -80,12 +80,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { }; } - /* - //TODO: use this to make better deobfuscator: - - - - @Test(dataProvider = "provideBasicTrueExpressions") + // todo: honfika @Test(dataProvider = "provideBasicTrueExpressions") public void testRemoveBasicTrueExpressions(String expression) throws ActionParseException, IOException, CompilationException, InterruptedException, TimeoutException{ String res = recompile("if("+expression+"){"+ "trace(\"OK\");"+ @@ -100,7 +95,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { } } - @Test(dataProvider = "provideBasicFalseExpressions") + // todo: honfika @Test(dataProvider = "provideBasicFalseExpressions") public void testRemoveBasicFalseExpressions(String expression) throws Exception { String res = recompile("if("+expression+"){"+ "trace(\"FAIL\");"+ @@ -116,7 +111,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { } - @Test + // todo: honfika @Test public void testRemoveKnownVariables() throws Exception{ String res = recompile("var a = true; var b = false;" + "if(a){" @@ -149,7 +144,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { } } - @Test + // todo: honfika @Test public void testNotRemoveParams() throws Exception { String res = recompile("function tst(p1,p2){" + "var a = 2;" @@ -182,7 +177,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { @Test public void testEvailExpressionAfterWhile() throws Exception { - String res = "var a = 5;" + String res = recompile("var a = 5;" + "while(true){" + "if(a==73){" + "a = 15;" @@ -212,7 +207,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { + "trace(\"OK\");" + "}else{" + "trace(\"FAIL3\");" - + "}"; + + "}"); if(res.contains("\"FAIL1\"")){ fail("unreachable if onTrue not removed"); } @@ -226,7 +221,7 @@ public class ActionScript2DeobfuscatorTest extends ActionStript2TestBase { fail("reachable of onTrue removed"); } } - //*/ + @Test public void testRemoveJumpsToTheNextAction() { String actionsString = "ConstantPool \"a\" \"b\" \"c\"\n"