diff --git a/CHANGELOG.md b/CHANGELOG.md index 977ad7149..4702aaaca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. (Debug listening capability - only for SWFs previously prepared by FFDec) - TTF export - setting italic / bold flags in the font - ffdec-cli.exe is signed +- [#2636] AS3 QName properties with nonvalid identifiers handled as square brackets ### Fixed - Metadata formatting diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java index 45f2731d2..1d29a2e1f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java @@ -28,15 +28,19 @@ import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.StringBuilderTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Reference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -156,7 +160,8 @@ public abstract class AVM2Item extends GraphTargetItem { } if (empty) { - return propertyName.toString(writer, localData); + ((FullMultinameAVM2Item) propertyName).appendTo(writer, localData, false); + return writer; } if (propertyName instanceof FullMultinameAVM2Item) { @@ -177,15 +182,45 @@ public abstract class AVM2Item extends GraphTargetItem { String operator = nullCondition ? "?." : "."; - if (((FullMultinameAVM2Item) propertyName).name != null) { - if (((FullMultinameAVM2Item) propertyName).namespace != null) { - writer.allowWrapHere().hilightSpecial(operator, HighlightSpecialType.PROPERTY_TYPE, 0, data); + String localName = ""; + boolean isAttribute = false; + boolean isValidName = false; + String namespaceSuffix = ""; + + if (multinameIndex >= 0 && multinameIndex < localData.constantsAvm2.getMultinameCount()) { + Reference customNsRef = new Reference<>(null); + isAttribute = localData.constantsAvm2.getMultiname(multinameIndex).isAttribute(); + localName = localData.constantsAvm2.getMultiname(multinameIndex).getNameAndCustomNamespace(new HashSet<>(), localData.abc, new ArrayList<>(), true, true, customNsRef); + namespaceSuffix = localData.constantsAvm2.getMultiname(multinameIndex).getNamespaceSuffix(); + + if ("*".equals(localName)) { + isValidName = true; } - return propertyName.toString(writer, localData); + if (isAttribute) { + isValidName = true; + } + if (!"".equals(namespaceSuffix)) { + isValidName = true; + } + if (IdentifiersDeobfuscation.isValidName(true, localName)) { + isValidName = true; + } } else { - writer.allowWrapHere().hilightSpecial(operator, HighlightSpecialType.PROPERTY_TYPE, 0, data); - return propertyName.toString(writer, localData); + isValidName = true; + } + + if (isValidName) { + if (((FullMultinameAVM2Item) propertyName).name != null) { + if (((FullMultinameAVM2Item) propertyName).namespace != null) { + writer.allowWrapHere().hilightSpecial(operator, HighlightSpecialType.PROPERTY_TYPE, 0, data); + } + } else { + writer.allowWrapHere().hilightSpecial(operator, HighlightSpecialType.PROPERTY_TYPE, 0, data); + } } + + ((FullMultinameAVM2Item) propertyName).appendTo(writer, localData, true); + return writer; } else { writer.append("[").allowWrapHere(); propertyName.toString(writer, localData); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java index aea6d9357..a302cade3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java @@ -29,9 +29,11 @@ import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.TypeItem; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Helper; import com.jpexs.helpers.Reference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; @@ -186,14 +188,13 @@ public class FullMultinameAVM2Item extends AVM2Item { @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return appendTo(writer, localData, false); + } + + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData, boolean afterDot) throws InterruptedException { if (namespace != null) { namespace.toString(writer, localData); writer.append("::"); - } else { - /*Namespace ns = constants.getMultiname(multinameIndex).getNamespace(constants); - if ((ns != null)&&(ns.name_index!=0)) { - ret = hilight(ns.getName(constants) + "::")+ret; - }*/ } if (name != null) { writer.append("["); @@ -207,12 +208,20 @@ public class FullMultinameAVM2Item extends AVM2Item { AVM2ConstantPool constants = localData.constantsAvm2; List fullyQualifiedNames = property ? new ArrayList<>() : localData.fullyQualifiedNames; if (multinameIndex > 0 && multinameIndex < constants.getMultinameCount()) { - String simpleName = constants.getMultiname(multinameIndex).getName(localData.usedDeobfuscations, localData.abc, constants, fullyQualifiedNames, true, false); + String simpleName = constants.getMultiname(multinameIndex).getName(new HashSet<>(), localData.abc, constants, fullyQualifiedNames, true, false); if ("*".equals(simpleName)) { writer.append("*"); } else { Reference customNsRef = new Reference<>(null); - String localName = constants.getMultiname(multinameIndex).getNameAndCustomNamespace(localData.usedDeobfuscations, localData.abc, fullyQualifiedNames, false, true, customNsRef); + String localName; + boolean isAttribute = constants.getMultiname(multinameIndex).isAttribute(); + String namespaceSuffix = constants.getMultiname(multinameIndex).getNamespaceSuffix(); + if (!isAttribute && afterDot && namespaceSuffix.isEmpty()) { + //do not deobfuscate + localName = constants.getMultiname(multinameIndex).getNameAndCustomNamespace(new HashSet<>(), localData.abc, fullyQualifiedNames, true, true, customNsRef); + } else { + localName = constants.getMultiname(multinameIndex).getNameAndCustomNamespace(localData.usedDeobfuscations, localData.abc, fullyQualifiedNames, false, true, customNsRef); + } DottedChain customNs = customNsRef.getVal(); if (customNs != null) { String nsname = customNs.getLast(); @@ -221,7 +230,19 @@ public class FullMultinameAVM2Item extends AVM2Item { writer.appendNoHilight("::"); } - writer.append(localName); + if (!isAttribute && afterDot && namespaceSuffix.isEmpty()) { + if (IdentifiersDeobfuscation.isValidName(true, localName)) { + writer.append(localName); + } else { + if (localName.matches("^0|[1-9][0-9]*$")) { + writer.append("[").append(localName).append("]"); + } else { + writer.append("[\"").append(Helper.escapeActionScriptString(localName)).append("\"]"); + } + } + } else { + writer.append(localName); + } } } else { writer.append("§§multiname(").append(multinameIndex).append(")"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/NulWriter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/NulWriter.java index 847a063b1..99d93de0c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/NulWriter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/NulWriter.java @@ -32,6 +32,8 @@ public class NulWriter extends GraphTextWriter { private final Stack stringAddedStack = new Stack<>(); private boolean stringAdded = false; + + private int length = 0; public NulWriter() { super(new CodeFormatting()); @@ -120,54 +122,63 @@ public class NulWriter extends GraphTextWriter { @Override public NulWriter hilightSpecial(String text, HighlightSpecialType type, String specialValue, HighlightData data) { stringAdded = true; + length += text.length(); return this; } @Override public GraphTextWriter appendWithData(String str, HighlightData data) { stringAdded = true; + length += str.length(); return this; } @Override public GraphTextWriter append(char value) { stringAdded = true; + length++; return this; } @Override public GraphTextWriter append(int value) { stringAdded = true; + length += ("" + value).length(); return this; } @Override public GraphTextWriter append(long value) { stringAdded = true; + length += ("" + value).length(); return this; } @Override public NulWriter append(String str) { stringAdded = true; + length += str.length(); return this; } @Override public NulWriter append(String str, long offset, long fileOffset) { stringAdded = true; + length += str.length(); return this; } @Override public NulWriter appendNoHilight(int i) { stringAdded = true; + length += ("" + i).length(); return this; } @Override public NulWriter appendNoHilight(String str) { stringAdded = true; + length += str.length(); return this; } @@ -188,4 +199,9 @@ public class NulWriter extends GraphTextWriter { stringAdded = stringAddedStack.pop() || result; return result; } + + @Override + public int getLength() { + return length; + } } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java index d99fd1a7f..e1b3aa1cd 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java @@ -86,8 +86,8 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT @Test public void testCollidingPublicTraits() { - decompileMethod("assembled", "testCollidingPublicTraits", "trace(\"ns1 = \" + this.a#90);\r\n" - + "trace(\"ns2 = \" + this.a#91);\r\n", + decompileMethod("assembled", "testCollidingPublicTraits", "trace(\"ns1 = \" + this.a#91);\r\n" + + "trace(\"ns2 = \" + this.a#92);\r\n", false); } @@ -335,6 +335,15 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT false); } + @Test + public void testObfusProperty() { + decompileMethod("assembled", "testObfusProperty", "var a:* = new Array();\r\n" + + "a[0] = 5;\r\n" + + "§Hello world§ = 1;\r\n" + + "a[\"one two\"][\"three four\"] = 2;\r\n", + false); + } + @Test public void testPushPlacement() { decompileMethod("assembled", "testPushPlacement", "var a:int = 1;\r\n" @@ -595,7 +604,6 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT + "trace(\"second\");\r\n" + "}\r\n" + "while(_loc5_ <= 100);\r\n" - + "\r\n" + "}\r\n" + "catch(e:Error)\r\n" + "{\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java index 4a1794344..60720bf24 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java @@ -2660,7 +2660,6 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "trace(\"C\");\r\n" + "}\r\n" + "while(i < 5);\r\n" - + "\r\n" + "}\r\n" + "trace(\"E\");\r\n", false); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java index b5a44826d..5e93b5918 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java @@ -597,7 +597,6 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes + "trace(\"z\");\r\n" + "}\r\n" + "while(true);\r\n" - + "\r\n" + "trace(\"g\");\r\n" + "if(b)\r\n" + "{\r\n" @@ -2657,7 +2656,6 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes + "trace(\"C\");\r\n" + "}\r\n" + "while(i < 5);\r\n" - + "\r\n" + "}\r\n" + "trace(\"E\");\r\n", false); diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc index 17f1b2d08..bf7bfa7c0 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc and b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc differ diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm index ffcdba87c..f416b794e 100644 --- a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm @@ -44,5 +44,6 @@ program #include "tests/TestCollidingPublicTraits.script.asasm" #include "tests/TestSetGetLocalOnStack.script.asasm" #include "tests/TestFindPropertyTemp.script.asasm" + #include "tests/TestObfusProperty.script.asasm" ; place to add next end ; program diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.class.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.class.asasm new file mode 100644 index 000000000..3c3843ddf --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.class.asasm @@ -0,0 +1,79 @@ +class + refid "tests:TestObfusProperty" + instance QName(PackageNamespace("tests"), "TestObfusProperty") + extends QName(PackageNamespace(""), "Object") + flag SEALED + flag PROTECTEDNS + protectedns ProtectedNamespace("tests:TestObfusProperty") + iinit + refid "tests:TestObfusProperty/instance/init" + body + maxstack 1 + localcount 1 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + + getlocal0 + constructsuper 0 + + returnvoid + end ; code + end ; body + end ; method + trait slot QName(PackageInternalNs(""),"Hello world") + slotid 0 + type null + value Integer(5) + end ; trait + trait method QName(PackageNamespace(""), "run") + method + refid "tests:TestObfusProperty/instance/run" + returns QName(PackageNamespace(""), "void") + body + maxstack 2 + localcount 4 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + debug 1, "a", 0, 15 + findpropstrict QName(PackageNamespace(""),"Array") + constructprop QName(PackageNamespace(""),"Array"), 0 + coerce_a + setlocal1 + getlocal1 + pushbyte 5 + setproperty QName(PackageNamespace(""),"0") + findproperty QName(PackageInternalNs(""),"Hello world") + pushbyte 1 + setproperty QName(PackageInternalNs(""),"Hello world") + getlocal1 + getproperty QName(PackageNamespace(""),"one two") + pushbyte 2 + setproperty QName(PackageNamespace(""),"three four") + returnvoid + end ; code + end ; body + end ; method + end ; trait + end ; instance + cinit + refid "tests:TestObfusProperty/class/init" + body + maxstack 1 + localcount 1 + initscopedepth 3 + maxscopedepth 4 + code + getlocal0 + pushscope + + returnvoid + end ; code + end ; body + end ; method +end ; class diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.script.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.script.asasm new file mode 100644 index 000000000..10a69dd3d --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.script.asasm @@ -0,0 +1,29 @@ +script + sinit + refid "tests:TestObfusProperty/init" + body + maxstack 2 + localcount 1 + initscopedepth 1 + maxscopedepth 3 + code + getlocal0 + pushscope + + findpropstrict Multiname("TestObfusProperty", [PackageNamespace("tests")]) + getlex QName(PackageNamespace(""), "Object") + pushscope + + getlex Multiname("Object", [PrivateNamespace(null, "tests:TestObfusProperty"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")]) + newclass "tests:TestObfusProperty" + popscope + initproperty QName(PackageNamespace("tests"), "TestObfusProperty") + + returnvoid + end ; code + end ; body + end ; method + trait class QName(PackageNamespace("tests"), "TestObfusProperty") + #include "TestObfusProperty.class.asasm" + end ; trait +end ; script diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf index 69818499f..ed2614be9 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf and b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf differ