diff --git a/examples/AS2DeobfuscatorSample.java b/examples/AS2DeobfuscatorSample.java new file mode 100644 index 000000000..bd22bd4cb --- /dev/null +++ b/examples/AS2DeobfuscatorSample.java @@ -0,0 +1,166 @@ +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.swf4.ActionAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; +import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionReturn; +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 java.util.ArrayList; +import java.util.EmptyStackException; +import java.util.List; +import java.util.Stack; + +public class AS2DeobfuscatorSample implements SWFDecompilerListener { + + @Override + public void actionListParsed(ActionList actions) { + combinePushs(actions); + if (removeFakeFunction(actions)) { + while (removeObfuscationIfs(actions)); + } + } + + private void combinePushs(ActionList actions) { + for (int i = 0; i < actions.size() - 1; i++) { + Action action = actions.get(i); + Action action2 = actions.get(i + 1); + if (action instanceof ActionPush && action2 instanceof ActionPush) { + ActionPush push = (ActionPush) action; + ActionPush push2 = (ActionPush) action2; + push.values.addAll(push2.values); + actions.remove(i + 1); + i--; + } + } + } + + private boolean removeObfuscationIfs(ActionList actions) { + if (actions.size() == 0) { + return false; + } + + for (int i = 0; i < actions.size(); i++) { + int idx = i; + List output = new ArrayList(); + ActionLocalData localData = new ActionLocalData(); + Stack stack = new Stack<>(); + + try { + int lastOkIdx = -1; + while (true) { + Action action = actions.get(idx); + + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + + if (!(action instanceof ActionPush || + action instanceof ActionAdd || + action instanceof ActionAdd2 || + action instanceof ActionSubtract || + action instanceof ActionDefineLocal || + action instanceof ActionJump || + action instanceof ActionGetVariable || + action instanceof ActionSetVariable || + action instanceof ActionEquals || + action instanceof ActionNot || + action instanceof ActionIf)) { + break; + } + + idx++; + + if (action instanceof ActionJump) { + ActionJump jump = (ActionJump) action; + long address = jump.getAddress() + jump.getTotalActionLength() + jump.getJumpOffset(); + idx = actions.indexOf(actions.getByAddress(address)); + } + + if (action instanceof ActionIf) { + ActionIf aif = (ActionIf) action; + if (EcmaScript.toBoolean(stack.peek().getResult())) { + long address = aif.getAddress() + aif.getTotalActionLength() + aif.getJumpOffset(); + idx = actions.indexOf(actions.getByAddress(address)); + } + stack.pop(); + } + + if (localData.variables.size() == 1 && stack.empty()) { + lastOkIdx = idx; + + // + } + } + + if (lastOkIdx != -1) { + int a = 1; + } + } catch (EmptyStackException | TranslateException | InterruptedException ex) { + } + } + + return false; + } + + private boolean removeFakeFunction(ActionList actions) { + /* + DefineFunction "fakeName" 0 { + Push 1777 + Return + } + */ + + for (int i = 0; i < actions.size() - 2; i++) { + Action action = actions.get(i); + if (action instanceof ActionDefineFunction) { + Action action2 = actions.get(i + 1); + Action action3 = actions.get(i + 2); + if (action2 instanceof ActionPush && action3 instanceof ActionReturn) { + ActionDefineFunction def = (ActionDefineFunction) action; + ActionPush push = (ActionPush) action2; + if (def.paramNames.isEmpty() && push.values.size() == 1) { + Object pushValueObj = push.values.get(0); + if (pushValueObj instanceof Long) { + String functionName = def.functionName; + long pushValue = (Long) pushValueObj; + for (int j = 0; j < 3; j++) { + actions.removeAction(i); + } + + for (int j = 0; j < actions.size() - 1; j++) { + action = actions.get(j); + if (action instanceof ActionPush) { + push = (ActionPush) action; + int pushValuesCount = push.values.size(); + if (pushValuesCount >= 2 + && push.values.get(pushValuesCount - 1).equals(functionName)) { + push.values.remove(pushValuesCount - 1); + push.values.remove(pushValuesCount - 2); + push.values.add(pushValue); + actions.removeAction(j + 1); + } + } + } + + return true; + } + } + } + } + } + + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/ActionList.java b/src/com/jpexs/decompiler/flash/action/ActionList.java new file mode 100644 index 000000000..506a1d8c3 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/action/ActionList.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.action; + +import com.jpexs.decompiler.flash.SWF; +import java.util.ArrayList; + +/** + * + * @author JPEXS + */ +public class ActionList extends ArrayList { + + public void removeAction(int index) { + ActionListReader.removeAction(this, index, SWF.DEFAULT_VERSION, true); + } + + public Action getByAddress(long address) { + for (Action action : this) { + if (action.getAddress() == address) { + return action; + } + } + return null; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/src/com/jpexs/decompiler/flash/action/ActionListReader.java index 0e15e195d..45d178beb 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionListReader.java +++ b/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -38,6 +38,8 @@ import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.ecma.Null; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; @@ -90,13 +92,15 @@ public class ActionListReader { */ public static List readActionListTimeout(final List listeners, final SWFInputStream sis, final int version, final int ip, final int endIp, final String path) throws IOException, InterruptedException, TimeoutException { try { - return CancellableWorker.call(new Callable>() { + ActionList actions = CancellableWorker.call(new Callable() { @Override - public List call() throws IOException, InterruptedException { + public ActionList call() throws IOException, InterruptedException { return readActionList(listeners, sis, version, ip, endIp, path); } }, Configuration.decompilationTimeoutSingleMethod.get(), TimeUnit.SECONDS); + + return actions; } catch (ExecutionException ex) { Throwable cause = ex.getCause(); if (cause instanceof InterruptedException) { @@ -124,7 +128,7 @@ public class ActionListReader { * @throws IOException * @throws java.lang.InterruptedException */ - private static List readActionList(List listeners, SWFInputStream sis, int version, int ip, int endIp, String path) throws IOException, InterruptedException { + private static ActionList readActionList(List listeners, SWFInputStream sis, int version, int ip, int endIp, String path) throws IOException, InterruptedException { ConstantPool cpool = new ConstantPool(); // Map of the actions. Use TreeMap to sort the keys in ascending order @@ -134,7 +138,7 @@ public class ActionListReader { sis, actionMap, nextOffsets, ip, 0, endIp, path, false, new ArrayList()); - List actions = new ArrayList<>(); + ActionList actions = new ActionList(); if (actionMap.isEmpty()) { return actions; } @@ -188,11 +192,25 @@ public class ActionListReader { endAddress -= aEnd.getTotalActionLength(); } - updateJumps(actions, jumps, containerLastActions, endAddress, version); + updateJumps(actions, jumps, containerLastActions, endAddress); updateActionStores(actions, jumps); updateContainerSizes(actions, containerLastActions); updateActionLengths(actions, version); + if (SWFDecompilerPlugin.listener != null) { + try { + SWFDecompilerPlugin.listener.actionListParsed(actions); + + updateAddresses(actions, 0, version); + updateJumps(actions, jumps, containerLastActions, endAddress); + updateActionStores(actions, jumps); + updateContainerSizes(actions, containerLastActions); + updateActionLengths(actions, version); + } catch (Throwable e) { + View.showMessageDialog(null, "Failed to call plugin method actionListParsed. Exception: " + e.getMessage()); + } + } + if (Configuration.autoDeobfuscate.get()) { try { actions = deobfuscateActionList(listeners, actions, version, 0, path); @@ -233,7 +251,7 @@ public class ActionListReader { * @throws IOException * @throws java.lang.InterruptedException */ - private static List deobfuscateActionList(List listeners, List actions, int version, int ip, String path) throws IOException, InterruptedException { + private static ActionList deobfuscateActionList(List listeners, ActionList actions, int version, int ip, String path) throws IOException, InterruptedException { if (actions.isEmpty()) { return actions; } @@ -255,12 +273,6 @@ public class ActionListReader { actionMap.set((int) a.getAddress(), a); } - ConstantPool cpool = new ConstantPool(); - - Stack stack = new Stack<>(); - - ActionLocalData localData = new ActionLocalData(); - int maxRecursionLevel = 0; for (int i = 0; i < actions.size(); i++) { Action a = actions.get(i); @@ -274,7 +286,16 @@ public class ActionListReader { } } - deobfustaceActionListAtPosRecursive(listeners, new ArrayList(), new HashMap>(), localData, stack, cpool, actionMap, ip, retdups, ip, endIp, path, new HashMap(), false, new HashMap>(), version, 0, maxRecursionLevel); + deobfustaceActionListAtPosRecursive(listeners, + new ArrayList(), + new HashMap>(), + new ActionLocalData(), + new Stack(), + new ConstantPool(), + actionMap, ip, retdups, ip, endIp, path, + new HashMap(), false, + new HashMap>(), + version, 0, maxRecursionLevel); List ret = new ArrayList<>(); Action last = null; @@ -285,7 +306,7 @@ public class ActionListReader { last = a; } ret = Action.removeNops(0, ret, version, path); - List reta = new ArrayList<>(); + ActionList reta = new ActionList(); for (Object o : ret) { if (o instanceof Action) { reta.add((Action) o); @@ -462,7 +483,7 @@ public class ActionListReader { } } - private static void updateJumps(List actions, Map jumps, Map> containerLastActions, long endAddress, int version) { + private static void updateJumps(List actions, Map jumps, Map> containerLastActions, long endAddress) { if (actions.isEmpty()) { return; } @@ -524,7 +545,7 @@ public class ActionListReader { * @param removeWhenLast * @return */ - private static boolean removeAction(List actions, int index, int version, boolean removeWhenLast) { + public static boolean removeAction(List actions, int index, int version, boolean removeWhenLast) { if (index < 0 || actions.size() <= index) { return false; @@ -576,7 +597,7 @@ public class ActionListReader { actions.remove(index); updateAddresses(actions, startIp, version); - updateJumps(actions, jumps, containerLastActions, endAddress, version); + updateJumps(actions, jumps, containerLastActions, endAddress); updateActionStores(actions, jumps); updateContainerSizes(actions, containerLastActions); updateActionLengths(actions, version); diff --git a/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java b/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java index 666ea3722..d3cece600 100644 --- a/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java +++ b/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.action.parser.pcode; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.flashlite.ActionFSCommand2; import com.jpexs.decompiler.flash.action.flashlite.ActionStrictMode; import com.jpexs.decompiler.flash.action.parser.ParseException; @@ -134,8 +135,8 @@ import java.util.logging.Logger; public class ASMParser { - public static List parse(boolean ignoreNops, List