sample AS2 deobfuscator plugin

This commit is contained in:
honfika
2014-08-18 23:52:12 +02:00
parent 8fae73cec3
commit 7d4fbe5075
15 changed files with 724 additions and 22 deletions

View File

@@ -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<GraphTargetItem> output = new ArrayList<GraphTargetItem>();
ActionLocalData localData = new ActionLocalData();
Stack<GraphTargetItem> 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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.SWF;
import java.util.ArrayList;
/**
*
* @author JPEXS
*/
public class ActionList extends ArrayList<Action> {
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;
}
}

View File

@@ -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<Action> readActionListTimeout(final List<DisassemblyListener> 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<List<Action>>() {
ActionList actions = CancellableWorker.call(new Callable<ActionList>() {
@Override
public List<Action> 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<Action> readActionList(List<DisassemblyListener> listeners, SWFInputStream sis, int version, int ip, int endIp, String path) throws IOException, InterruptedException {
private static ActionList readActionList(List<DisassemblyListener> 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<Long>());
List<Action> 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<Action> deobfuscateActionList(List<DisassemblyListener> listeners, List<Action> actions, int version, int ip, String path) throws IOException, InterruptedException {
private static ActionList deobfuscateActionList(List<DisassemblyListener> 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<GraphTargetItem> 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<GraphTargetItem>(), new HashMap<Long, List<GraphSourceItemContainer>>(), localData, stack, cpool, actionMap, ip, retdups, ip, endIp, path, new HashMap<Integer, Integer>(), false, new HashMap<Integer, HashMap<String, GraphTargetItem>>(), version, 0, maxRecursionLevel);
deobfustaceActionListAtPosRecursive(listeners,
new ArrayList<GraphTargetItem>(),
new HashMap<Long, List<GraphSourceItemContainer>>(),
new ActionLocalData(),
new Stack<GraphTargetItem>(),
new ConstantPool(),
actionMap, ip, retdups, ip, endIp, path,
new HashMap<Integer, Integer>(), false,
new HashMap<Integer, HashMap<String, GraphTargetItem>>(),
version, 0, maxRecursionLevel);
List<Action> ret = new ArrayList<>();
Action last = null;
@@ -285,7 +306,7 @@ public class ActionListReader {
last = a;
}
ret = Action.removeNops(0, ret, version, path);
List<Action> 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<Action> actions, Map<Action, Action> jumps, Map<Action, List<Action>> containerLastActions, long endAddress, int version) {
private static void updateJumps(List<Action> actions, Map<Action, Action> jumps, Map<Action, List<Action>> containerLastActions, long endAddress) {
if (actions.isEmpty()) {
return;
}
@@ -524,7 +545,7 @@ public class ActionListReader {
* @param removeWhenLast
* @return
*/
private static boolean removeAction(List<Action> actions, int index, int version, boolean removeWhenLast) {
public static boolean removeAction(List<Action> 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);

View File

@@ -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<Action> parse(boolean ignoreNops, List<Label> labels, long address, FlasmLexer lexer, List<String> constantPool, int version) throws IOException, ParseException {
List<Action> list = new ArrayList<>();
public static ActionList parse(boolean ignoreNops, List<Label> labels, long address, FlasmLexer lexer, List<String> constantPool, int version) throws IOException, ParseException {
ActionList list = new ActionList();
Stack<GraphSourceItemContainer> containers = new Stack<>();
ActionConstantPool cpool = new ActionConstantPool(constantPool);
@@ -446,7 +447,7 @@ public class ASMParser {
}
}
public static List<Action> parse(long address, boolean ignoreNops, String source, int version, boolean throwOnError) throws IOException, ParseException {
public static ActionList parse(long address, boolean ignoreNops, String source, int version, boolean throwOnError) throws IOException, ParseException {
FlasmLexer lexer = new FlasmLexer(new StringReader(source));
List<Action> list = parseAllActions(lexer, version);
@@ -459,7 +460,7 @@ public class ASMParser {
lexer = new FlasmLexer(new StringReader(source));
List<Label> labels = new ArrayList<>();
List<Action> ret = parse(ignoreNops, labels, address, lexer, constantPool, version);
ActionList ret = parse(ignoreNops, labels, address, lexer, constantPool, version);
//Action.setActionsAddresses(ret, address, version);
for (Action link : ret) {
if (!(link instanceof ActionIf || link instanceof ActionJump)) {

View File

@@ -326,6 +326,8 @@ public class Configuration {
@ConfigurationDefaultInt(128)
public static final ConfigurationItem<Integer> lzmaFastBytes = null;
public static final ConfigurationItem<String> pluginPath = null;
private enum OSId {
WINDOWS, OSX, UNIX

View File

@@ -327,7 +327,7 @@ public class AdvancedSettingsDialog extends AppDialog implements ActionListener
// Reflection exceptions. This should never happen
throw new Error(ex.getMessage());
}
if (item.get() != null && !item.get().equals(value)) {
if (item.get() == null || !item.get().equals(value)) {
item.set(value);
modified = true;
}

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.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.treeitems.SWFList;
import com.jpexs.helpers.Cache;
@@ -877,6 +878,16 @@ public class Main {
* @throws IOException
*/
public static void main(String[] args) throws IOException {
String pluginPath = Configuration.pluginPath.get();
if (pluginPath != null) {
try {
SWFDecompilerPlugin.loadPlugin(pluginPath);
} catch (Throwable e) {
View.showMessageDialog(null, "Failed to load plugin: " + pluginPath);
}
}
AppStrings.setResourceClass(MainFrame.class);
initLogging(Configuration.debugMode.get());
initLang();

View File

@@ -259,3 +259,7 @@ config.description.textExportExportFontFace = Embed font files in SVG using font
config.name.lzmaFastBytes = LZMA fast bytes (valid values: 5-255)
config.description.lzmaFastBytes = Fast bytes parameter of the LZMA encoder
#temporary setting, do not translate it
config.name.pluginPath = Plugin Path
config.description.pluginPath = -

View File

@@ -0,0 +1,32 @@
/*
* 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.helpers;
import com.jpexs.decompiler.flash.action.ActionList;
/**
*
* @author JPEXS
*/
public class EmptySWFDecompilerListener implements SWFDecompilerListener {
@Override
public void actionListParsed(ActionList actions) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.helpers;
import com.jpexs.decompiler.flash.action.ActionList;
/**
*
* @author JPEXS
*/
public interface SWFDecompilerListener {
void actionListParsed(ActionList actions);
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2010-2014 JPEXS, Miron Sadziak
*
* 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.helpers;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.plugin.CharSequenceJavaFileObject;
import com.jpexs.helpers.plugin.ClassFileManager;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
/**
*
* @author JPEXS
*/
public class SWFDecompilerPlugin {
public static SWFDecompilerListener listener;
public static void loadPlugin(String path) {
// Here we specify the source code of the class to be compiled
String src = Helper.readTextFile(path);
// Full name of the class that will be compiled.
// If class should be in some package,
// fullName should contain it too
// (ex. "testpackage.DynaClass")
int idx = src.indexOf("public class ");
String fullName = src.substring(idx + 13);
fullName = fullName.substring(0, fullName.indexOf(' ')).trim();
// We get an instance of JavaCompiler. Then
// we create a file manager
// (our custom implementation of it)
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager fileManager = new
ClassFileManager(compiler.getStandardFileManager(null, null, null));
// Dynamic compiling requires specifying
// a list of "files" to compile. In our case
// this is a list containing one "file" which is in our case
// our own implementation (see details below)
List<JavaFileObject> jfiles = new ArrayList<>();
jfiles.add(new CharSequenceJavaFileObject(fullName, src));
// We specify a task to the compiler. Compiler should use our file
// manager and our list of "files".
// Then we run the compilation with call()
compiler.getTask(null, fileManager, null, null, null, jfiles).call();
// 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

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2010-2014 JPEXS, Miron Sadziak
*
* 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.helpers.plugin;
import java.net.URI;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
/**
*
* @author JPEXS
*/
public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
/**
* CharSequence representing the source code to be compiled
*/
private final CharSequence content;
/**
* This constructor will store the source code in the internal "content"
* variable and register it as a source code, using a URI containing the
* class full name
*
* @param className name of the public class in the source code
* @param content source code to compile
*/
public CharSequenceJavaFileObject(String className, CharSequence content) {
super(URI.create("string:///" + className.replace('.', '/')
+ Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
/**
* Answers the CharSequence to be compiled. It will give the source code
* stored in variable "content"
*
* @param ignoreEncodingErrors
* @return
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2010-2014 JPEXS, Miron Sadziak
*
* 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.helpers.plugin;
import java.io.IOException;
import java.security.SecureClassLoader;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;
/**
*
* @author JPEXS
*/
public class ClassFileManager extends
ForwardingJavaFileManager<JavaFileManager> {
/**
* Instance of JavaClassObject that will store the compiled bytecode of our
* class
*/
private JavaClassObject jclassObject;
/**
* Will initialize the manager with the specified standard java file manager
*
* @param standardManager
*/
public ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
}
/**
* Will be used by us to get the class loader for our compiled class. It
* creates an anonymous class extending the SecureClassLoader which uses the
* byte code created by the compiler and stored in the JavaClassObject, and
* returns the Class for it
*
* @param location
* @return
*/
@Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader() {
@Override
protected Class<?> findClass(String name)
throws ClassNotFoundException {
byte[] b = jclassObject.getBytes();
return super.defineClass(name, jclassObject
.getBytes(), 0, b.length);
}
};
}
/**
* Gives the compiler an instance of the JavaClassObject so that the
* compiler can write the byte code into it.
*
* @param location
* @param className
* @param kind
* @param sibling
* @return
* @throws java.io.IOException
*/
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, Kind kind, FileObject sibling)
throws IOException {
jclassObject = new JavaClassObject(className, kind);
return jclassObject;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2010-2014 JPEXS, Miron Sadziak
*
* 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.helpers.plugin;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
/**
*
* @author JPEXS
*/
public class JavaClassObject extends SimpleJavaFileObject {
/**
* Byte code created by the compiler will be stored in this
* ByteArrayOutputStream so that we can later get the byte array out of it
* and put it in the memory as an instance of our class.
*/
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
/**
* Registers the compiled class object under URI containing the class full
* name
*
* @param name Full name of the compiled class
* @param kind Kind of the data. It will be CLASS in our case
*/
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/')
+ kind.extension), kind);
}
/**
* Will be used by our file manager to get the byte code that can be put
* into memory to instantiate our class
*
* @return compiled byte code
*/
public byte[] getBytes() {
return bos.toByteArray();
}
/**
* Will provide the compiler with an output stream that leads to our byte
* array. This way the compiler will write everything into the byte array
* that we will instantiate later
*
* @return
* @throws java.io.IOException
*/
@Override
public OutputStream openOutputStream() throws IOException {
return bos;
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.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.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
import com.jpexs.decompiler.flash.helpers.HilightedTextWriter;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.testng.Assert;
import static org.testng.Assert.fail;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
*
* @author JPEXS
*/
public class ActionScript2ModificationTest extends ActionStript2TestBase {
@BeforeClass
public void init() throws IOException, InterruptedException {
Main.initLogging(false);
Configuration.autoDeobfuscate.set(false);
swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as2/as2.swf")), false);
}
private String normalizeLabels(String actions) {
int labelCnt = 1;
while (true) {
Pattern pattern = Pattern.compile("^([a-z][0-9a-z]+):", Pattern.MULTILINE);
Matcher matcher = pattern.matcher(actions);
if (matcher.find()) {
String str = matcher.group(1);
actions = actions.replaceAll(str, "label_" + labelCnt++);
}
else {
break;
}
}
return actions;
}
@Test
public void testRemoveAction() {
String actionsString =
"ConstantPool\n" +
"Push 1\n" +
"Jump l1\n" +
"l1:Push 2";
String expectedResult =
"ConstantPool\n" +
"Push 1 2\n";
try {
ActionList actions = ASMParser.parse(0, true, actionsString, swf.version, false);
actions.removeAction(2);
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 = actualResult.replaceAll("[ \r\n]", "");
expectedResult = expectedResult.replaceAll("[ \r\n]", "");
Assert.assertEquals(actualResult, expectedResult);
} catch (IOException | ParseException | InterruptedException ex) {
fail();
}
}
}