better actionlist deobfuscation

This commit is contained in:
honfika
2014-08-22 20:16:15 +02:00
parent d6a8ea60f0
commit 97447968b1
31 changed files with 2611 additions and 1937 deletions

View File

@@ -24,7 +24,7 @@ import com.jpexs.decompiler.flash.abc.ClassPath;
import com.jpexs.decompiler.flash.abc.RenameType;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionDeobfuscation;
import com.jpexs.decompiler.flash.action.Deobfuscation;
import com.jpexs.decompiler.flash.action.ActionGraphSource;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionLocalData;
@@ -1672,7 +1672,7 @@ public final class SWF implements TreeItem, Timelined {
private List<GraphSourceItem> allFunctions = new ArrayList<>();
private HashMap<DirectValueActionItem, ConstantPool> allStrings = new HashMap<>();
private final HashMap<DirectValueActionItem, String> usageTypes = new HashMap<>();
private final ActionDeobfuscation deobfuscation = new ActionDeobfuscation();
private final Deobfuscation deobfuscation = new Deobfuscation();
private static void getVariables(ConstantPool constantPool, BaseLocalData localData, Stack<GraphTargetItem> stack, List<GraphTargetItem> output, ActionGraphSource code, int ip, List<MyEntry<DirectValueActionItem, ConstantPool>> variables, List<GraphSourceItem> functions, HashMap<DirectValueActionItem, ConstantPool> strings, List<Integer> visited, HashMap<DirectValueActionItem, String> usageTypes, String path) throws InterruptedException {
boolean debugMode = false;
@@ -1683,7 +1683,7 @@ public final class SWF implements TreeItem, Timelined {
GraphSourceItem ins = code.get(ip);
if (debugMode) {
System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ArrayList<GraphSourceItem>(), new ArrayList<Long>(), new ArrayList<String>(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool())));
System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ActionList(), new ArrayList<Long>(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool())));
}
if (ins.isExit()) {
break;

View File

@@ -398,25 +398,9 @@ public class Action implements GraphSourceItem {
* @param version SWF version
* @param exportMode PCode or hex?
* @param writer
* @return HilightedTextWriter
* @return GraphTextWriter
*/
public static GraphTextWriter actionsToString(List<DisassemblyListener> listeners, long address, List<Action> list, int version, ScriptExportMode exportMode, GraphTextWriter writer) {
return actionsToString(listeners, address, list, new ArrayList<String>(), version, exportMode, writer);
}
/**
* Converts list of actions to ASM source
*
* @param listeners
* @param address
* @param list List of actions
* @param importantOffsets List of important offsets to mark as labels
* @param constantPool Constant pool
* @param version SWF version
* @param hex Add hexadecimal?
* @return HilightedTextWriter
*/
private static GraphTextWriter actionsToString(List<DisassemblyListener> listeners, long address, List<Action> list, List<String> constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer) {
public static GraphTextWriter actionsToString(List<DisassemblyListener> listeners, long address, ActionList list, int version, ScriptExportMode exportMode, GraphTextWriter writer) {
long offset;
List<Long> importantOffsets = getActionsAllRefs(list);
/*List<ConstantPool> cps = SWFInputStream.getConstantPool(new ArrayList<DisassemblyListener>(), new ActionGraphSource(list, version, new HashMap<Integer, String>(), new HashMap<String, GraphTargetItem>(), new HashMap<String, GraphTargetItem>()), 0, version, path);
@@ -467,8 +451,11 @@ public class Action implements GraphSourceItem {
if (containers.containsKey(offset)) {
for (int i = 0; i < containers.get(offset).size(); i++) {
if (lastPush) {
writer.newLine();
lastPush = false;
}
writer.appendNoHilight("}").newLine();
lastPush = false;
GraphSourceItemContainer cnt = containers.get(offset).get(i);
int cntPos = containersPos.get(cnt);
writer.appendNoHilight(cnt.getASMSourceBetween(cntPos));
@@ -517,7 +504,7 @@ public class Action implements GraphSourceItem {
add = "";*/
if ((a instanceof ActionPush) && lastPush) {
writer.appendNoHilight(" ");
((ActionPush) a).paramsToStringReplaced(list, importantOffsets, constantPool, version, exportMode, writer);
((ActionPush) a).paramsToStringReplaced(list, importantOffsets, exportMode, writer);
} else {
if (lastPush) {
writer.newLine();
@@ -547,7 +534,7 @@ public class Action implements GraphSourceItem {
//nojump, ignore
}
} else {
a.getASMSourceReplaced(list, importantOffsets, constantPool, exportMode, writer);
a.getASMSourceReplaced(list, importantOffsets, exportMode, writer);
}
writer.appendNoHilight(a.isIgnored() ? "; ignored" : "");
writer.appendNoHilight(add);
@@ -592,11 +579,10 @@ public class Action implements GraphSourceItem {
*
* @param container
* @param knownAddreses List of important offsets to mark as labels
* @param constantPool Constant pool
* @param exportMode PCode or hex?
* @return String of P-code source
*/
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
return toString();
}
@@ -1209,8 +1195,8 @@ public class Action implements GraphSourceItem {
}
}
public GraphTextWriter getASMSourceReplaced(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode, GraphTextWriter writer) {
writer.appendNoHilight(getASMSource(container, knownAddreses, constantPool, exportMode));
public GraphTextWriter getASMSourceReplaced(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) {
writer.appendNoHilight(getASMSource(container, knownAddreses, exportMode));
return writer;
}

View File

@@ -0,0 +1,357 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.action.special.ActionStore;
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.ActionConstantPool;
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.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerListener;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateException;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
*
* @author JPEXS
*/
public class ActionDeobfuscator implements SWFDecompilerListener {
@Override
public void actionListParsed(ActionList actions) {
combinePushs(actions);
removeFakeFunction(actions);
removeUnreachableActions(actions);
removeObfuscationIfs(actions);
removeUnreachableActions(actions);
removeZeroJumps(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) {
if (actions.getReferencesFor(action2).hasNext()) {
ActionPush push = (ActionPush) action;
ActionPush push2 = (ActionPush) action2;
push.values.addAll(push2.values);
actions.remove(i + 1);
i--;
}
}
}
}
private boolean removeUnreachableActions(ActionList actions) {
Set<Action> reachableActions = new HashSet<>();
Set<Action> processedActions = new HashSet<>();
reachableActions.add(actions.get(0));
boolean modified = true;
while (modified) {
modified = false;
for (int i = 0; i < actions.size(); i++) {
Action action = actions.get(i);
if (reachableActions.contains(action) && !processedActions.contains(action)) {
if (!action.isExit() && !(action instanceof ActionJump) && i != actions.size() - 1) {
Action next = actions.get(i + 1);
if (!reachableActions.contains(next)) {
reachableActions.add(next);
}
}
if (action instanceof ActionJump) {
ActionJump aJump = (ActionJump) action;
long ref = aJump.getAddress() + aJump.getTotalActionLength() + aJump.getJumpOffset();
Action target = actions.getByAddress(ref);
if (target != null && !reachableActions.contains(target)) {
reachableActions.add(target);
}
} else if (action instanceof ActionIf) {
ActionIf aIf = (ActionIf) action;
long ref = aIf.getAddress() + aIf.getTotalActionLength() + aIf.getJumpOffset();
Action target = actions.getByAddress(ref);
if (target != null && !reachableActions.contains(target)) {
reachableActions.add(target);
}
} else if (action instanceof ActionStore) {
ActionStore aStore = (ActionStore) action;
int storeSize = aStore.getStoreSize();
if (actions.size() > i + storeSize) {
Action target = actions.get(i + storeSize);
if (!reachableActions.contains(target)) {
reachableActions.add(target);
}
}
} else if (action instanceof GraphSourceItemContainer) {
GraphSourceItemContainer container = (GraphSourceItemContainer) action;
long ref = action.getAddress() + action.getTotalActionLength();
for (Long size : container.getContainerSizes()) {
ref += size;
Action target = actions.getByAddress(ref);
if (target != null && !reachableActions.contains(target)) {
reachableActions.add(target);
}
}
}
processedActions.add(action);
modified = true;
}
}
}
boolean result = false;
for (int i = 0; i < actions.size(); i++) {
if (!reachableActions.contains(actions.get(i))) {
actions.removeAction(i);
i--;
result = true;
}
}
return result;
}
private boolean removeZeroJumps(ActionList actions) {
boolean result = false;
for (int i = 0; i < actions.size(); i++) {
Action action = actions.get(i);
if (action instanceof ActionJump && ((ActionJump) action).getJumpOffset() == 0) {
actions.removeAction(i);
i--;
result = true;
}
}
return result;
}
private boolean removeObfuscationIfs(ActionList actions) {
if (actions.size() == 0) {
return false;
}
for (int i = 0; i < actions.size(); i++) {
int idx = i;
List<GraphTargetItem> output = new ArrayList<>();
ActionLocalData localData = new ActionLocalData();
Stack<GraphTargetItem> stack = new Stack<>();
try {
int lastOkIdx = -1;
int lastOkInstructionsProcessed = -1;
int instructionsProcessed = 0;
Map<String, Object> variables = new HashMap<>();
Set<String> defines = new HashSet<>();
ActionConstantPool constantPool = null;
while (true) {
Action action = actions.get(idx);
instructionsProcessed++;
System.out.print(action.getASMSource(actions, new ArrayList<Long>(), ScriptExportMode.PCODE));
for (int j = 0; j < stack.size(); j++) {
System.out.print(" '" + stack.get(j).getResult() + "'");
}
System.out.println();
if (action instanceof ActionConstantPool) {
constantPool = (ActionConstantPool) action;
}
if (action instanceof ActionDefineLocal) {
GraphTargetItem top = stack.pop();
String variableName = stack.peek().getResult().toString();
defines.add(variableName);
stack.push(top);
}
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 ||
action instanceof ActionConstantPool)) {
break;
}
/*for (String variable : localData.variables.keySet()) {
System.out.println(Helper.byteArrToString(variable.getBytes()));
}*/
idx++;
if (action instanceof ActionJump) {
ActionJump jump = (ActionJump) action;
long address = jump.getAddress() + jump.getTotalActionLength() + jump.getJumpOffset();
idx = actions.indexOf(actions.getByAddress(address));
if (idx == -1) {
int a = 1;
}
}
if (action instanceof ActionIf) {
ActionIf aif = (ActionIf) action;
if (EcmaScript.toBoolean(stack.pop().getResult())) {
System.out.println("if true");
long address = aif.getAddress() + aif.getTotalActionLength() + aif.getJumpOffset();
idx = actions.indexOf(actions.getByAddress(address));
if (idx == -1) {
int a = 1;
}
}
}
if (/*localData.variables.size() == 1 && */stack.empty()) {
lastOkIdx = idx;
lastOkInstructionsProcessed = instructionsProcessed;
variables.clear();
for (String variableName : localData.variables.keySet()) {
Object value = localData.variables.get(variableName).getResult();
variables.put(variableName, value);
}
}
}
if (lastOkIdx != -1) {
int newIstructionCount = constantPool != null ? 2 : 1;
newIstructionCount += 2 * variables.size();
if (newIstructionCount < lastOkInstructionsProcessed) {
Action target = actions.get(lastOkIdx);
Action prevAction = actions.get(i);
if (constantPool != null) {
actions.addAction(i++, constantPool);
prevAction = constantPool;
}
for (String variableName : variables.keySet()) {
Object value = variables.get(variableName);
ActionPush push = new ActionPush(variableName);
push.values.add(value);
push.setAddress(prevAction.getAddress());
actions.addAction(i++, push);
prevAction = push;
if (defines.contains(variableName)) {
Action defineLocal = new ActionDefineLocal();
defineLocal.setAddress(prevAction.getAddress());
actions.addAction(i++, defineLocal);
prevAction = defineLocal;
} else {
Action setVariable = new ActionSetVariable();
setVariable.setAddress(prevAction.getAddress());
actions.addAction(i++, setVariable);
prevAction = setVariable;
}
}
ActionJump jump = new ActionJump(0);
jump.setAddress(prevAction.getAddress());
jump.setJumpOffset((int) (target.getAddress() - jump.getAddress() - jump.getTotalActionLength()));
actions.addAction(i++, jump);
return true;
}
}
} 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;
}
}

View File

@@ -16,15 +16,38 @@
*/
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.special.ActionNop;
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.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.EmptySWFDecompilerListener;
import com.jpexs.decompiler.flash.helpers.FileTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class ActionList extends ArrayList<Action> {
public ActionList() {
}
public ActionList(Collection<Action> actions) {
super(actions);
}
public void removeAction(int index) {
ActionListReader.removeAction(this, index, SWF.DEFAULT_VERSION, true);
@@ -42,7 +65,70 @@ public class ActionList extends ArrayList<Action> {
}
public void addAction(int index, Action action) {
ActionListReader.removeAction(this, index, SWF.DEFAULT_VERSION, true);
ActionListReader.addAction(this, index, action, SWF.DEFAULT_VERSION, false, false);
}
public void fixActionList() {
ActionListReader.fixActionList(this, null, SWF.DEFAULT_VERSION);
}
public Iterator<Action> getReferencesFor(final Action target) {
final ActionList diz = this;
return new Iterator<Action>() {
private final Iterator<Action> iterator = diz.iterator();
private Action action = getNext();
@Override
public boolean hasNext() {
return action != null;
}
@Override
public Action next() {
Action a = action;
action = getNext();
return a;
}
private Action getNext() {
while (iterator.hasNext()) {
Action a = iterator.next();
if (a instanceof ActionJump) {
ActionJump aJump = (ActionJump) a;
long ref = aJump.getAddress() + aJump.getTotalActionLength() + aJump.getJumpOffset();
if (target.getAddress() == ref) {
return aJump;
}
} else if (a instanceof ActionIf) {
ActionIf aIf = (ActionIf) a;
long ref = aIf.getAddress() + aIf.getTotalActionLength() + aIf.getJumpOffset();
if (target.getAddress() == ref) {
return aIf;
}
} else if (a instanceof ActionStore) {
ActionStore aStore = (ActionStore) a;
int storeSize = aStore.getStoreSize();
int idx = indexOf(a);
int idx2 = indexOf(target);
if (idx != -1 && idx2 == idx + storeSize) {
return a;
}
} else if (a instanceof GraphSourceItemContainer) {
GraphSourceItemContainer container = (GraphSourceItemContainer) a;
long ref = a.getAddress() + a.getTotalActionLength();
for (Long size : container.getContainerSizes()) {
ref += size;
if (target.getAddress() == ref) {
return a;
}
}
}
}
return null;
}
};
}
public void removeNops() {
@@ -54,11 +140,36 @@ public class ActionList extends ArrayList<Action> {
}
public Action getByAddress(long address) {
for (Action action : this) {
if (action.getAddress() == address) {
return action;
int idx = getIndexByAddress(address);
return idx == -1 ? null : get(idx);
}
public int getIndexByAddress(long address) {
int min = 0;
int max = size() - 1;
while (max >= min) {
int mid = (min + max) / 2;
long midValue = get(mid).getAddress();
if (midValue == address) {
return mid;
} else if (midValue < address) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return null;
return -1;
}
public void saveToFile(String fileName) {
File file = new File(fileName);
try (FileTextWriter writer = new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(file))) {
Action.actionsToString(new ArrayList<DisassemblyListener>(), 0, this, SWF.DEFAULT_VERSION, ScriptExportMode.PCODE, writer);
} catch (IOException ex) {
Logger.getLogger(EmptySWFDecompilerListener.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

View File

@@ -41,7 +41,6 @@ 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;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.NotCompileTimeItem;
@@ -132,90 +131,56 @@ public class ActionListReader {
ConstantPool cpool = new ConstantPool();
// Map of the actions. Use TreeMap to sort the keys in ascending order
// actionMap and nextOffsets should contain exaclty the same keys
Map<Long, Action> actionMap = new TreeMap<>();
Map<Long, Long> nextOffsets = new HashMap<>();
Action entryAction = readActionListAtPos(listeners, cpool,
sis, actionMap, nextOffsets,
ip, 0, endIp, path, false, new ArrayList<Long>());
ActionList actions = new ActionList();
if (actionMap.isEmpty()) {
return actions;
return new ActionList();
}
Map<Action, List<Action>> containerLastActions = new HashMap<>();
List<Long> addresses = new ArrayList<>(actionMap.keySet());
getContainerLastActions(actionMap, addresses, containerLastActions);
// jump to the entry action when it is diffrent from the first action in the map
long index = addresses.get(0);
if (index != -1 && entryAction != actionMap.get(index)) {
ActionJump jump = new ActionDeobfuscateJump(0);
int size = jump.getTotalActionLength();
jump.setJumpOffset((int) (entryAction.getAddress() - size));
actions.add(jump);
}
// remove nulls
index = getNearAddress(addresses, index, true);
while (index > -1) {
Action action = actionMap.get(index);
long nextOffset = nextOffsets.get(index);
long nextIndex = getNearAddress(addresses, index + 1, true);
actions.add(action);
if (nextIndex != -1 && nextOffset != nextIndex) {
if (!action.isExit() && !(action instanceof ActionJump)) {
ActionJump jump = new ActionDeobfuscateJump(0);
jump.setAddress(action.getAddress());
int size = jump.getTotalActionLength();
jump.setJumpOffset((int) (nextOffset - action.getAddress() - size));
actions.add(jump);
}
}
index = nextIndex;
}
// Map for storing the targers of the "jump" actions
// "jump" action can be ActionIf, ActionJump and any ActionStore
Map<Action, Action> jumps = new HashMap<>();
getJumps(actions, jumps);
updateActionLengths(actions, version);
long endAddress = updateAddresses(actions, 0);
// add end action
Action lastAction = actions.get(actions.size() - 1);
Action aEnd = new ActionEnd();
Action lastAction = actionMap.get(addresses.get(addresses.size() - 1));
long endAddress;
if (!(lastAction instanceof ActionEnd)) {
aEnd.setAddress(endAddress);
actions.add(aEnd);
} else {
endAddress -= aEnd.getTotalActionLength();
Action aEnd = new ActionEnd();
aEnd.setAddress(nextOffsets.get(lastAction.getAddress()));
endAddress = aEnd.getAddress();
actionMap.put(aEnd.getAddress(), aEnd);
nextOffsets.put(endAddress, endAddress + 1);
}
ActionList actions = fixActionList(new ActionList(actionMap.values()), nextOffsets, version);
updateJumps(actions, jumps, containerLastActions, endAddress);
updateActionStores(actions, jumps);
updateContainerSizes(actions, containerLastActions);
// jump to the entry action when it is diffrent from the first action in the map
if (entryAction != actions.get(0)) {
ActionJump jump = new ActionDeobfuscateJump(0);
actions.addAction(0, jump);
jump.setJumpOffset((int) (entryAction.getAddress() - jump.getTotalActionLength()));
}
if (SWFDecompilerPlugin.listener != null) {
try {
SWFDecompilerPlugin.listener.actionListParsed(actions);
updateActionLengths(actions, version);
updateAddresses(actions, 0);
updateJumps(actions, jumps, containerLastActions, endAddress);
updateActionStores(actions, jumps);
updateContainerSizes(actions, containerLastActions);
actions = fixActionList(actions, null, version);
} catch (Throwable e) {
Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, 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);
new ActionDeobfuscator().actionListParsed(actions);
/*actions = deobfuscateActionList(listeners, actions, version, 0, path);
updateActionLengths(actions, version);
removeZeroJumps(actions, version);
removeZeroJumps(actions, version);*/
} catch (OutOfMemoryError | StackOverflowError | TranslateException ex) {
// keep orignal (not deobfuscated) actions
Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, ex);
@@ -224,6 +189,51 @@ public class ActionListReader {
return actions;
}
public static ActionList fixActionList(ActionList actions, Map<Long, Long> nextOffsets, int version) {
Map<Action, List<Action>> containerLastActions = new HashMap<>();
getContainerLastActions(actions, containerLastActions);
ActionList ret = new ActionList();
if (nextOffsets != null) {
int index = 0;
while (index != -1 && index < actions.size()) {
Action action = actions.get(index);
ret.add(action);
index++;
if (index < actions.size()) {
long nextAddress = nextOffsets.get(action.getAddress());
if (actions.get(index).getAddress() != nextAddress) {
if (!action.isExit() && !(action instanceof ActionJump)) {
ActionJump jump = new ActionDeobfuscateJump(0);
jump.setAddress(action.getAddress());
int size = jump.getTotalActionLength();
jump.setJumpOffset((int) (nextAddress - action.getAddress() - size));
ret.add(jump);
}
}
}
}
} else {
ret.addAll(actions);
}
// Map for storing the targers of the "jump" actions
// "jump" action can be ActionIf, ActionJump and any ActionStore
Map<Action, Action> jumps = new HashMap<>();
getJumps(ret, jumps);
updateActionLengths(ret, version);
updateAddresses(ret, 0);
long endAddress = ret.get(ret.size() - 1).getAddress();
updateJumps(ret, jumps, containerLastActions, endAddress);
updateActionStores(ret, jumps);
updateContainerSizes(ret, containerLastActions);
return ret;
}
public static List<Action> getOriginalActions(SWFInputStream sis, int startIp, int endIp) throws IOException, InterruptedException {
ConstantPool cpool = new ConstantPool();
@@ -313,13 +323,13 @@ public class ActionListReader {
return reta;
}
private static long getNearAddress(List<Long> addresses, long address, boolean next) {
private static long getNearAddress(ActionList actions, long address, boolean next) {
int min = 0;
int max = addresses.size() - 1;
int max = actions.size() - 1;
while (max >= min) {
int mid = (min + max) / 2;
long midValue = addresses.get(mid);
long midValue = actions.get(mid).getAddress();
if (midValue == address) {
return address;
} else if (midValue < address) {
@@ -330,8 +340,8 @@ public class ActionListReader {
}
return next
? (min < addresses.size() ? addresses.get(min) : -1)
: (max > 0 ? addresses.get(max) : -1);
? (min < actions.size() ? actions.get(min).getAddress() : -1)
: (max > 0 ? actions.get(max).getAddress() : -1);
}
private static Map<Long, Action> actionListToMap(List<Action> actions) {
@@ -378,10 +388,8 @@ public class ActionListReader {
}
}
private static void getContainerLastActions(Map<Long, Action> actionMap, List<Long> addresses, Map<Action, List<Action>> lastActions) {
for (Long address : actionMap.keySet()) {
Action a = actionMap.get(address);
private static void getContainerLastActions(ActionList actions, Map<Action, List<Action>> lastActions) {
for (Action a : actions) {
if (a instanceof GraphSourceItemContainer) {
GraphSourceItemContainer container = (GraphSourceItemContainer) a;
List<Long> sizes = container.getContainerSizes();
@@ -389,10 +397,10 @@ public class ActionListReader {
List<Action> lasts = new ArrayList<>(sizes.size());
for (long size : sizes) {
endAddress += size;
long lastActionIndex = getNearAddress(addresses, endAddress - 1, false);
long lastActionAddress = getNearAddress(actions, endAddress - 1, false);
Action lastAction = null;
if (lastActionIndex != -1) {
lastAction = actionMap.get(lastActionIndex);
if (lastActionAddress != -1) {
lastAction = actions.getByAddress(lastActionAddress);
}
lasts.add(lastAction);
}
@@ -518,20 +526,6 @@ public class ActionListReader {
}
}
private static void removeZeroJumps(List<Action> actions, int version) {
for (int i = 0; i < actions.size(); i++) {
Action a = actions.get(i);
if (a instanceof ActionJump) {
ActionJump aJump = (ActionJump) a;
if (aJump.getJumpOffset() == 0) {
if (removeAction(actions, i, version, false)) {
i--;
}
}
}
}
}
/**
* Removes an action from the action list, and updates all references
* This method will keep the inner actions of the container when you remove the container
@@ -542,7 +536,7 @@ public class ActionListReader {
* @param removeWhenLast
* @return
*/
public static boolean removeAction(List<Action> actions, int index, int version, boolean removeWhenLast) {
public static boolean removeAction(ActionList actions, int index, int version, boolean removeWhenLast) {
if (index < 0 || actions.size() <= index) {
return false;
@@ -552,14 +546,8 @@ public class ActionListReader {
Action lastAction = actions.get(actions.size() - 1);
long endAddress = lastAction.getAddress() + lastAction.getTotalActionLength();
Map<Long, Action> actionMap = new TreeMap<>();
for (Action a : actions) {
actionMap.put(a.getAddress(), a);
}
List<Long> addresses = new ArrayList<>(actionMap.keySet());
Map<Action, List<Action>> containerLastActions = new HashMap<>();
getContainerLastActions(actionMap, addresses, containerLastActions);
getContainerLastActions(actions, containerLastActions);
Map<Action, Action> jumps = new HashMap<>();
getJumps(actions, jumps);
@@ -602,6 +590,75 @@ public class ActionListReader {
return true;
}
/**
* Adds an action to the action list to the specified location, and updates all references
*
* @param actions
* @param index
* @param action
* @param version
* @param addToContainer
* @param replaceJump
* @return
*/
public static boolean addAction(ActionList actions, int index, Action action,
int version, boolean addToContainer, boolean replaceJump) {
if (index < 0 || actions.size() < index) {
return false;
}
long startIp = actions.get(0).getAddress();
Action lastAction = actions.get(actions.size() - 1);
if (!(lastAction instanceof ActionEnd)) {
Action aEnd = new ActionEnd();
aEnd.setAddress(lastAction.getAddress() + lastAction.getTotalActionLength());
actions.add(aEnd);
lastAction = aEnd;
}
long endAddress = lastAction.getAddress();
Map<Action, List<Action>> containerLastActions = new HashMap<>();
getContainerLastActions(actions, containerLastActions);
Map<Action, Action> jumps = new HashMap<>();
List<Action> tempActions = new ArrayList<>(actions);
tempActions.add(action);
getJumps(tempActions, jumps);
Action prevAction = actions.get(index);
if (addToContainer) {
for (Action a : containerLastActions.keySet()) {
List<Action> lastActions = containerLastActions.get(a);
for (int i = 0; i < lastActions.size(); i++) {
if (lastActions.get(i) == prevAction) {
lastActions.set(i, action);
}
}
}
}
if (replaceJump) {
for (Action a : jumps.keySet()) {
Action targetAction = jumps.get(a);
if (targetAction == prevAction) {
jumps.put(a, action);
}
}
}
actions.add(index, action);
updateActionLengths(actions, version);
updateAddresses(actions, startIp);
updateJumps(actions, jumps, containerLastActions, endAddress);
updateActionStores(actions, jumps);
updateContainerSizes(actions, containerLastActions);
return true;
}
private static Action readActionListAtPos(List<DisassemblyListener> listeners, ConstantPool cpool,
SWFInputStream sis, Map<Long, Action> actions, Map<Long, Long> nextOffsets,
long ip, long startIp, long endIp, String path, boolean indeterminate, List<Long> visitedContainers) throws IOException {
@@ -742,7 +799,7 @@ public class ActionListReader {
}
if (debugMode) {
String atos = a.getASMSource(new ArrayList<GraphSourceItem>(), new ArrayList<Long>(), cpool.constants, ScriptExportMode.PCODE);
String atos = a.getASMSource(new ActionList(), new ArrayList<Long>(), ScriptExportMode.PCODE);
if (a instanceof GraphSourceItemContainer) {
atos = a.toString();
}
@@ -928,8 +985,10 @@ public class ActionListReader {
}
@SuppressWarnings("unchecked")
Stack<GraphTargetItem> substack = (Stack<GraphTargetItem>) stack.clone();
deobfustaceActionListAtPosRecursive(listeners, output, containers, prepareLocalBranch(localData), substack, cpool, actions, ip + actionLen + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel);
Stack<GraphTargetItem> subStack = (Stack<GraphTargetItem>) stack.clone();
ActionLocalData subLocalData = new ActionLocalData(new HashMap<>(localData.regNames),
new HashMap<>(localData.variables), new HashMap<>(localData.functions));
deobfustaceActionListAtPosRecursive(listeners, output, containers, subLocalData, subStack, cpool, actions, ip + actionLen + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel);
}
if (newip > -1) {
@@ -946,10 +1005,4 @@ public class ActionListReader {
listener.progress(AppStrings.translate("disassemblingProgress.deobfuscating"), ip, actions.size());
}
}
private static ActionLocalData prepareLocalBranch(ActionLocalData localData) {
return new ActionLocalData(new HashMap<>(localData.regNames),
new HashMap<>(localData.variables), new HashMap<>(localData.functions));
}
}

View File

@@ -32,7 +32,7 @@ import java.util.regex.Pattern;
*
* @author JPEXS
*/
public class ActionDeobfuscation {
public class Deobfuscation {
private final Random rnd = new Random();
private final int DEFAULT_FOO_SIZE = 10;

View File

@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.action.flashlite.ActionStrictMode;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.special.ActionDeobfuscateJump;
import com.jpexs.decompiler.flash.action.special.ActionDeobfuscatePop;
import com.jpexs.decompiler.flash.action.special.ActionEnd;
import com.jpexs.decompiler.flash.action.special.ActionNop;
import com.jpexs.decompiler.flash.action.swf3.ActionGetURL;
import com.jpexs.decompiler.flash.action.swf3.ActionGoToLabel;
@@ -408,6 +409,8 @@ public class ASMParser {
a = (new ActionStrictMode(lexer));
} else if (instructionName.compareToIgnoreCase("Nop") == 0) {
a = (new ActionNop());
} else if (instructionName.compareToIgnoreCase("End") == 0) {
a = (new ActionEnd());
} else if (instructionName.compareToIgnoreCase("FFDec_DeobfuscatePop") == 0) {
a = (new ActionDeobfuscatePop());
} else if (instructionName.compareToIgnoreCase("FFDec_DeobfuscateJump") == 0) {

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionGraph;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem;
@@ -28,7 +29,6 @@ import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.action.special.ActionStore;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -56,7 +56,7 @@ public class ActionWaitForFrame extends Action implements ActionStore {
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
String ret = "WaitForFrame " + frame + " " + skipCount;
return ret;
}

View File

@@ -19,11 +19,11 @@ package com.jpexs.decompiler.flash.action.swf4;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.helpers.Helper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -74,8 +74,9 @@ public class ActionIf extends Action {
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
String ofsStr = Helper.formatAddress(getAddress() + getTotalActionLength() + offset);
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
long address = getAddress() + getTotalActionLength() + offset;
String ofsStr = Helper.formatAddress(address);
return "If loc" + ofsStr + (!jumpUsed ? " ;compileTimeIgnore" : (!ignoreUsed ? " ;compileTimeJump" : ""));
}

View File

@@ -20,11 +20,11 @@ import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionGraphSource;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.helpers.Helper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -75,8 +75,9 @@ public class ActionJump extends Action {
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
String ofsStr = Helper.formatAddress(getAddress() + getTotalActionLength() + offset);
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
long address = getAddress() + getTotalActionLength() + offset;
String ofsStr = Helper.formatAddress(address);
return "Jump loc" + ofsStr;
}

View File

@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.EndOfStreamException;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.TemporaryRegister;
import com.jpexs.decompiler.flash.action.parser.ParseException;
@@ -231,7 +232,7 @@ public class ActionPush extends Action {
}
@Override
public GraphTextWriter getASMSourceReplaced(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode, GraphTextWriter writer) {
public GraphTextWriter getASMSourceReplaced(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) {
if (replacement == null || replacement.size() < values.size()) {
return toString(writer);
}
@@ -242,7 +243,7 @@ public class ActionPush extends Action {
return writer;
}
public GraphTextWriter paramsToStringReplaced(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer) {
public GraphTextWriter paramsToStringReplaced(List<? extends GraphSourceItem> container, List<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) {
if (replacement == null || replacement.size() < values.size()) {
return paramsToString(writer);
}

View File

@@ -21,13 +21,13 @@ import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionGraph;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.action.special.ActionStore;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -116,13 +116,13 @@ public class ActionWaitForFrame2 extends Action implements ActionStore {
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
String ret = "WaitForFrame2 " + skipCount;
/*for (int i = 0; i < skipped.size(); i++) {
if (skipped.get(i) instanceof ActionEnd) {
break;
}
ret += "\r\n" + skipped.get(i).getASMSource(container, knownAddreses, constantPool, version, exportMode);
ret += "\r\n" + skipped.get(i).getASMSource(container, knownAddreses, version, exportMode);
}*/
return ret;
}

View File

@@ -19,13 +19,13 @@ package com.jpexs.decompiler.flash.action.swf5;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.helpers.Helper;
@@ -146,7 +146,7 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
StringBuilder paramStr = new StringBuilder();
for (int i = 0; i < paramNames.size(); i++) {
paramStr.append("\"").append(Helper.escapeString(paramNames.get(i))).append("\" ");
@@ -156,7 +156,7 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta
}
@Override
public GraphTextWriter getASMSourceReplaced(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode, GraphTextWriter writer) {
public GraphTextWriter getASMSourceReplaced(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) {
List<String> oldParamNames = paramNames;
if (replacedParamNames != null) {
paramNames = replacedParamNames;
@@ -165,7 +165,7 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta
if (replacedFunctionName != null) {
functionName = replacedFunctionName;
}
String ret = getASMSource(container, knownAddreses, constantPool, exportMode);
String ret = getASMSource(container, knownAddreses, exportMode);
paramNames = oldParamNames;
functionName = oldFunctionName;
writer.appendNoHilight(ret);

View File

@@ -19,11 +19,11 @@ package com.jpexs.decompiler.flash.action.swf5;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.clauses.WithActionItem;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import java.io.ByteArrayOutputStream;
@@ -75,7 +75,7 @@ public class ActionWith extends Action implements GraphSourceItemContainer {
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
return "With {";
}

View File

@@ -19,13 +19,13 @@ package com.jpexs.decompiler.flash.action.swf7;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.helpers.Helper;
@@ -226,7 +226,7 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont
}
@Override
public GraphTextWriter getASMSourceReplaced(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode, GraphTextWriter writer) {
public GraphTextWriter getASMSourceReplaced(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) {
List<String> oldParamNames = paramNames;
if (replacedParamNames != null) {
paramNames = replacedParamNames;
@@ -235,7 +235,7 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont
if (replacedFunctionName != null) {
functionName = replacedFunctionName;
}
String ret = getASMSource(container, knownAddreses, constantPool, exportMode);
String ret = getASMSource(container, knownAddreses, exportMode);
paramNames = oldParamNames;
functionName = oldFunctionName;
writer.appendNoHilight(ret);
@@ -244,7 +244,7 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
StringBuilder paramStr = new StringBuilder();
for (int i = 0; i < paramNames.size(); i++) {
paramStr.append(paramRegisters.get(i)).append(" \"").append(Helper.escapeString(paramNames.get(i))).append("\" ");

View File

@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.action.swf7;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.model.ActionItem;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem;
@@ -27,7 +28,6 @@ import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import com.jpexs.decompiler.flash.action.swf4.RegisterNumber;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.helpers.Helper;
@@ -148,7 +148,7 @@ public class ActionTry extends Action implements GraphSourceItemContainer {
}
@Override
public String getASMSource(List<? extends GraphSourceItem> container, List<Long> knownAddreses, List<String> constantPool, ScriptExportMode exportMode) {
public String getASMSource(ActionList container, List<Long> knownAddreses, ScriptExportMode exportMode) {
String ret = "";
ret += "Try ";
if (catchBlockFlag) {

View File

@@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.console.CommandLineArgumentParser;
import com.jpexs.decompiler.flash.console.ContextMenuTools;
import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame;
import com.jpexs.decompiler.flash.helpers.EmptySWFDecompilerListener;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.treeitems.SWFList;
@@ -880,7 +881,7 @@ public class Main {
public static void main(String[] args) throws IOException {
String pluginPath = Configuration.pluginPath.get();
if (pluginPath != null) {
if (pluginPath != null && !pluginPath.isEmpty()) {
try {
SWFDecompilerPlugin.loadPlugin(pluginPath);
} catch (Throwable e) {

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionGraph;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser;
import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser;
@@ -112,7 +113,7 @@ public class ActionPanel extends JPanel implements ActionListener, SearchListene
private boolean ignoreCarret = false;
private boolean editMode = false;
private boolean editDecompiledMode = false;
private List<Action> lastCode;
private ActionList lastCode;
private ASMSource src;
public JPanel topButtonsPan;
private HilightedText srcWithHex;
@@ -395,7 +396,7 @@ public class ActionPanel extends JPanel implements ActionListener, SearchListene
}
DisassemblyListener listener = getDisassemblyListener();
asm.addDisassemblyListener(listener);
List<Action> actions = asm.getActions();
ActionList actions = asm.getActions();
lastCode = actions;
asm.removeDisassemblyListener(listener);
srcWithHex = null;

View File

@@ -28,5 +28,5 @@ public class EmptySWFDecompilerListener implements SWFDecompilerListener {
public void actionListParsed(ActionList actions) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View File

@@ -71,7 +71,6 @@ public class SWFDecompilerPlugin {
// Creating an instance of our compiled class and
try {
listener = (SWFDecompilerListener) fileManager.getClassLoader(null).loadClass(fullName).newInstance();
listener = new EmptySWFDecompilerListener();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(SWFDecompilerPlugin.class.getName()).log(Level.SEVERE, null, ex);
}

View File

@@ -136,13 +136,14 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
/**
* Converts actions to ASM source
*
* @param actions
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}

View File

@@ -86,13 +86,14 @@ public class DoActionTag extends Tag implements ASMSource {
/**
* Converts actions to ASM source
*
* @param actions
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}

View File

@@ -100,13 +100,14 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource {
/**
* Converts actions to ASM source
*
* @param actions
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}

View File

@@ -41,7 +41,7 @@ public interface ASMSource extends TreeItem {
* @return ASM source
* @throws java.lang.InterruptedException
*/
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException;
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException;
/**
* Whether or not this object contains ASM source

View File

@@ -155,13 +155,14 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem, S
/**
* Converts actions to ASM source
*
* @param actions
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}

View File

@@ -171,13 +171,14 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem, S
/**
* Converts actions to ASM source
*
* @param actions
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}

View File

@@ -20,6 +20,8 @@ import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser;
import com.jpexs.decompiler.flash.action.swf4.ActionJump;
import com.jpexs.decompiler.flash.action.swf5.ActionGetMember;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.gui.Main;
@@ -80,8 +82,29 @@ public class ActionScript2ModificationTest extends ActionStript2TestBase {
doa.getASMSource(ScriptExportMode.PCODE, writer, doa.getActions());
String actualResult = normalizeLabels(writer.toString());
actualResult = actualResult.replaceAll("[ \r\n]", "");
expectedResult = expectedResult.replaceAll("[ \r\n]", "");
actualResult = cleanPCode(actualResult);
expectedResult = cleanPCode(expectedResult);
Assert.assertEquals(actualResult, expectedResult);
} catch (IOException | ParseException | InterruptedException ex) {
fail();
}
}
public void testAddAction(String actionsString, String expectedResult, Action action, int index) {
try {
ActionList actions = ASMParser.parse(0, true, actionsString, swf.version, false);
actions.addAction(index, action);
DoActionTag doa = getFirstActionTag();
doa.setActionBytes(Action.actionsToBytes(actions, true, swf.version));
HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false);
doa.getASMSource(ScriptExportMode.PCODE, writer, doa.getActions());
String actualResult = normalizeLabels(writer.toString());
actualResult = cleanPCode(actualResult);
expectedResult = cleanPCode(expectedResult);
Assert.assertEquals(actualResult, expectedResult);
} catch (IOException | ParseException | InterruptedException ex) {
@@ -204,4 +227,107 @@ public class ActionScript2ModificationTest extends ActionStript2TestBase {
"label_1:";
testRemoveAction(actionsString, expectedResult, new int[] {7});
}
@Test
public void testAddAtion1() {
String actionsString =
"ConstantPool\n" +
"DefineFunction \"test\" 1 \"p1\" {\n" +
"Push 1\n" +
"GetVariable\n" +
"}\n" +
"Push 2\n" +
"If label_1\n" +
"Push 3\n" +
"label_1:Push 4";
String expectedResult =
"ConstantPool\n" +
"GetMember\n" +
"DefineFunction \"test\" 1 \"p1\" {\n" +
"Push 1\n" +
"GetVariable\n" +
"}\n" +
"Push 2\n" +
"If label_1\n" +
"Push 3\n" +
"label_1:Push 4";
testAddAction(actionsString, expectedResult, new ActionGetMember(), 1);
}
@Test
public void testAddAtionToContainer() {
String actionsString =
"ConstantPool\n" +
"DefineFunction \"test\" 1 \"p1\" {\n" +
"Push 1\n" +
"GetVariable\n" +
"}\n" +
"Push 2\n" +
"If label_1\n" +
"Push 3\n" +
"label_1:Push 4";
String expectedResult =
"ConstantPool\n" +
"DefineFunction \"test\" 1 \"p1\" {\n" +
"GetMember\n" +
"Push 1\n" +
"GetVariable\n" +
"}\n" +
"Push 2\n" +
"If label_1\n" +
"Push 3\n" +
"label_1:Push 4";
testAddAction(actionsString, expectedResult, new ActionGetMember(), 2);
}
@Test
public void testAddActionIf() {
String actionsString =
"ConstantPool\n" +
"DefineFunction \"test\" 1 \"p1\" {\n" +
"Push 1\n" +
"GetVariable\n" +
"}\n" +
"Push 2\n" +
"If label_1\n" +
"Push 3\n" +
"label_1:Push 4";
String expectedResult =
"ConstantPool\n" +
"DefineFunction \"test\" 1 \"p1\" {\n" +
"Push 1\n" +
"GetVariable\n" +
"}\n" +
"Push 2\n" +
"If label_1\n" +
"Push 3\n" +
"GetMember\n" +
"label_1:Push 4";
testAddAction(actionsString, expectedResult, new ActionGetMember(), 7);
}
@Test
public void testAddToJumpTarget() {
String actionsString =
"ConstantPool\n" +
"If label_1\n" +
"GetMember\n" +
"label_1:Jump label_2\n" + // address 9
"label_2:Jump label_3\n" +
"label_3:Jump labelend\n" +
"labelend:End"; // address 24
String expectedResult =
"ConstantPool\n" +
"If label_1\n" +
"GetMember\n" +
"Jump label_4\n" +
"label_1:Jump label_2\n" +
"label_2:Jump label_3\n" +
"label_3:Jump label_4\n" +
"label_4:";
ActionJump jump = new ActionJump(0);
jump.setAddress(9);
jump.setJumpOffset(24 - 9 - 5);
testAddAction(actionsString, expectedResult, jump, 3);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash;
/**
*
* @author JPEXS
*/
public class ActionScriptTestBase {
protected String cleanPCode(String pCode) {
pCode = pCode.replaceAll(" *[\r\n]+ *", "\n").trim();
pCode = pCode.replaceAll(" +", " ").trim();
return pCode;
}
}

View File

@@ -1,39 +1,38 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import com.jpexs.decompiler.flash.tags.Tag;
/**
*
* @author JPEXS
*/
public class ActionStript2TestBase {
protected SWF swf;
protected DoActionTag getFirstActionTag() {
for (Tag t : swf.tags) {
if (t instanceof DoActionTag) {
return (DoActionTag) t;
}
}
return null;
}
}
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import com.jpexs.decompiler.flash.tags.Tag;
/**
*
* @author JPEXS
*/
public class ActionStript2TestBase extends ActionScriptTestBase {
protected SWF swf;
protected DoActionTag getFirstActionTag() {
for (Tag t : swf.tags) {
if (t instanceof DoActionTag) {
return (DoActionTag) t;
}
}
return null;
}
}