Fixed: #2486 decompilation timeout on infinite loop

This commit is contained in:
Jindra Petřík
2025-07-17 22:42:32 +02:00
parent 415a10502c
commit b471b48ba7
18 changed files with 344 additions and 266 deletions

View File

@@ -17,32 +17,49 @@
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorGroupParts;
import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorJumps;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser;
import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.ClassInfo;
import com.jpexs.decompiler.flash.abc.types.ConvertData;
import com.jpexs.decompiler.flash.abc.types.Float4;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.ValueKind;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.ScopeStack;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeoutException;
import macromedia.asc.util.Decimal128;
import org.testng.Assert;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
@@ -65,9 +82,98 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
Configuration.padAs3PCodeInstructionName.set(false);
Configuration.useOldStyleGetSetLocalsAs3PCode.set(false);
Configuration.labelOnSeparateLineAs3PCode.set(true);
Configuration.decompilationTimeoutSingleMethod.set(5);
swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false);
}
private void decompilePCode(String pCode, String expected) throws Exception {
pCode = "code\r\n"
+ "getlocal0\r\n"
+ "pushscope\r\n"
+ pCode
+ "returnvoid\r\n";
final ABC abc = new ABC(new ABCContainerTag() {
@Override
public ABC getABC() {
return null;
}
@Override
public SWF getSwf() {
return swf;
}
@Override
public int compareTo(ABCContainerTag o) {
return 0;
}
@Override
public void setABC(ABC abc) {
}
});
MethodBody b = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]);
AVM2Code code = ASM3Parser.parse(abc, new StringReader(pCode), null, new MissingSymbolHandler() {
//no longer ask for adding new constants
@Override
public boolean missingString(String value) {
return true;
}
@Override
public boolean missingInt(long value) {
return true;
}
@Override
public boolean missingUInt(long value) {
return true;
}
@Override
public boolean missingDouble(double value) {
return true;
}
@Override
public boolean missingDecimal(Decimal128 value) {
return true;
}
@Override
public boolean missingFloat(float value) {
return true;
}
@Override
public boolean missingFloat4(Float4 value) {
return true;
}
}, b, new MethodInfo());
b.setCode(code);
abc.addMethodBody(b);
abc.addMethodInfo(new MethodInfo(new int[]{0}, 0, 0, 0, new ValueKind[0], new int[0]));
ClassInfo ci = new ClassInfo();
InstanceInfo ii = new InstanceInfo();
ii.name_index = abc.constants.addMultiname(
Multiname.createQName(
false,
abc.constants.getStringId("Test", true),
abc.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true)
));
abc.addClass(ci, ii, 0);
ScriptInfo si = new ScriptInfo(new Traits());
abc.script_info.add(si);
code.removeTraps(null, 0, b, abc, 0, -1, true, pCode);
code.removeLabelsAndDebugLine(b);
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
String actual = b.toSource(10, new ArrayList<>(), swf.getAbcIndex(), 0, new HashSet<>());
actual = actual.replace("\r\n", "\n");
assertEquals(actual, expected);
}
private String recompilePCode(String str, SWFDecompilerAdapter deobfuscator) throws IOException, AVM2ParseException, InterruptedException {
str = "code\r\n"
+ "getlocal0\r\n"
@@ -316,6 +422,158 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
+ "returnvoid\r\n");
}
@Test
public void testIgnoreLabels() throws Exception {
decompilePCode("getlocal1\n"
+ " setlocal2\n"
+ " pushfalse\n"
+ " setlocal3\n"
+ " pushbyte 0\n"
+ " convert_u\n"
+ " setlocal 4\n"
+ " jump ofs00a4\n"
+ " ofs000f:\n"
+ " label\n"
+ " jump ofs001d\n"
+ " ofs0014:\n"
+ " label\n"
+ " getlocal2\n"
+ " pushstring \"C\"\n"
+ " ifne ofs001d\n"
+ " ofs001c:\n"
+ " label\n" //this label critical to be removed
+ " ofs001d:\n"
+ " returnvoid\n"
+ " pushtrue\n"
+ " iftrue ofs0023\n"
+ " ofs0023:\n"
+ " label\n"
+ " getlocal0\n"
+ " getlocal1\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"trace\"), 1\n"
+ " jump ofs002d\n"
+ " ofs002d:\n"
+ " label\n"
+ " jump ofs000f\n"
+ " getlocal0\n"
+ " getproperty QName(PackageNamespace(\"\"),\"trace\")\n"
+ " getlocal1\n"
+ " callpropvoid QName(Namespace(\"http://adobe.com/AS3/2006/builtin\"),\"push\"), 1\n"
+ " jump ofs003d\n"
+ " ofs003d:\n"
+ " label\n"
+ " getlocal 4\n"
+ " increment\n"
+ " convert_u\n"
+ " setlocal 4\n"
+ " jump ofs0048\n"
+ " ofs0048:\n"
+ " label\n"
+ " jump ofs002d\n"
+ " label\n"
+ " getlocal1\n"
+ " getlocal0\n"
+ " getproperty QName(PackageNamespace(\"\"),\"xxx\")\n"
+ " getlocal 4\n"
+ " getproperty QName(PackageNamespace(\"\"),\"xxx\")\n"
+ " ifne ofs005c\n"
+ " ofs005c:\n"
+ " pushtrue\n"
+ " setlocal3\n"
+ " jump ofs0062\n"
+ " ofs0062:\n"
+ " label\n"
+ " jump ofs001c\n"
+ " ofs0067:\n"
+ " label\n"
+ " getlocal2\n"
+ " pushstring \"B\"\n"
+ " ifne ofs0014\n"
+ " pushbyte 0\n"
+ " convert_u\n"
+ " setlocal 4\n"
+ " jump ofs0048\n"
+ " ofs0078:\n"
+ " label\n"
+ " findproperty QName(PackageNamespace(\"\"),\"trace\")\n"
+ " getlocal1\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"trace\"), 1\n"
+ " jump ofs0062\n"
+ " ofs0083:\n"
+ " label\n"
+ " getlocal3\n"
+ " pushfalse\n"
+ " ifne ofs0062\n"
+ " jump ofs0078\n"
+ " label\n"
+ " getlocal 4\n"
+ " increment\n"
+ " convert_u\n"
+ " setlocal 4\n"
+ " jump ofs0099\n"
+ " ofs0099:\n"
+ " label\n"
+ " jump ofs0083\n"
+ " pushtrue\n"
+ " setlocal3\n"
+ " jump ofs00a4\n"
+ " ofs00a4:\n"
+ " getlocal2\n"
+ " pushstring \"A\"\n"
+ " ifne ofs0067\n"
+ " pushbyte 0\n"
+ " convert_u\n"
+ " setlocal 4\n"
+ " jump ofs0099\n", " var _loc2_:* = param1;\n"
+ " var _loc4_:uint = 0;\n"
+ " if(_loc2_ == \"A\")\n"
+ " {\n"
+ " _loc4_ = 0;\n"
+ " trace(param1);\n"
+ " }\n"
+ " else if(_loc2_ == \"B\")\n"
+ " {\n"
+ " _loc4_ = 0;\n"
+ " }\n"
+ " else if(_loc2_ == \"C\")\n"
+ " {\n"
+ " }\n");
}
@Test
public void testWhileTrue() throws Exception {
decompilePCode(
" getlex QName(PackageNamespace(\"\"),\"Math\")\n" +
" getlex QName(PackageNamespace(\"\"),\"Math\")\n" +
" debugline 8\n" +
" callproperty QName(PackageNamespace(\"\"),\"random\"), 0\n" +
" pushbyte 6\n" +
" multiply\n" +
" callproperty QName(PackageNamespace(\"\"),\"floor\"), 1\n" +
" convert_i\n" +
" setlocal1\n" +
" getlocal1\n" +
" debugline 10\n" +
" pushbyte 4\n" +
" ifngt ofs0034\n" +
" jump ofs0030\n" +
" ofs002d:\n" +
" label\n" +
" debugline 11\n" +
" ofs0030:\n" +
" jump ofs002d\n" +
" ofs0034:\n" +
" ", " param1 = Math.floor(Math.random() * 6);\n" +
" if(param1 <= 4)\n" +
" {\n" +
" return;\n" +
" }\n" +
" while(true)\n" +
" {\n" +
" }\n");
}
// TODO: JPEXS @Test
public void testNotRemoveParams() throws Exception {
String res = recompile("function tst(p1,p2){"

View File

@@ -284,29 +284,6 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT
false);
}
@Test
public void testLabel() {
decompileMethod("assembled", "testLabel", "var _loc2_:String = param1.substr(0,1);\r\n"
+ "var _loc3_:Boolean = false;\r\n"
+ "var _loc4_:uint = 0;\r\n"
+ "if(_loc2_ == \"E\")\r\n"
+ "{\r\n"
+ "_loc4_ = 0;\r\n"
+ "if(_loc3_ == false)\r\n"
+ "{\r\n"
+ "trace(param1);\r\n"
+ "}\r\n"
+ "}\r\n"
+ "else if(_loc2_ == \"A\")\r\n"
+ "{\r\n"
+ "_loc4_ = 0;\r\n"
+ "}\r\n"
+ "else if(_loc2_ == \"L\")\r\n"
+ "{\r\n"
+ "}\r\n",
false);
}
@Test
public void testLocalRegIf() {
decompileMethod("assembled", "testLocalRegIf", "var _loc1_:int = 8;\r\n"
@@ -463,33 +440,33 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT
+ "case 1:\r\n"
+ "case 6:\r\n"
+ "trace(\"1-6\");\r\n"
+ "addr0121:\r\n"
+ "addr0120:\r\n"
+ "trace(\"F\");\r\n"
+ "break;\r\n"
+ "case 5:\r\n"
+ "trace(\"5\");\r\n"
+ "addr011a:\r\n"
+ "addr0119:\r\n"
+ "trace(\"E\");\r\n"
+ "§§goto(addr0121);\r\n"
+ "§§goto(addr0120);\r\n"
+ "case 7:\r\n"
+ "trace(\"7\");\r\n"
+ "addr0113:\r\n"
+ "addr0112:\r\n"
+ "trace(\"D\");\r\n"
+ "§§goto(addr011a);\r\n"
+ "§§goto(addr0119);\r\n"
+ "case 2:\r\n"
+ "trace(\"2\");\r\n"
+ "addr010c:\r\n"
+ "addr010b:\r\n"
+ "trace(\"C\");\r\n"
+ "§§goto(addr0113);\r\n"
+ "§§goto(addr0112);\r\n"
+ "case 8:\r\n"
+ "trace(\"8\");\r\n"
+ "addr0105:\r\n"
+ "addr0104:\r\n"
+ "trace(\"B\");\r\n"
+ "§§goto(addr010c);\r\n"
+ "§§goto(addr010b);\r\n"
+ "default:\r\n"
+ "trace(\"def\");\r\n"
+ "trace(\"A\");\r\n"
+ "§§goto(addr0105);\r\n"
+ "§§goto(addr0104);\r\n"
+ "}\r\n"
+ "trace(\"G\");\r\n"
+ "return null;\r\n",

View File

@@ -425,8 +425,8 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile
decompileMethod("classic_air", "testDefaultNotLastGrouped", "var k:int = 10;\r\n"
+ "switch(k)\r\n"
+ "{\r\n"
+ "case \"six\":\r\n"
+ "default:\r\n"
+ "case \"six\":\r\n"
+ "trace(\"def and 6\");\r\n"
+ "case \"five\":\r\n"
+ "trace(\"def and 6 and 5\");\r\n"
@@ -2423,6 +2423,19 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile
false);
}
@Test
public void testWhileTrue() {
decompileMethod("classic_air", "testWhileTrue", "var a:int = Math.floor(Math.random() * 6);\r\n"
+ "if(a <= 4)\r\n"
+ "{\r\n"
+ "return;\r\n"
+ "}\r\n"
+ "while(true)\r\n"
+ "{\r\n"
+ "}\r\n",
false);
}
@Test
public void testWhileTry() {
decompileMethod("classic_air", "testWhileTry", "while(true)\r\n"

View File

@@ -2416,6 +2416,18 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes
false);
}
@Test
public void testWhileTrue() {
decompileMethod("classic", "testWhileTrue", "var a:int = Math.floor(Math.random() * 6);\r\n"
+ "if(a > 4)\r\n"
+ "{\r\n"
+ "while(true)\r\n"
+ "{\r\n"
+ "}\r\n"
+ "}\r\n",
false);
}
@Test
public void testWhileTry() {
decompileMethod("classic", "testWhileTry", "while(true)\r\n"