decompilation timeouts

This commit is contained in:
Honfika
2013-08-20 19:18:01 +02:00
parent 6480721007
commit 0ef3a7dea1
7 changed files with 129 additions and 32 deletions
@@ -53,7 +53,15 @@ public class Configuration {
* Limit of code subs (for obfuscated code)
*/
public static final int SUBLIMITER = 500;
//using parameter names in decompiling may cause problems because oficial programs like Flash CS 5.5 inserts wrong parameter names indices
/**
* Decompilation timeout in seconds
*/
public static final int DECOMPILATION_TIMEOUT = 30 * 60;
/**
* Decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds
*/
public static final int DECOMPILATION_TIMEOUT_SINGLE_METHOD = 5;
//using parameter names in decompiling may cause problems because official programs like Flash CS 5.5 inserts wrong parameter names indices
public static final boolean PARAM_NAMES_ENABLE = false;
private static HashMap<String, Object> config = new HashMap<>();
/**
@@ -622,9 +622,9 @@ public class SWF {
try {
executor.shutdown();
executor.awaitTermination(30, TimeUnit.MINUTES);
executor.awaitTermination(Configuration.DECOMPILATION_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, "30 minutes ActionScript export limit reached", ex);
Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.DECOMPILATION_TIMEOUT) + " ActionScript export limit reached", ex);
}
return ret;
}
@@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.CodeStats;
import com.jpexs.decompiler.flash.abc.avm2.ConstantPool;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.helpers.Highlighting;
import com.jpexs.decompiler.graph.Graph;
@@ -31,6 +32,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -103,7 +108,7 @@ public class MethodBody implements Cloneable, Serializable {
return ret;
}
public String toString(String path, boolean pcode, boolean isStatic, int scriptIndex, int classIndex, ABC abc, ConstantPool constants, MethodInfo method_info[], Stack<GraphTargetItem> scopeStack, boolean isStaticInitializer, boolean hilight, List<String> fullyQualifiedNames, Traits initTraits) {
public String toString(final String path, boolean pcode, final boolean isStatic, final int scriptIndex, final int classIndex, final ABC abc, final ConstantPool constants, final MethodInfo method_info[], final Stack<GraphTargetItem> scopeStack, final boolean isStaticInitializer, final boolean hilight, final List<String> fullyQualifiedNames, final Traits initTraits) {
if (debugMode) {
System.err.println("Decompiling " + path);
}
@@ -118,30 +123,46 @@ public class MethodBody implements Cloneable, Serializable {
if (pcode) {
s += code.toASMSource(constants, this, false);
} else {
AVM2Code deobfuscated = null;
MethodBody b = (MethodBody) Helper.deepCopy(this);
deobfuscated = b.code;
deobfuscated.markMappedOffsets();
if ((Boolean) Configuration.getConfig("autoDeobfuscate", true)) {
try {
deobfuscated.removeTraps(constants, b, abc, scriptIndex, classIndex, isStatic, path);
} catch (Exception | StackOverflowError ex) {
Logger.getLogger(MethodBody.class.getName()).log(Level.SEVERE, "Error during remove traps in " + path, ex);
}
}
//deobfuscated.restoreControlFlow(constants, b);
//try {
s += deobfuscated.toSource(path, isStatic, scriptIndex, classIndex, abc, constants, method_info, b, hilight, getLocalRegNames(abc), scopeStack, isStaticInitializer, fullyQualifiedNames, initTraits, Graph.SOP_USE_STATIC, new HashMap<Integer, Integer>(), deobfuscated.visitCode(b));
s = s.trim();
if (s.equals("")) {
s = " ";
}
if (hilight) {
s = Highlighting.hilighMethod(s, this.method_info);
try {
s += Helper.timedCall(new Callable<String>() {
@Override
public String call() throws Exception {
return toSource(path, isStatic, scriptIndex, classIndex, abc, constants, method_info, scopeStack, isStaticInitializer, hilight, fullyQualifiedNames, initTraits);
}
}, Configuration.DECOMPILATION_TIMEOUT_SINGLE_METHOD, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex);
s += "/*\r\n * Decompilation error\r\n * Timeout (" + Helper.formatTimeToText(Configuration.DECOMPILATION_TIMEOUT_SINGLE_METHOD) + ") was reached\r\n */";
}
}
return s;
}
public String toSource(String path, boolean isStatic, int scriptIndex, int classIndex, ABC abc, ConstantPool constants, MethodInfo method_info[], Stack<GraphTargetItem> scopeStack, boolean isStaticInitializer, boolean hilight, List<String> fullyQualifiedNames, Traits initTraits) {
AVM2Code deobfuscated = null;
MethodBody b = (MethodBody) Helper.deepCopy(this);
deobfuscated = b.code;
deobfuscated.markMappedOffsets();
if ((Boolean) Configuration.getConfig("autoDeobfuscate", true)) {
try {
deobfuscated.removeTraps(constants, b, abc, scriptIndex, classIndex, isStatic, path);
} catch (Exception | StackOverflowError ex) {
Logger.getLogger(MethodBody.class.getName()).log(Level.SEVERE, "Error during remove traps in " + path, ex);
}
}
//deobfuscated.restoreControlFlow(constants, b);
//try {
String s = deobfuscated.toSource(path, isStatic, scriptIndex, classIndex, abc, constants, method_info, b, hilight, getLocalRegNames(abc), scopeStack, isStaticInitializer, fullyQualifiedNames, initTraits, Graph.SOP_USE_STATIC, new HashMap<Integer, Integer>(), deobfuscated.visitCode(b));
s = s.trim();
if (s.equals("")) {
s = " ";
}
if (hilight) {
s = Highlighting.hilighMethod(s, this.method_info);
}
return s;
}
@Override
public Object clone() throws CloneNotSupportedException {
@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.Configuration;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
@@ -49,6 +50,7 @@ import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.helpers.Highlighting;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
@@ -62,6 +64,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -408,7 +413,7 @@ public class Action implements GraphSourceItem {
* @param path
* @return ASM source as String
*/
public static String actionsToString(List<DisassemblyListener> listeners, long address, List<Action> list, List<Long> importantOffsets, List<String> constantPool, int version, boolean hex, long swfPos, String path) {
private static String actionsToString(List<DisassemblyListener> listeners, long address, List<Action> list, List<Long> importantOffsets, List<String> constantPool, int version, boolean hex, long swfPos, String path) {
long offset;
if (importantOffsets == null) {
//setActionsAddresses(list, 0, version);
@@ -434,6 +439,10 @@ public class Action implements GraphSourceItem {
}
pos++;
if (hex) {
if (lastPush) {
ret.append("\r\n");
lastPush = false;
}
ret.append("<ffdec:hex>");/* +"0x"+Helper.formatAddress(a.getFileAddress())+": "+*/;
ret.append(Helper.bytesToHexString(a.getBytes(version)));
ret.append("</ffdec:hex>\r\n");
@@ -707,15 +716,22 @@ public class Action implements GraphSourceItem {
* @param path
* @return String with Source code
*/
public static String actionsToSource(List<Action> actions, int version, String path) {
public static String actionsToSource(final List<Action> actions, final int version, final String path) {
try {
//List<ActionItem> tree = actionsToTree(new HashMap<Integer, String>(), actions, version);
int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC;
return Helper.timedCall(new Callable<String>() {
@Override
public String call() throws Exception {
//List<ActionItem> tree = actionsToTree(new HashMap<Integer, String>(), actions, version);
int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC;
List<GraphTargetItem> tree = actionsToTree(new HashMap<Integer, String>(), new HashMap<String, GraphTargetItem>(), new HashMap<String, GraphTargetItem>(), actions, version, staticOperation, path);
List<GraphTargetItem> tree = actionsToTree(new HashMap<Integer, String>(), new HashMap<String, GraphTargetItem>(), new HashMap<String, GraphTargetItem>(), actions, version, staticOperation, path);
return Graph.graphToString(tree);
return Graph.graphToString(tree);
}
}, Configuration.DECOMPILATION_TIMEOUT_SINGLE_METHOD, TimeUnit.SECONDS);
} catch (TimeoutException ex) {
Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex);
return "/*\r\n * Decompilation error\r\n * Timeout (" + Helper.formatTimeToText(Configuration.DECOMPILATION_TIMEOUT_SINGLE_METHOD) + ") was reached\r\n */";
} catch (Exception | OutOfMemoryError | StackOverflowError ex2) {
Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex2);
if (ex2 instanceof OutOfMemoryError) {
@@ -1404,6 +1404,12 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel
@Override
public void run() {
if (record.getLevel() == Level.SEVERE) {
if (errorNotificationButton == null) {
// todo: honfika
// why null?
return;
}
errorNotificationButton.setIcon(View.getIcon("error16"));
errorNotificationButton.setToolTipText(translate("errors.present"));
if (timer != null) {
@@ -168,7 +168,7 @@ public class ActionPanel extends JPanel implements ActionListener {
private void cacheScript(ASMSource src) {
if (!cache.contains(src)) {
List<Action> as = src.getActions(SWF.DEFAULT_VERSION);
String s = com.jpexs.decompiler.flash.action.Action.actionsToSource(as, SWF.DEFAULT_VERSION, src.toString()/*FIXME?*/);
String s = Action.actionsToSource(as, SWF.DEFAULT_VERSION, src.toString()/*FIXME?*/);
List<Highlighting> hilights = Highlighting.getInstrHighlights(s);
String srcNoHex = Highlighting.stripHilights(s);
cache.put(src, new CachedScript(srcNoHex, hilights));
@@ -567,6 +567,10 @@ public class ActionPanel extends JPanel implements ActionListener {
}
public void setDecompiledEditMode(boolean val) {
if (lastASM == null) {
return;
}
String pref = lastASM.getActionSourcePrefix();
int lastPos = decompiledEditor.getCaretPosition();
int lastLine = decompiledEditor.getLine();
@@ -34,6 +34,13 @@ import java.util.BitSet;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -46,6 +53,8 @@ import java.util.regex.Pattern;
*/
public class Helper {
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
/**
* Converts array of int values to string
*
@@ -504,4 +513,37 @@ public class Helper {
timeStr += Helper.padZeros(timeS, 2) + "." + Helper.padZeros(timeMs, 3);
return timeStr;
}
public static String formatTimeToText(int timeS) {
long timeM = timeS / 60;
timeS = timeS % 60;
long timeH = timeM / 60;
timeM = timeM % 60;
String timeStr = "";
if (timeH > 0) {
timeStr += timeH + (timeH > 1 ? " hours" : " hour");
}
if (timeM > 0) {
if (timeStr.length() > 0) {
timeStr += " and ";
}
timeStr += timeM + (timeM > 1 ? " minutes" : " minute");
}
if (timeS > 0) {
if (timeStr.length() > 0) {
timeStr += " and ";
}
timeStr += timeS + (timeS > 1 ? " seconds" : " second");
}
// (currently) used only in log, so no localization is required
return timeStr;
}
public static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
FutureTask<T> task = new FutureTask<T>(c);
THREAD_POOL.execute(task);
return task.get(timeout, timeUnit);
}
}