From 56bd4cd1f6145bf2001fcf84703d61886598ae14 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 1 Nov 2015 12:12:04 +0100 Subject: [PATCH] AS2 deobfuscation improvement --- .../src/com/jpexs/decompiler/flash/SWF.java | 2 - .../deobfuscation/ActionDeobfuscator.java | 327 ++++-------------- .../FixItemCounterTranslateStack.java | 59 ---- .../action/fastactionlist/ActionItem.java | 4 + .../flash/configuration/Configuration.java | 2 + .../com/jpexs/helpers/stat/Statistics.java | 9 +- .../console/CommandLineArgumentParser.java | 3 +- .../decompiler/flash/gui/FlashPlayerTest.java | 4 +- 8 files changed, 77 insertions(+), 333 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/FixItemCounterTranslateStack.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 4cbbebf5a..bbe5e538a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -88,7 +88,6 @@ import com.jpexs.decompiler.flash.tags.FileAttributesTag; import com.jpexs.decompiler.flash.tags.JPEGTablesTag; import com.jpexs.decompiler.flash.tags.MetadataTag; import com.jpexs.decompiler.flash.tags.ProtectTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; import com.jpexs.decompiler.flash.tags.ShowFrameTag; import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; @@ -126,7 +125,6 @@ import com.jpexs.decompiler.flash.treeitems.TreeItem; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.filters.BlendComposite; 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 1c1c71ab7..7ba8c3e7a 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 @@ -234,66 +234,67 @@ public class ActionDeobfuscator implements SWFDecompilerListener { } } - int unreachableCount = actions.getUnreachableActionCount(actionItem, result.item); - /*int unreachableCount = result.minSkippedInstructions; - if (unreachableCount <= newIstructionCount && newIstructionCount < result.maxSkippedInstructions) { - unreachableCount = actions.getUnreachableActionCount(actionItem, result.item); - }*/ - - if (allValueValid && newIstructionCount < unreachableCount) { - if (result.stack.isEmpty() && result.variables.isEmpty() && result.constantPool == null && actionItem.action instanceof ActionJump) { - actionItem.setJumpTarget(result.item); - } else { - ActionItem prevActionItem = actionItem.prev; - if (result.constantPool != null) { - ActionConstantPool constantPool2 = new ActionConstantPool(new ArrayList<>(result.constantPool.constantPool)); - ActionItem constantPoolItem = new ActionItem(constantPool2); - iterator.addBefore(constantPoolItem); - } - - for (String variableName : result.variables.keySet()) { - Object value = result.variables.get(variableName); - ActionPush push = new ActionPush(variableName); - ActionItem pushItem = new ActionItem(push); - iterator.addBefore(pushItem); - push = new ActionPush(value); - pushItem = new ActionItem(push); - iterator.addBefore(pushItem); - - if (result.defines.contains(variableName)) { - ActionDefineLocal defineLocal = new ActionDefineLocal(); - ActionItem defineLocalItem = new ActionItem(defineLocal); - iterator.addBefore(defineLocalItem); - } else { - ActionSetVariable setVariable = new ActionSetVariable(); - ActionItem setVariableItem = new ActionItem(setVariable); - iterator.addBefore(setVariableItem); - } - } - - if (!result.stack.isEmpty()) { - for (Object obj : result.stack) { - ActionPush push = new ActionPush(obj); - ActionItem pushItem = new ActionItem(push); - iterator.addBefore(pushItem); - } - } - - ActionJump jump = new ActionJump(0); - ActionItem jumpItem = new ActionItem(jump); - jumpItem.setJumpTarget(result.item); - iterator.addBefore(jumpItem); - - actions.replaceJumpTargets(actionItem, prevActionItem.next); + if (allValueValid && newIstructionCount < result.maxSkippedInstructions) { + int unreachableCount = result.minSkippedInstructions; + if (newIstructionCount >= result.minSkippedInstructions) { + unreachableCount = actions.getUnreachableActionCount(actionItem, result.item); } - ActionItem prevItem = actionItem.prev; + if (newIstructionCount < unreachableCount) { + if (result.stack.isEmpty() && result.variables.isEmpty() && result.constantPool == null && actionItem.action instanceof ActionJump) { + actionItem.setJumpTarget(result.item); + } else { + ActionItem prevActionItem = actionItem.prev; + if (result.constantPool != null) { + ActionConstantPool constantPool2 = new ActionConstantPool(new ArrayList<>(result.constantPool.constantPool)); + ActionItem constantPoolItem = new ActionItem(constantPool2); + iterator.addBefore(constantPoolItem); + } - actions.removeUnreachableActions(); - actions.removeZeroJumps(); + for (String variableName : result.variables.keySet()) { + Object value = result.variables.get(variableName); + ActionPush push = new ActionPush(variableName); + ActionItem pushItem = new ActionItem(push); + iterator.addBefore(pushItem); + push = new ActionPush(value); + pushItem = new ActionItem(push); + iterator.addBefore(pushItem); - iterator.setCurrent(prevItem.next.next); - ret = true; + if (result.defines.contains(variableName)) { + ActionDefineLocal defineLocal = new ActionDefineLocal(); + ActionItem defineLocalItem = new ActionItem(defineLocal); + iterator.addBefore(defineLocalItem); + } else { + ActionSetVariable setVariable = new ActionSetVariable(); + ActionItem setVariableItem = new ActionItem(setVariable); + iterator.addBefore(setVariableItem); + } + } + + if (!result.stack.isEmpty()) { + for (Object obj : result.stack) { + ActionPush push = new ActionPush(obj); + ActionItem pushItem = new ActionItem(push); + iterator.addBefore(pushItem); + } + } + + ActionJump jump = new ActionJump(0); + ActionItem jumpItem = new ActionItem(jump); + jumpItem.setJumpTarget(result.item); + iterator.addBefore(jumpItem); + + actions.replaceJumpTargets(actionItem, prevActionItem.next); + } + + ActionItem prevItem = actionItem.prev; + + actions.removeUnreachableActions(); + actions.removeZeroJumps(); + + iterator.setCurrent(prevItem.next.next); + ret = true; + } } } @@ -601,9 +602,9 @@ public class ActionDeobfuscator implements SWFDecompilerListener { instructionsProcessed++; if (!jumpFound) { - boolean isJumpTarget = prevItem.isJumpTarget(); + boolean isJumpTarget = prevItem.isJumpTarget() || prevItem.prev.isContainerLastAction(); if (isJumpTarget) { - if (prevJumpedHere && prevItem.jumpsHere.size() == 1) { + if (prevJumpedHere && prevItem.jumpsHereSize() == 1) { isJumpTarget = false; } } @@ -612,12 +613,14 @@ public class ActionDeobfuscator implements SWFDecompilerListener { jumpFound = true; } else { skippedInstructions++; + if (action instanceof ActionIf) { + jumpFound = true; + } } if (jumpedHere) { jumpFound = true; } - } } @@ -647,212 +650,6 @@ public class ActionDeobfuscator implements SWFDecompilerListener { } } - private void executeActionsOld(ActionItem item, LocalDataArea localData, ActionConstantPool constantPool, ExecutionResult result, Map fakeFunctions, boolean useVariables, boolean allowGetUninitializedVariables) throws InterruptedException { - FixItemCounterStack stack = (FixItemCounterStack) localData.stack; - int instructionsProcessed = 0; - ActionConstantPool lastConstantPool = null; - - while (true) { - if (item.isExcluded()) { - break; - } - - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - - if (instructionsProcessed > executionLimit) { - break; - } - - Action action = item.action; - - /*System.out.print(action.getASMSource(actions, new ArrayList(), ScriptExportMode.PCODE)); - for (int j = 0; j < stack.size(); j++) { - System.out.print(" '" + stack.get(j).getResult() + "'"); - } - System.out.println();*/ - if (action instanceof ActionConstantPool) { - lastConstantPool = (ActionConstantPool) action; - } - - if (action instanceof ActionDefineLocal) { - if (stack.size() < 2) { - break; - } - - String variableName = EcmaScript.toString(stack.peek(2)); - result.defines.add(variableName); - } - - if (action instanceof ActionGetVariable) { - if (stack.isEmpty()) { - break; - } - - String variableName = stack.peek().toString(); - if (!localData.localVariables.containsKey(variableName) - && (!allowGetUninitializedVariables || !isFakeName(variableName))) { - break; - } - } - - if (action instanceof ActionSetVariable) { - if (stack.size() < 2) { - break; - } - } - - if (action instanceof ActionCallFunction) { - if (stack.size() < 2) { - break; - } - - String functionName = stack.pop().toString(); - long numArgs = EcmaScript.toUint32(stack.pop()); - if (numArgs == 0) { - if (fakeFunctions != null && fakeFunctions.containsKey(functionName)) { - stack.push(fakeFunctions.get(functionName)); - } else { - break; - } - } else { - break; - } - } else { - Object o = null; - if (action instanceof ActionIf) { - if (!stack.isEmpty()) { - o = stack.peek(); - } - } - - if (!action.execute(localData)) { - break; - } - - if (o != null) { - stack.push(o); - } - } - - if (!useVariables && (action instanceof ActionDefineLocal - || action instanceof ActionGetVariable - || action instanceof ActionSetVariable - || action instanceof ActionConstantPool - || action instanceof ActionCallFunction - || action instanceof ActionReturn - || action instanceof ActionEnd)) { - break; - } - - if (!(action instanceof ActionPush - || action instanceof ActionPushDuplicate - //|| action instanceof ActionPop - || action instanceof ActionAsciiToChar - || action instanceof ActionCharToAscii - || action instanceof ActionDecrement - || action instanceof ActionIncrement - || action instanceof ActionNot - || action instanceof ActionToInteger - || action instanceof ActionToNumber - || action instanceof ActionToString - || action instanceof ActionTypeOf - || action instanceof ActionStringLength - || action instanceof ActionMBAsciiToChar - || action instanceof ActionMBStringLength - || action instanceof ActionAnd - || action instanceof ActionAdd - || action instanceof ActionAdd2 - || action instanceof ActionBitAnd - || action instanceof ActionBitLShift - || action instanceof ActionBitOr - || action instanceof ActionBitRShift - || action instanceof ActionBitURShift - || action instanceof ActionBitXor - || action instanceof ActionDivide - || action instanceof ActionEquals - || action instanceof ActionEquals2 - || action instanceof ActionGreater - || action instanceof ActionLess - || action instanceof ActionLess2 // todo: fix (tz.swf/frame_6/DoAction: _loc3_.icon.gotoAndStop((Number(item.cost) || 0) >= 0?1:2) - || action instanceof ActionModulo - || action instanceof ActionMultiply - || action instanceof ActionOr - || action instanceof ActionStringAdd - || action instanceof ActionStringEquals - || action instanceof ActionStringGreater - || action instanceof ActionStringLess - || action instanceof ActionSubtract - || action instanceof ActionIf - || action instanceof ActionJump - || action instanceof ActionDefineLocal - || action instanceof ActionGetVariable - || action instanceof ActionSetVariable - || action instanceof ActionConstantPool - || action instanceof ActionCallFunction - || action instanceof ActionReturn - || action instanceof ActionEnd)) { - break; - } - - if (action instanceof ActionPush) { - ActionPush push = (ActionPush) action; - boolean ok = true; - instructionsProcessed += push.values.size() - 1; - for (Object value : push.values) { - if ((constantPool == null && value instanceof ConstantIndex) || value instanceof RegisterNumber) { - ok = false; - break; - } - } - if (!ok) { - break; - } - } - - ActionItem prevItem = item; - if (action instanceof ActionJump) { - item = item.getJumpTarget(); - } else if (action instanceof ActionIf) { - if (stack.isEmpty()) { - break; - } - - if (EcmaScript.toBoolean(stack.pop())) { - item = item.getJumpTarget(); - } else { - item = item.next; - } - } else { - item = item.next; - } - - instructionsProcessed++; - - if ((stack.allItemsFixed() || action instanceof ActionEnd) && !(action instanceof ActionPush)) { - result.item = item; - result.instructionsProcessed = instructionsProcessed; - result.constantPool = lastConstantPool; - result.variables.clear(); - for (String variableName : localData.localVariables.keySet()) { - Object value = localData.localVariables.get(variableName); - result.variables.put(variableName, value); - } - result.stack.clear(); - result.stack.addAll(stack); - } - - if (action instanceof ActionReturn) { - result.resultValue = localData.returnValue; - break; - } else if (action instanceof ActionEnd) { - result.item = prevItem; - break; - } - } - } - @Override public void actionTreeCreated(List tree, SWF swf) { if (tree.size() > 0) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/FixItemCounterTranslateStack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/FixItemCounterTranslateStack.java deleted file mode 100644 index 677710042..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/FixItemCounterTranslateStack.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.action.deobfuscation; - -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.TranslateStack; - -/** - * - * @author JPEXS - */ -public class FixItemCounterTranslateStack extends TranslateStack { - - private int fixItemCount = Integer.MAX_VALUE; - - public FixItemCounterTranslateStack(String path) { - super(null); //null path => do not add PushItems - } - - @Override - public GraphTargetItem pop() { - GraphTargetItem result = super.pop(); - int itemCount = size(); - if (itemCount < fixItemCount) { - fixItemCount = itemCount; - } - return result; - } - - @Override - public synchronized GraphTargetItem remove(int index) { - if (index < fixItemCount) { - fixItemCount = index; - } - return super.remove(index); - } - - public boolean allItemsFixed() { - return size() <= fixItemCount; - } - - public int getFixItemCount() { - return fixItemCount; - } -} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/ActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/ActionItem.java index 8d85cc1a2..6d56af518 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/ActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/ActionItem.java @@ -54,6 +54,10 @@ public class ActionItem { return jumpsHere != null && !jumpsHere.isEmpty(); } + public int jumpsHereSize() { + return jumpsHere == null ? 0 : jumpsHere.size(); + } + public boolean isContainerLastAction() { return lastActionOf != null && !lastActionOf.isEmpty(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 468fd8c82..c1bdf2c98 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -70,6 +70,8 @@ public class Configuration { public static final Level logLevel; + public static boolean showStat; + @ConfigurationDefaultBoolean(true) @ConfigurationCategory("ui") public static final ConfigurationItem openMultipleFiles = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/stat/Statistics.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/stat/Statistics.java index 62369648b..80198450c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/stat/Statistics.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/stat/Statistics.java @@ -16,6 +16,7 @@ */ package com.jpexs.helpers.stat; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.helpers.Stopwatch; import java.util.HashMap; import java.util.Map; @@ -94,12 +95,14 @@ public class Statistics implements AutoCloseable { public Statistics(String name) { this.name = name; - sw = Stopwatch.startNew(); + sw = Configuration.showStat ? Stopwatch.startNew() : null; } @Override public void close() { - sw.stop(); - addTime(name, sw.getElapsedNanoseconds()); + if (sw != null) { + sw.stop(); + addTime(name, sw.getElapsedNanoseconds()); + } } } diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 0735d5ab7..45817a7cd 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -142,7 +142,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -988,6 +987,7 @@ public class CommandLineArgumentParser { private static void parseStat(Stack args) { showStat = true; + Configuration.showStat = showStat; } private static void parseStdOut(Stack args) { @@ -2857,5 +2857,4 @@ public class CommandLineArgumentParser { } return vals[0]; } - } diff --git a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java index 0c4d8d623..2db40af5e 100644 --- a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java +++ b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java @@ -26,7 +26,6 @@ import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.ActionLocalData; import com.jpexs.decompiler.flash.action.LocalDataArea; -import com.jpexs.decompiler.flash.action.deobfuscation.FixItemCounterTranslateStack; import com.jpexs.decompiler.flash.action.swf4.ActionAdd; import com.jpexs.decompiler.flash.action.swf4.ActionAnd; import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; @@ -75,6 +74,7 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.javactivex.ActiveX; import com.jpexs.javactivex.ActiveXEvent; import com.jpexs.javactivex.ActiveXEventListener; @@ -269,7 +269,7 @@ public class FlashPlayerTest { List output = new ArrayList<>(); ActionLocalData localData = new ActionLocalData(); - FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack(""); + TranslateStack stack = new TranslateStack(""); for (Action a : newActions) { a.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); }