From e8425fcadd582b523fc55ee4e2376f6162d08a8e Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Wed, 7 Oct 2015 15:53:36 +0200 Subject: [PATCH] faster "removeGetTimes" --- .../flash/action/ActionListReader.java | 4 + .../flash/action/FastActionList.java | 235 ------- .../ActionDeobfuscatorSimple.java | 11 +- .../ActionDeobfuscatorSimpleFast.java | 389 +++++++++++ .../action/fastactionlist/ActionItem.java | 44 ++ .../action/fastactionlist/FastActionList.java | 610 ++++++++++++++++++ .../FastActionListIterator.java | 80 +++ 7 files changed, 1130 insertions(+), 243 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/FastActionList.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimpleFast.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/ActionItem.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionListIterator.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java index 37705c33e..e07794d95 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscator; import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscatorSimple; +import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscatorSimpleFast; import com.jpexs.decompiler.flash.action.model.ConstantPool; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.special.ActionDeobfuscateJump; @@ -180,6 +181,9 @@ public class ActionListReader { } } else if (deobfuscationMode == 1) { try { + try (Statistics s = new Statistics("ActionDeobfuscatorSimpleFast")) { + new ActionDeobfuscatorSimpleFast().actionListParsed(actions, sis.getSwf()); + } try (Statistics s = new Statistics("ActionDeobfuscatorSimple")) { new ActionDeobfuscatorSimple().actionListParsed(actions, sis.getSwf()); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/FastActionList.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/FastActionList.java deleted file mode 100644 index 00b28fdc5..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/FastActionList.java +++ /dev/null @@ -1,235 +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; - -import com.jpexs.decompiler.flash.action.special.ActionStore; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionJump; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.graph.GraphSourceItemContainer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; - -/** - * - * @author JPEXS - */ -public class FastActionList { - - private LinkedList actions; - - private Map> containerLastActions; - - private Map jumps; - - public FastActionList(ActionList actions) { - this.actions = new LinkedList<>(actions); - - Map> containerLastActions = new HashMap<>(); - getContainerLastActions(this.actions, actions, containerLastActions); - this.containerLastActions = containerLastActions; - - Map jumps = new HashMap<>(); - getJumps(actions, jumps); - this.jumps = jumps; - } - - private void getContainerLastActions(LinkedList linkedActions, ActionList actions, Map> lastActions) { - for (Action a : linkedActions) { - if (a instanceof GraphSourceItemContainer) { - lastActions.put(a, getContainerLastActions(actions, a)); - } - } - } - - private List getContainerLastActions(ActionList actions, Action action) { - GraphSourceItemContainer container = (GraphSourceItemContainer) action; - List sizes = container.getContainerSizes(); - long endAddress = action.getAddress() + container.getHeaderSize(); - List lasts = new ArrayList<>(sizes.size()); - for (long size : sizes) { - endAddress += size; - long lastActionAddress = getNearAddress(actions, endAddress - 1, false); - Action lastAction = null; - if (lastActionAddress != -1) { - lastAction = actions.getByAddress(lastActionAddress); - } - lasts.add(lastAction); - } - return lasts; - } - - private long getNearAddress(ActionList actions, long address, boolean next) { - int min = 0; - int max = actions.size() - 1; - - while (max >= min) { - int mid = (min + max) / 2; - long midValue = actions.get(mid).getAddress(); - if (midValue == address) { - return address; - } else if (midValue < address) { - min = mid + 1; - } else { - max = mid - 1; - } - } - - return next - ? (min < actions.size() ? actions.get(min).getAddress() : -1) - : (max >= 0 ? actions.get(max).getAddress() : -1); - } - - private static void getJumps(ActionList actions, Map jumps) { - for (Action a : actions) { - long target = -1; - if (a instanceof ActionIf) { - target = ((ActionIf) a).getTargetAddress(); - } else if (a instanceof ActionJump) { - target = ((ActionJump) a).getTargetAddress(); - } else if (a instanceof ActionStore) { - ActionStore aStore = (ActionStore) a; - int storeSize = aStore.getStoreSize(); - // skip storeSize + 1 actions (+1 is the current action) - Action targetAction = a; - for (int i = 0; i <= storeSize; i++) { - long address = targetAction.getAddress() + targetAction.getTotalActionLength(); - targetAction = actions.getByAddress(address); - if (targetAction == null) { - break; - } - } - jumps.put(a, targetAction); - } - if (target >= 0) { - Action targetAction = actions.getByAddress(target); - jumps.put(a, targetAction); - } - } - } - - private void updateJumps(Map jumps) { - if (actions.isEmpty()) { - return; - } - - long endAddress = actions.getLast().getAddress(); - for (Action a : actions) { - if (a instanceof ActionIf) { - ActionIf aIf = (ActionIf) a; - Action target = jumps.get(a); - long offset; - if (target != null) { - offset = target.getAddress() - a.getAddress() - a.getTotalActionLength(); - } else { - offset = endAddress - a.getAddress() - a.getTotalActionLength(); - } - aIf.setJumpOffset((int) offset); - } else if (a instanceof ActionJump) { - ActionJump aJump = (ActionJump) a; - Action target = jumps.get(a); - long offset; - if (target != null) { - offset = target.getAddress() - a.getAddress() - a.getTotalActionLength(); - } else { - offset = endAddress - a.getAddress() - a.getTotalActionLength(); - } - aJump.setJumpOffset((int) offset); - } - } - } - - private void updateActionStores(ActionList actionList, Map jumps) { - for (Action a : actions) { - if (a instanceof ActionStore) { - ActionStore aStore = (ActionStore) a; - Action nextActionAfterStore = jumps.get(a); - Action a1 = a; - List store = new ArrayList<>(); - while (true) { - long address = a1.getAddress() + a1.getTotalActionLength(); - a1 = actionList.getByAddress(address); - if (a1 == null || a1 == nextActionAfterStore) { - break; - } - store.add(a1); - } - aStore.setStore(store); - } - } - } - - private void updateContainerSizes(Map> containerLastActions) { - for (Action a : actions) { - if (a instanceof GraphSourceItemContainer) { - GraphSourceItemContainer container = (GraphSourceItemContainer) a; - List lastActions = containerLastActions.get(a); - long startAddress = a.getAddress() + container.getHeaderSize(); - for (int j = 0; j < lastActions.size(); j++) { - Action lastAction = lastActions.get(j); - int length = (int) (lastAction.getAddress() + lastAction.getTotalActionLength() - startAddress); - container.setContainerSize(j, length); - startAddress += length; - } - } - } - } - - public void expandPushes() { - ListIterator iterator = actions.listIterator(); - while (iterator.hasNext()) { - Action action = iterator.next(); - if (action instanceof ActionPush) { - ActionPush push = (ActionPush) action; - if (push.values.size() > 1) { - for (int i = 1; i < push.values.size(); i++) { - Object value = push.values.get(i); - ActionPush newPush = new ActionPush(value); - newPush.constantPool = push.constantPool; - iterator.add(newPush); - } - - Object obj = push.values.get(0); - push.values.clear(); - push.values.add(obj); - } - } - } - } - - private void updateActionAddressesAndLengths() { - long address = actions.get(0).getAddress(); - for (Action action : actions) { - action.setAddress(address); - action.updateLength(); - address += action.getTotalActionLength(); - } - } - - public ActionList toActionList() { - ActionList result = new ActionList(actions); - updateActionAddressesAndLengths(); - updateJumps(jumps); - updateActionStores(result, jumps); - updateContainerSizes(containerLastActions); - return result; - } -} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimple.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimple.java index 036303908..76370760e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimple.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimple.java @@ -22,7 +22,6 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody; 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.FastActionList; import com.jpexs.decompiler.flash.action.swf4.ActionAdd; import com.jpexs.decompiler.flash.action.swf4.ActionAnd; import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; @@ -84,11 +83,7 @@ public class ActionDeobfuscatorSimple implements SWFDecompilerListener { @Override public void actionListParsed(ActionList actions, SWF swf) throws InterruptedException { - FastActionList fastActions = new FastActionList(actions); - fastActions.expandPushes(); - actions.setActions(fastActions.toActionList()); - - removeGetTimes(actions); + //removeGetTimes(actions); removeObfuscationIfs(actions); } @@ -370,7 +365,7 @@ public class ActionDeobfuscatorSimple implements SWFDecompilerListener { if (action instanceof ActionJump) { ActionJump jump = (ActionJump) action; - long address = jump.getAddress() + jump.getTotalActionLength() + jump.getJumpOffset(); + long address = jump.getTargetAddress(); idx = actions.getIndexByAddress(address); if (idx == -1) { throw new TranslateException("Jump target not found: " + address); @@ -384,7 +379,7 @@ public class ActionDeobfuscatorSimple implements SWFDecompilerListener { } if (EcmaScript.toBoolean(stack.pop().getResult())) { - long address = aif.getAddress() + aif.getTotalActionLength() + aif.getJumpOffset(); + long address = aif.getTargetAddress(); idx = actions.getIndexByAddress(address); if (idx == -1) { throw new TranslateException("If target not found: " + address); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimpleFast.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimpleFast.java new file mode 100644 index 000000000..126737e34 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/deobfuscation/ActionDeobfuscatorSimpleFast.java @@ -0,0 +1,389 @@ +/* + * 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.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +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.fastactionlist.ActionItem; +import com.jpexs.decompiler.flash.action.fastactionlist.FastActionList; +import com.jpexs.decompiler.flash.action.fastactionlist.FastActionListIterator; +import com.jpexs.decompiler.flash.action.swf4.ActionAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionAnd; +import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; +import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii; +import com.jpexs.decompiler.flash.action.swf4.ActionDivide; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetTime; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar; +import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength; +import com.jpexs.decompiler.flash.action.swf4.ActionMultiply; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionOr; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionStringEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLength; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; +import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; +import com.jpexs.decompiler.flash.action.swf4.ActionToInteger; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; +import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; +import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd; +import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitOr; +import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; +import com.jpexs.decompiler.flash.action.swf5.ActionDecrement; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.action.swf5.ActionModulo; +import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; +import com.jpexs.decompiler.flash.action.swf5.ActionToNumber; +import com.jpexs.decompiler.flash.action.swf5.ActionToString; +import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf; +import com.jpexs.decompiler.flash.action.swf6.ActionGreater; +import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerListener; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateException; +import com.jpexs.decompiler.graph.TranslateStack; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ActionDeobfuscatorSimpleFast implements SWFDecompilerListener { + + private final int executionLimit = 30000; + + @Override + public void actionListParsed(ActionList actions, SWF swf) throws InterruptedException { + FastActionList fastActions = new FastActionList(actions); + fastActions.expandPushes(); + removeGetTimes(fastActions); + //removeObfuscationIfs(fastActions); + actions.setActions(fastActions.toActionList()); + } + + private boolean removeGetTimes(FastActionList actions) { + if (actions.isEmpty()) { + return false; + } + + boolean changed = true; + int getTimeCount = 1; + while (changed && getTimeCount > 0) { + changed = false; + actions.removeUnreachableActions(); + actions.removeZeroJumps(); + getTimeCount = 0; + + // GetTime, If => Jump, assume GetTime > 0 + FastActionListIterator iterator = actions.iterator(); + while (iterator.hasNext()) { + Action a = iterator.next().action; + ActionItem a2Item = iterator.peek(0); + Action a2 = a2Item.action; + boolean isGetTime = a instanceof ActionGetTime; + if (isGetTime) { + getTimeCount++; + } + + if (isGetTime && a2 instanceof ActionIf) { + ActionJump jump = new ActionJump(0); + ActionItem jumpItem = new ActionItem(jump); + jumpItem.jumpTarget = a2Item.jumpTarget; + iterator.remove(); // GetTime + iterator.next(); + iterator.remove(); // If + iterator.add(jumpItem); // replace If with Jump + changed = true; + getTimeCount--; + break; + } + } + + if (!changed && getTimeCount > 0) { + // GetTime, Increment If => Jump + iterator = actions.iterator(); + while (iterator.hasNext()) { + Action a = iterator.next().action; + Action a1 = iterator.peek(0).action; + ActionItem a2Item = iterator.peek(1); + Action a2 = a2Item.action; + if (a instanceof ActionGetTime && a1 instanceof ActionIncrement && a2 instanceof ActionIf) { + ActionJump jump = new ActionJump(0); + ActionItem jumpItem = new ActionItem(jump); + jumpItem.jumpTarget = a2Item.jumpTarget; + iterator.remove(); // GetTime + iterator.next(); + iterator.remove(); // Increment + iterator.next(); + iterator.remove(); // If + iterator.add(jumpItem); // replace If with Jump + changed = true; + break; + } + } + } + } + + return false; + } + +// private boolean removeObfuscationIfs(FastActionList actions) throws InterruptedException { +// if (actions.isEmpty()) { +// return false; +// } +// +// actions.removeUnreachableActions(); +// actions.removeZeroJumps(); +// +// FastActionListIterator iterator = actions.iterator(); +// while (iterator.hasNext()) { +// ActionItem actionItem = iterator.next(); +// ExecutionResult result = new ExecutionResult(); +// executeActions(actions, i, actions.size() - 1, result); +// +// if (result.idx != -1) { +// int newIstructionCount = 1 /*jump */ + result.stack.size(); +// List unreachable = actions.getUnreachableActions(i, result.idx); +// int unreachableCount = unreachable.size(); +// +// if (newIstructionCount < unreachableCount) { +// Action target = actions.get(result.idx); +// Action prevAction = actions.get(i); +// +// if (result.stack.isEmpty() && prevAction instanceof ActionJump) { +// ActionJump jump = (ActionJump) prevAction; +// jump.setJumpOffset((int) (target.getAddress() - jump.getAddress() - jump.getTotalActionLength())); +// } else { +// if (!result.stack.isEmpty()) { +// ActionPush push = new ActionPush(0); +// push.values.clear(); +// for (GraphTargetItem graphTargetItem : result.stack) { +// push.values.add(graphTargetItem.getResult()); +// } +// push.setAddress(prevAction.getAddress()); +// actions.addAction(i++, push); +// prevAction = push; +// } +// +// ActionJump jump = new ActionJump(0); +// jump.setAddress(prevAction.getAddress()); +// jump.setJumpOffset((int) (target.getAddress() - jump.getAddress() - jump.getTotalActionLength())); +// actions.addAction(i++, jump); +// } +// +// Action nextAction = actions.size() > i ? actions.get(i) : null; +// +// actions.removeUnreachableActions(); +// actions.removeZeroJumps(); +// +// if (nextAction != null) { +// int nextIdx = actions.indexOf(nextAction); +// if (nextIdx < i) { +// i = nextIdx; +// } +// } +// } +// } +// } +// +// return false; +// } + protected boolean isFakeName(String name) { + for (char ch : name.toCharArray()) { + if (ch > 31) { + return false; + } + } + + return true; + } + + private void executeActions(ActionList actions, int idx, int endIdx, ExecutionResult result) throws InterruptedException { + List output = new ArrayList<>(); + ActionLocalData localData = new ActionLocalData(); + FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack(""); + int instructionsProcessed = 0; + + while (true) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + + if (idx > endIdx) { + break; + } + + if (instructionsProcessed > executionLimit) { + break; + } + + Action action = actions.get(idx); + + /*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();*/ + // do not throw EmptyStackException, much faster + int requiredStackSize = action.getStackPopCount(localData, stack); + if (stack.size() < requiredStackSize) { + return; + } + + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + + 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)) { + break; + } + + if (action instanceof ActionPush) { + ActionPush push = (ActionPush) action; + boolean ok = true; + instructionsProcessed += push.values.size() - 1; + for (Object value : push.values) { + if (value instanceof ConstantIndex || value instanceof RegisterNumber) { + ok = false; + break; + } + } + if (!ok) { + break; + } + } + + idx++; + + if (action instanceof ActionJump) { + ActionJump jump = (ActionJump) action; + long address = jump.getTargetAddress(); + idx = actions.getIndexByAddress(address); + if (idx == -1) { + throw new TranslateException("Jump target not found: " + address); + } + } + + if (action instanceof ActionIf) { + ActionIf aif = (ActionIf) action; + if (stack.isEmpty()) { + return; + } + + if (EcmaScript.toBoolean(stack.pop().getResult())) { + long address = aif.getTargetAddress(); + idx = actions.getIndexByAddress(address); + if (idx == -1) { + throw new TranslateException("If target not found: " + address); + } + } + } + + instructionsProcessed++; + + if (stack.allItemsFixed() && !(action instanceof ActionPush)) { + result.idx = idx == actions.size() ? idx - 1 : idx; + result.instructionsProcessed = instructionsProcessed; + result.stack.clear(); + result.stack.addAll(stack); + } + } + } + + @Override + public byte[] proxyFileCatched(byte[] data) { + return null; + } + + @Override + public void swfParsed(SWF swf) { + } + + @Override + public void abcParsed(ABC abc, SWF swf) { + } + + @Override + public void methodBodyParsed(MethodBody body, SWF swf) { + } + + class ExecutionResult { + + public int idx = -1; + + public int instructionsProcessed = -1; + + public TranslateStack stack = new TranslateStack("?"); + + public Object resultValue; + } +} 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 new file mode 100644 index 000000000..b7d05ba64 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/ActionItem.java @@ -0,0 +1,44 @@ +/* + * 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.fastactionlist; + +import com.jpexs.decompiler.flash.action.Action; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ActionItem { + + public Action action; + + public ActionItem prev; + + public ActionItem next; + + public ActionItem jumpTarget; + + public List containerLastActions; + + // 1 means reachable, 2 means reachable and processed + int reachable; + + public ActionItem(Action action) { + this.action = action; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java new file mode 100644 index 000000000..93ef0b283 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java @@ -0,0 +1,610 @@ +/* + * 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.fastactionlist; + +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionList; +import com.jpexs.decompiler.flash.action.special.ActionStore; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class FastActionList implements Collection { + + private int size; + + private ActionItem firstAction; + + private final Map actionItemMap; + + private final Set actionItemSet; + + public FastActionList(ActionList actions) { + actionItemMap = new HashMap<>(actions.size()); + actionItemSet = new HashSet<>(actions.size()); + for (Action action : actions) { + insertItemAfter(null, action); + } + + size = actions.size(); + getContainerLastActions(actions, actionItemMap); + getJumps(actions, actionItemMap); + } + + final ActionItem insertItemAfter(ActionItem item, Action action) { + ActionItem newItem = new ActionItem(action); + return insertItemAfter(item, newItem); + } + + final ActionItem insertItemAfter(ActionItem item, ActionItem newItem) { + if (item == null && firstAction == null) { + firstAction = newItem; + newItem.next = newItem; + newItem.prev = newItem; + } else { + if (item == null) { + // insert to the end + item = firstAction.prev; + } + + ActionItem oldNext = item.next; + newItem.prev = item; + newItem.next = oldNext; + item.next = newItem; + oldNext.prev = newItem; + } + + size++; + actionItemMap.put(newItem.action, newItem); + actionItemSet.add(newItem); + return newItem; + } + + ActionItem removeItem(ActionItem item) { + ActionItem next = null; + if (item == firstAction) { + if (item.next == item) { + // there is only 1 item + firstAction = null; + } else { + next = item.next; + firstAction = next; + next.prev = item.prev; + item.prev.next = next; + } + } else { + next = item.next; + item.prev.next = next; + next.prev = item.prev; + } + + size--; + actionItemMap.remove(item.action); + actionItemSet.remove(item); + return next; + } + + private void getContainerLastActions(ActionList actions, Map actionItemMap) { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof GraphSourceItemContainer) { + item.containerLastActions = getContainerLastActions(actions, action, actionItemMap); + } + + item = item.next; + } while (item != firstAction); + } + + private List getContainerLastActions(ActionList actions, Action action, Map actionItemMap) { + GraphSourceItemContainer container = (GraphSourceItemContainer) action; + List sizes = container.getContainerSizes(); + long endAddress = action.getAddress() + container.getHeaderSize(); + List lasts = new ArrayList<>(sizes.size()); + for (long size : sizes) { + endAddress += size; + long lastActionAddress = getNearAddress(actions, endAddress - 1, false); + Action lastAction = null; + if (lastActionAddress != -1) { + lastAction = actions.getByAddress(lastActionAddress); + } + + if (lastAction != null) { + lasts.add(actionItemMap.get(lastAction)); + } else { + lasts.add(null); + } + } + return lasts; + } + + private long getNearAddress(ActionList actions, long address, boolean next) { + int min = 0; + int max = actions.size() - 1; + + while (max >= min) { + int mid = (min + max) / 2; + long midValue = actions.get(mid).getAddress(); + if (midValue == address) { + return address; + } else if (midValue < address) { + min = mid + 1; + } else { + max = mid - 1; + } + } + + return next + ? (min < actions.size() ? actions.get(min).getAddress() : -1) + : (max >= 0 ? actions.get(max).getAddress() : -1); + } + + private void getJumps(ActionList actions, Map actionItemMap) { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + Action action = item.action; + long target = -1; + if (action instanceof ActionIf) { + target = ((ActionIf) action).getTargetAddress(); + } else if (action instanceof ActionJump) { + target = ((ActionJump) action).getTargetAddress(); + } else if (action instanceof ActionStore) { + ActionStore aStore = (ActionStore) action; + int storeSize = aStore.getStoreSize(); + // skip storeSize + 1 actions (+1 is the current action) + Action targetAction = action; + for (int i = 0; i <= storeSize; i++) { + long address = targetAction.getAddress() + targetAction.getTotalActionLength(); + targetAction = actions.getByAddress(address); + if (targetAction == null) { + break; + } + } + + item.jumpTarget = actionItemMap.get(targetAction); + } + if (target >= 0) { + Action targetAction = actions.getByAddress(target); + item.jumpTarget = actionItemMap.get(targetAction); + } + + item = item.next; + } while (item != firstAction); + } + + private void updateActionAddressesAndLengths() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + long address = item.action.getAddress(); + do { + Action action = item.action; + action.setAddress(address); + action.updateLength(); + address += action.getTotalActionLength(); + item = item.next; + } while (item != firstAction); + } + + private void updateJumps() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + long endAddress = item.prev.action.getAddress(); + do { + Action action = item.action; + if (action instanceof ActionIf) { + ActionIf aIf = (ActionIf) action; + Action target = item.jumpTarget == null ? null : item.jumpTarget.action; + long offset; + if (target != null) { + offset = target.getAddress() - action.getAddress() - action.getTotalActionLength(); + } else { + offset = endAddress - action.getAddress() - action.getTotalActionLength(); + } + aIf.setJumpOffset((int) offset); + } else if (action instanceof ActionJump) { + ActionJump aJump = (ActionJump) action; + Action target = item.jumpTarget == null ? null : item.jumpTarget.action; + long offset; + if (target != null) { + offset = target.getAddress() - action.getAddress() - action.getTotalActionLength(); + } else { + offset = endAddress - action.getAddress() - action.getTotalActionLength(); + } + aJump.setJumpOffset((int) offset); + } + + item = item.next; + } while (item != firstAction); + } + + private void updateActionStores(ActionList actionList) { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof ActionStore) { + ActionStore aStore = (ActionStore) action; + Action nextActionAfterStore = item.jumpTarget == null ? null : item.jumpTarget.action; + Action a1 = action; + List store = new ArrayList<>(); + while (true) { + long address = a1.getAddress() + a1.getTotalActionLength(); + a1 = actionList.getByAddress(address); + if (a1 == null || a1 == nextActionAfterStore) { + break; + } + store.add(a1); + } + aStore.setStore(store); + } + + item = item.next; + } while (item != firstAction); + } + + private void updateContainerSizes() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof GraphSourceItemContainer) { + GraphSourceItemContainer container = (GraphSourceItemContainer) action; + List lastActions = item.containerLastActions; + long startAddress = action.getAddress() + container.getHeaderSize(); + for (int j = 0; j < lastActions.size(); j++) { + Action lastAction = lastActions.get(j).action; + int length = (int) (lastAction.getAddress() + lastAction.getTotalActionLength() - startAddress); + container.setContainerSize(j, length); + startAddress += length; + } + } + + item = item.next; + } while (item != firstAction); + } + + public void expandPushes() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof ActionPush) { + ActionPush push = (ActionPush) action; + if (push.values.size() > 1) { + for (int i = 1; i < push.values.size(); i++) { + Object value = push.values.get(i); + ActionPush newPush = new ActionPush(value); + newPush.constantPool = push.constantPool; + insertItemAfter(item, newPush); + item = item.next; + } + + Object obj = push.values.get(0); + push.values.clear(); + push.values.add(obj); + } + } + + item = item.next; + } while (item != firstAction); + } + + public void removeZeroJumps() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof ActionJump) { + if (item.jumpTarget == item.next && item.jumpTarget != firstAction) { + item = removeItem(item); + continue; + } + } + + item = item.next; + } while (item != firstAction); + } + + public void removeUnreachableActions() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + updateReachableFlags(null, null); + + do { + if (item.reachable == 0) { + item = removeItem(item); + continue; + } + + item = item.next; + } while (item != firstAction); + } + + private void clearReachableFlags() { + ActionItem item = firstAction; + if (item == null) { + return; + } + + do { + item.reachable = 0; + item = item.next; + } while (item != firstAction); + } + + private void updateReachableFlags(ActionItem jump, ActionItem jumpTarget) { + if (firstAction == null) { + return; + } + + clearReachableFlags(); + + firstAction.reachable = 1; + boolean modified = true; + while (modified) { + modified = false; + int i = 0; + FastActionListIterator iterator = iterator(); + while (iterator.hasNext()) { + ActionItem item = iterator.next(); + Action action = item.action; + if (item.reachable == 1) { + item.reachable = 2; + modified = true; + + if (item == jump) { + if (jumpTarget.reachable == 0) { + jumpTarget.reachable = 1; + } + + continue; + } + + if (!action.isExit() && !(action instanceof ActionJump) && item.next != null) { + if (item.next.reachable == 0) { + item.next.reachable = 1; + } + } + + ActionItem target = item.jumpTarget; + if (target != null) { + if (target.reachable == 0) { + target.reachable = 1; + } + } + + if (action instanceof GraphSourceItemContainer) { + for (ActionItem lastActionItem : item.containerLastActions) { + if (lastActionItem != null && lastActionItem.next != null && lastActionItem.next.reachable == 0) { + lastActionItem.next.reachable = 1; + } + } + } + } + } + } + } + + public ActionList updateActions() { + List resultList = new ArrayList<>(size); + ActionItem item = firstAction; + if (item == null) { + return new ActionList(resultList); + } + + do { + resultList.add(item.action); + item = item.next; + } while (item != firstAction); + + ActionList result = new ActionList(resultList); + updateActionAddressesAndLengths(); + updateJumps(); + updateActionStores(result); + updateContainerSizes(); + return result; + } + + public ActionList toActionList() { + return updateActions(); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public boolean contains(Object o) { + if (o instanceof ActionItem) { + return actionItemSet.contains(o); + } else if (o instanceof Action) { + return actionItemMap.containsKey((Action) o); + } + + return false; + } + + @Override + public FastActionListIterator iterator() { + return new FastActionListIterator(firstAction, this); + } + + @Override + public Object[] toArray() { + Object[] result = new Object[size]; + + ActionItem item = firstAction; + if (item == null) { + return result; + } + + int i = 0; + do { + result[i] = item.action; + item = item.next; + i++; + } while (item != firstAction); + return null; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length != size) { + a = (T[]) new ActionItem[size]; + } + + ActionItem item = firstAction; + if (item == null) { + return a; + } + + int i = 0; + do { + a[i] = (T) item; + item = item.next; + i++; + } while (item != firstAction); + return null; + } + + @Override + public boolean add(ActionItem e) { + insertItemAfter(null, e); + return true; + } + + @Override + public boolean remove(Object o) { + ActionItem item = null; + if (o instanceof ActionItem) { + item = (ActionItem) o; + } else if (o instanceof Action) { + item = actionItemMap.get(o); + } + + if (item == null) { + return false; + } + + removeItem(item); + return true; + } + + @Override + public boolean containsAll(Collection c) { + for (Object c1 : c) { + if (!contains(c1)) { + return false; + } + } + + return true; + } + + @Override + public boolean addAll(Collection c) { + for (ActionItem c1 : c) { + insertItemAfter(null, c1); + } + + return true; + } + + @Override + public boolean removeAll(Collection c) { + boolean result = false; + for (Object c1 : c) { + result |= remove(c1); + } + + return result; + } + + @Override + public boolean retainAll(Collection c) { + ActionItem item = firstAction; + if (item == null) { + return false; + } + + boolean modified = false; + do { + if (!c.contains(item)) { + item = removeItem(item); + modified = true; + continue; + } + + item = item.next; + } while (item != firstAction); + return modified; + } + + @Override + public void clear() { + firstAction = null; + size = 0; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionListIterator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionListIterator.java new file mode 100644 index 000000000..3d538d097 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionListIterator.java @@ -0,0 +1,80 @@ +/* + * 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.fastactionlist; + +import com.jpexs.decompiler.flash.action.Action; +import java.util.Iterator; + +/** + * + * @author JPEXS + */ +public class FastActionListIterator implements Iterator { + + private ActionItem item; + + private final FastActionList list; + + private int index; + + FastActionListIterator(ActionItem firstAction, FastActionList list) { + item = firstAction; + this.list = list; + } + + @Override + public boolean hasNext() { + return index < list.size(); + } + + @Override + public ActionItem next() { + ActionItem result = item; + index++; + item = item.next; + return result; + } + + @Override + public void remove() { + item = list.removeItem(item.prev); + index--; + } + + public void add(Action action) { + item = list.insertItemAfter(item.prev, action).next; + index++; + } + + public void add(ActionItem actionItem) { + item = list.insertItemAfter(item.prev, actionItem).next; + index++; + } + + public ActionItem peek(int index) { + ActionItem item = this.item; + for (int i = 0; i < index; i++) { + if (item == null) { + break; + } + + item = item.next; + } + + return item; + } +}