Fixed: #2486 AS3 switches detection in some cases

This commit is contained in:
Jindra Petřík
2025-07-20 11:55:37 +02:00
parent 4e62cddacf
commit 7076cdfc36
12 changed files with 816 additions and 63 deletions

View File

@@ -17,7 +17,6 @@
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;
@@ -169,10 +168,10 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
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<>());
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"
@@ -543,37 +542,383 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
@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");
" 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");
}
@Test
public void testObfuscatedSwitch() throws Exception {
decompilePCode("getlocal0\n"
+ " pushbyte 0\n"
+ " initproperty QName(PackageNamespace(\"\"),\"testA\")\n"
+ " jump ofs0012\n"
+ " call 7\n"
+ " throw\n"
+ " callmethod 1413, 7\n"
+ " ofs0012:\n"
+ " jump ofs001a\n"
+ " ifge ofs001a\n"
+ " ofs001a:\n"
+ " jump ofs0024\n"
+ " equals\n"
+ " pushwith\n"
+ " newfunction 30\n"
+ " pop\n"
+ " setlocal2\n"
+ " ofs0024:\n"
+ " jump ofs0132\n"
+ " pushwith\n"
+ " setlocal3\n"
+ " ofs002a:\n"
+ " label\n"
+ " pushtrue\n"
+ " iftrue ofs0036\n"
+ " returnvoid\n"
+ " popscope\n"
+ " callsuper QName(PackageNamespace(\"aaa\"),\"xxx\"), 10\n"
+ " pushwith\n"
+ " ofs0036:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs01ff\n"
+ " pushwith\n"
+ " setlocal0\n"
+ " ofs0040:\n"
+ " label\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " getlocal0\n"
+ " pushtrue\n"
+ " iftrue ofs0052\n"
+ " returnvoid\n"
+ " popscope\n"
+ " pushwith\n"
+ " newfunction 48\n"
+ " pop\n"
+ " divide\n"
+ " ofs0052:\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs01ff\n"
+ " ifgt ofs0061\n"
+ " ofs0061:\n"
+ " label\n"
+ " jump ofs006a\n"
+ " ifstricteq ofs006a\n"
+ " ofs006a:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs0078\n"
+ " popscope\n"
+ " pushwith\n"
+ " newfunction 4\n"
+ " throw\n"
+ " checkfilter\n"
+ " ofs0078:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs01ff\n"
+ " pushwith\n"
+ " setlocal0\n"
+ " getlocal3\n"
+ " ofs0083:\n"
+ " label\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " pushfalse\n"
+ " iffalse ofs0094\n"
+ " returnvoid\n"
+ " popscope\n"
+ " equals\n"
+ " setlocal1\n"
+ " newactivation\n"
+ " increment\n"
+ " decrement\n"
+ " ofs0094:\n"
+ " jump ofs01ff\n"
+ " pushwith\n"
+ " setlocal2\n"
+ " ofs009a:\n"
+ " label\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " getlocal0\n"
+ " jump ofs00a9\n"
+ " modulo\n"
+ " callsupervoid QName(PackageInternalNs(\"xxxx\"),\"tmp\"), 5\n"
+ " pushwith\n"
+ " ofs00a9:\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs00b4\n"
+ " ifge ofs00b4\n"
+ " ofs00b4:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs00c1\n"
+ " popscope\n"
+ " callproplex QName(PackageNamespace(\"\"),\"xxx\"), 18\n"
+ " setlocal2\n"
+ " ofs00c1:\n"
+ " jump ofs01ff\n"
+ " ifstrictne ofs00c9\n"
+ " ofs00c9:\n"
+ " label\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs00d8\n"
+ " nextname\n"
+ " dxns \"position\"\n"
+ " pushwith\n"
+ " setlocal2\n"
+ " returnvalue\n"
+ " ofs00d8:\n"
+ " jump ofs01ff\n"
+ " pushwith\n"
+ " setlocal3\n"
+ " getlocal1\n"
+ " ofs00df:\n"
+ " label\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " getlocal0\n"
+ " jump ofs00ef\n"
+ " popscope\n"
+ " pushwith\n"
+ " newfunction 24\n"
+ " pop\n"
+ " setlocal3\n"
+ " ofs00ef:\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs00fa\n"
+ " ifne ofs00fa\n"
+ " ofs00fa:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " jump ofs0107\n"
+ " popscope\n"
+ " callproplex TypeName(QName(PackageNamespace(\"__AS3__.vec\"),\"Vector\")<QName(PackageNamespace(\"xxx\"),\"aaa\")>), 15\n"
+ " throw\n"
+ " ofs0107:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " pushtrue\n"
+ " iftrue ofs0117\n"
+ " returnvoid\n"
+ " checkfilter\n"
+ " pushwith\n"
+ " newfunction 51\n"
+ " throw\n"
+ " instanceof\n"
+ " ofs0117:\n"
+ " getlocal0\n"
+ " callpropvoid QName(PackageNamespace(\"\"),\"test\"), 0\n"
+ " pushfalse\n"
+ " iffalse ofs0127\n"
+ " returnvoid\n"
+ " popscope\n"
+ " setlocal1\n"
+ " istypelate\n"
+ " setlocal1\n"
+ " setlocal2\n"
+ " divide\n"
+ " ofs0127:\n"
+ " jump ofs01ff\n"
+ " pushwith\n"
+ " setlocal2\n"
+ " getlocal1\n"
+ " jump ofs0132\n"
+ " ofs0132:\n"
+ " getlex QName(PackageNamespace(\"\"),\"Abc\")\n"
+ " getproperty QName(PackageNamespace(\"\"),\"value\")\n"
+ " setlocal1\n"
+ " jump ofs013f\n"
+ " ifle ofs013f\n"
+ " ofs013f:\n"
+ " pushbyte 1\n"
+ " getlocal1\n"
+ " ifstricteq ofs014a\n"
+ " jump ofs015e\n"
+ " ofs014a:\n"
+ " pushbyte 0\n"
+ " jump ofs0156\n"
+ " pushscope\n"
+ " setlocal0\n"
+ " istypelate\n"
+ " nextvalue\n"
+ " istypelate\n"
+ " decrement\n"
+ " ofs0156:\n"
+ " jump ofs01e3\n"
+ " ifgt ofs015e\n"
+ " ofs015e:\n"
+ " pushbyte 2\n"
+ " getlocal1\n"
+ " ifstricteq ofs0169\n"
+ " jump ofs0178\n"
+ " ofs0169:\n"
+ " pushbyte 1\n"
+ " jump ofs01e3\n"
+ " jump ofs0173\n"
+ " ofs0173:\n"
+ " dup\n"
+ " callsuper QName(PackageNamespace(\"xxx\"),\"aaa\"), 27\n"
+ " pop\n"
+ " ofs0178:\n"
+ " pushbyte 3\n"
+ " getlocal1\n"
+ " ifstricteq ofs0183\n"
+ " jump ofs018b\n"
+ " ofs0183:\n"
+ " pushbyte 2\n"
+ " jump ofs01e3\n"
+ " pushwith\n"
+ " setlocal0\n"
+ " ofs018b:\n"
+ " pushbyte 4\n"
+ " getlocal1\n"
+ " ifstrictne ofs01a2\n"
+ " pushbyte 3\n"
+ " jump ofs01e3\n"
+ " jump ofs019c\n"
+ " ofs019c:\n"
+ " convert_s\n"
+ " construct 110\n"
+ " pushwith\n"
+ " nextvalue\n"
+ " setlocal2\n"
+ " ofs01a2:\n"
+ " pushbyte 5\n"
+ " getlocal1\n"
+ " ifstricteq ofs01ad\n"
+ " jump ofs01b5\n"
+ " ofs01ad:\n"
+ " pushbyte 4\n"
+ " jump ofs01e3\n"
+ " pushwith\n"
+ " setlocal2\n"
+ " ofs01b5:\n"
+ " pushbyte 6\n"
+ " jump ofs01bf\n"
+ " ifgt ofs01bf\n"
+ " ofs01bf:\n"
+ " getlocal1\n"
+ " ifstrictne ofs01d4\n"
+ " pushbyte 5\n"
+ " jump ofs01e3\n"
+ " jump ofs01ce\n"
+ " ofs01ce:\n"
+ " popscope\n"
+ " pushwith\n"
+ " newfunction 49\n"
+ " pop\n"
+ " checkfilter\n"
+ " ofs01d4:\n"
+ " jump ofs01e1\n"
+ " throw\n"
+ " setlocal3\n"
+ " getlocal2\n"
+ " pushbyte 6\n"
+ " jump ofs01e1\n"
+ " ofs01e1:\n"
+ " pushbyte 6\n"
+ " ofs01e3:\n"
+ " kill 1\n"
+ " lookupswitch ofs00df, [ofs002a, ofs0040, ofs0061, ofs0083, ofs009a, ofs00c9, ofs00df]\n"
+ " ofs01ff:\n"
+ " getlocal0\n"
+ " pushbyte 0\n"
+ " initproperty QName(PackageNamespace(\"\"),\"testX\")\n"
+ " jump ofs020e\n"
+ " popscope\n"
+ " pushwith\n"
+ " setlocal1\n"
+ " bitxor\n"
+ " convert_d\n"
+ " setlocal2\n"
+ " ofs020e:\n"
+ " pushfalse\n"
+ " iffalse ofs021a\n"
+ " returnvoid\n"
+ " typeof\n"
+ " checkfilter\n"
+ " bitand\n"
+ " in\n"
+ " convert_s\n"
+ " nextvalue\n"
+ " ofs021a:\n"
+ " jump ofs0222\n"
+ " ifgt ofs0222\n"
+ " ofs0222:\n"
+ " pushtrue\n"
+ " iftrue ofs022d\n"
+ " returnvoid\n"
+ " setlocal2\n"
+ " callsuper QName(PackageNamespace(\"xxx\"),\"aaa\"), 15\n"
+ " throw\n"
+ " ofs022d:", " this.testA = 0;\n"
+ " switch(Abc.value)\n"
+ " {\n"
+ " case 1:\n"
+ " this.test();\n"
+ " break;\n"
+ " case 2:\n"
+ " this.test();\n"
+ " this.test();\n"
+ " this.test();\n"
+ " break;\n"
+ " case 3:\n"
+ " this.test();\n"
+ " this.test();\n"
+ " break;\n"
+ " case 4:\n"
+ " this.test();\n"
+ " break;\n"
+ " case 5:\n"
+ " this.test();\n"
+ " this.test();\n"
+ " this.test();\n"
+ " break;\n"
+ " case 6:\n"
+ " this.test();\n"
+ " break;\n"
+ " default:\n"
+ " this.test();\n"
+ " this.test();\n"
+ " this.test();\n"
+ " this.test();\n"
+ " this.test();\n"
+ " }\n"
+ " this.testX = 0;\n");
}
// TODO: JPEXS @Test
public void testNotRemoveParams() throws Exception {
String res = recompile("function tst(p1,p2){"

View File

@@ -1937,6 +1937,35 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile
false);
}
@Test
public void testSwitchBig() {
decompileMethod("classic_air", "testSwitchBig", "var k:int = 10;\r\n"
+ "switch(k)\r\n"
+ "{\r\n"
+ "case \"A\":\r\n"
+ "trace(\"A\");\r\n"
+ "break;\r\n"
+ "case \"B\":\r\n"
+ "case \"C\":\r\n"
+ "trace(\"BC\");\r\n"
+ "break;\r\n"
+ "case \"D\":\r\n"
+ "default:\r\n"
+ "case \"E\":\r\n"
+ "trace(\"D-default-E\");\r\n"
+ "break;\r\n"
+ "case \"F\":\r\n"
+ "trace(\"F no break\");\r\n"
+ "case \"G\":\r\n"
+ "trace(\"G\");\r\n"
+ "break;\r\n"
+ "case \"H\":\r\n"
+ "trace(\"H last\");\r\n"
+ "}\r\n"
+ "trace(\"after switch\");\r\n",
false);
}
@Test
public void testSwitchComma() {
decompileMethod("classic_air", "testSwitchComma", "var b:int = 5;\r\n"
@@ -1948,7 +1977,7 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile
+ "break;\r\n"
+ "case \"B\":\r\n"
+ "trace(\"is B\");\r\n"
+ "case \"C\":\r\n"
+ "case 7, \"C\":\r\n"
+ "trace(\"is C\");\r\n"
+ "}\r\n",
false);

View File

@@ -424,8 +424,8 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes
decompileMethod("classic", "testDefaultNotLastGrouped", "var k:* = 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"
@@ -1924,6 +1924,35 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes
false);
}
@Test
public void testSwitchBig() {
decompileMethod("classic", "testSwitchBig", "var k:* = 10;\r\n"
+ "switch(k)\r\n"
+ "{\r\n"
+ "case \"A\":\r\n"
+ "trace(\"A\");\r\n"
+ "break;\r\n"
+ "case \"B\":\r\n"
+ "case \"C\":\r\n"
+ "trace(\"BC\");\r\n"
+ "break;\r\n"
+ "case \"D\":\r\n"
+ "default:\r\n"
+ "case \"E\":\r\n"
+ "trace(\"D-default-E\");\r\n"
+ "break;\r\n"
+ "case \"F\":\r\n"
+ "trace(\"F no break\");\r\n"
+ "case \"G\":\r\n"
+ "trace(\"G\");\r\n"
+ "break;\r\n"
+ "case \"H\":\r\n"
+ "trace(\"H last\");\r\n"
+ "}\r\n"
+ "trace(\"after switch\");\r\n",
false);
}
@Test
public void testSwitchComma() {
decompileMethod("classic", "testSwitchComma", "var b:int = 5;\r\n"