From 114f77467297886f51ce146e9eedbf67015a9683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 17 Feb 2026 20:50:57 +0100 Subject: [PATCH] Added: #2636 AS3 QName properties with nonvalid identifiers handled as square brackets --- CHANGELOG.md | 1 + .../flash/abc/avm2/model/AVM2Item.java | 49 +++++++++-- .../abc/avm2/model/FullMultinameAVM2Item.java | 37 ++++++-- .../decompiler/flash/helpers/NulWriter.java | 16 ++++ .../ActionScript3AssembledDecompileTest.java | 14 +++- .../ActionScript3ClassicAirDecompileTest.java | 1 - .../ActionScript3ClassicDecompileTest.java | 2 - .../as3_assembled-0/as3_assembled-0.main.abc | Bin 11233 -> 11504 bytes .../as3_assembled-0.main.asasm | 1 + .../tests/TestObfusProperty.class.asasm | 79 ++++++++++++++++++ .../tests/TestObfusProperty.script.asasm | 29 +++++++ .../as3_assembled/bin/as3_assembled.swf | Bin 6462 -> 6611 bytes 12 files changed, 208 insertions(+), 21 deletions(-) create mode 100644 libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.class.asasm create mode 100644 libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestObfusProperty.script.asasm 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 17f1b2d08ea25ef696164158737eeafc53572582..bf7bfa7c0b5dd389b9e761eb3b6b48c2ebd7f0e6 100644 GIT binary patch delta 3624 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 R=V$SMq49Hz{5x}Zq6J!{%8>v7 literal 6462 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