From 1c29ada719773e4047db03578509b9192ace5e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 7 Oct 2023 17:36:15 +0200 Subject: [PATCH] Fixed #116 AS3 Cyclic typenames protection --- CHANGELOG.md | 3 +- .../com/jpexs/decompiler/flash/abc/ABC.java | 1 + .../flash/abc/avm2/AVM2ConstantPool.java | 6 ++ .../flash/abc/types/InstanceInfo.java | 10 +-- .../decompiler/flash/abc/types/Multiname.java | 84 ++++++++++++++----- 5 files changed, 78 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f14bced4a..f12ba2726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. - [#2095] AS3 Changing script when debugging - [#223] AS2 Detecting uninitialized class fields - Embed tag not properly escaped on obfuscated files +- [#116] AS3 Cyclic typenames protection ## [19.0.0] - 2023-10-01 ### Added @@ -3143,6 +3144,7 @@ Major version of SWF to XML export changed to 2. [#2094]: https://www.free-decompiler.com/flash/issues/2094 [#2095]: https://www.free-decompiler.com/flash/issues/2095 [#223]: https://www.free-decompiler.com/flash/issues/223 +[#116]: https://www.free-decompiler.com/flash/issues/116 [#1449]: https://www.free-decompiler.com/flash/issues/1449 [#2070]: https://www.free-decompiler.com/flash/issues/2070 [#2073]: https://www.free-decompiler.com/flash/issues/2073 @@ -3575,7 +3577,6 @@ Major version of SWF to XML export changed to 2. [#1148]: https://www.free-decompiler.com/flash/issues/1148 [#1152]: https://www.free-decompiler.com/flash/issues/1152 [#1154]: https://www.free-decompiler.com/flash/issues/1154 -[#116]: https://www.free-decompiler.com/flash/issues/116 [#1070]: https://www.free-decompiler.com/flash/issues/1070 [#1098]: https://www.free-decompiler.com/flash/issues/1098 [#1033]: https://www.free-decompiler.com/flash/issues/1033 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index 4b2532835..85170562e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -729,6 +729,7 @@ public class ABC implements Openable { } ais.endDumpLevel(); } + constants.checkCyclicTypeNames(); ais.endDumpLevel(); // cpool_info diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java index 2bdedfea6..7b7698aa5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java @@ -867,4 +867,10 @@ public class AVM2ConstantPool implements Cloneable { } } + + public void checkCyclicTypeNames() { + for (int i = 0; i < constant_multiname.size(); i++) { + Multiname.checkTypeNameCyclic(this, i); + } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java index 20fb3e466..a0379a36b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java @@ -99,11 +99,11 @@ public class InstanceInfo { CharacterTag ct = abc.getSwf().getCharacterByClass(className); if (ct != null) { if (ct instanceof DefineBinaryDataTag) { - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".bin\", mimeType=\"application/octet-stream\")]").newLine(); + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + Helper.makeFileName(ct.getCharacterExportFileName()) + ".bin\", mimeType=\"application/octet-stream\")]").newLine(); } if (ct instanceof ImageTag) { ImageTag it = (ImageTag) ct; - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ((ImageTag) ct).getImageFormat().getExtension() + "\")]").newLine(); + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + Helper.makeFileName(ct.getCharacterExportFileName()) + ((ImageTag) ct).getImageFormat().getExtension() + "\")]").newLine(); } if (ct instanceof DefineSpriteTag) { writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + "assets.swf\", symbol=\"" + Helper.escapeActionScriptString(className) + "\")]").newLine(); @@ -111,7 +111,7 @@ public class InstanceInfo { if (ct instanceof DefineSoundTag) { //should be mp3, otherwise it won't work. Should we convert this? DefineSoundTag st = (DefineSoundTag) ct; - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + "." + (st.getSoundFormat().formatId == SoundFormat.FORMAT_MP3 ? "mp3" : "wav") + "\")]").newLine(); + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + Helper.makeFileName(ct.getCharacterExportFileName()) + "." + (st.getSoundFormat().formatId == SoundFormat.FORMAT_MP3 ? "mp3" : "wav") + "\")]").newLine(); } if (ct instanceof FontTag) { FontTag ft = (FontTag) ct; @@ -125,7 +125,7 @@ public class InstanceInfo { } } - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".ttf\",").newLine(); + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + Helper.makeFileName(ct.getCharacterExportFileName()) + ".ttf\",").newLine(); writer.appendNoHilight("fontName=\"" + Helper.escapeActionScriptString(ft.getFontNameIntag()) + "\",").newLine(); writer.appendNoHilight("fontFamily=\"" + Helper.escapeActionScriptString(ft.getFontName())+ "\",").newLine(); writer.appendNoHilight("mimeType=\"application/x-font\",").newLine(); @@ -174,7 +174,7 @@ public class InstanceInfo { if (ct instanceof DefineFont4Tag) { DefineFont4Tag ft4 = (DefineFont4Tag)ct; - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".cff\",").newLine(); + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + Helper.makeFileName(ct.getCharacterExportFileName()) + ".cff\",").newLine(); writer.appendNoHilight("fontName=\"" + Helper.escapeActionScriptString(ft4.fontName) + "\",").newLine(); writer.appendNoHilight("mimeType=\"application/x-font\",").newLine(); writer.appendNoHilight("fontWeight=\"" + (ft4.fontFlagsBold ? "bold" : "normal") + "\",").newLine(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java index 5d0d75a3e..d6b740cd4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java @@ -80,6 +80,9 @@ public class Multiname { @Internal private boolean displayNamespace = false; + @Internal + private boolean cyclic = false; + public String getNamespaceSuffix() { if (displayNamespace) { return "#" + namespace_index; @@ -157,6 +160,39 @@ public class Multiname { return new Multiname(TYPENAME, 0, 0, 0, qname_index, params); } + public static void checkTypeNameCyclic(AVM2ConstantPool constants, int name_index) { + Set visited = new HashSet<>(); + if (name_index >= constants.getMultinameCount()) { + return; + } + Multiname m = constants.getMultiname(name_index); + if (m != null) { + m.cyclic = checkCyclicTypeNameSub(constants, name_index, visited); + } + } + + private static boolean checkCyclicTypeNameSub(AVM2ConstantPool constants, int name_index, Set visited) { + if (name_index >= constants.getMultinameCount()) { + return false; + } + Multiname m = constants.getMultiname(name_index); + if (m != null && m.kind == Multiname.TYPENAME) { + if (visited.contains(name_index)) { + return true; + } + visited.add(name_index); + if (checkCyclicTypeNameSub(constants, m.qname_index, visited)) { + return true; + } + for (int p : m.params) { + if (checkCyclicTypeNameSub(constants, p, visited)) { + return true; + } + } + } + return false; + } + public boolean isAttribute() { if (kind == QNAMEA) { return true; @@ -272,16 +308,15 @@ public class Multiname { String versionAdd = ""; if (name != null && name.length() > 0) { char lastChar = name.charAt(name.length() - 1); - + if (lastChar >= Namespace.MIN_API_MARK && lastChar <= Namespace.MAX_API_MARK) { name = name.substring(0, name.length() - 1); versionAdd = String.format("\\u%04x", (int) lastChar); } } - - - return constants.getNamespace(index).getKindStr() + "(" + (name == null ? "null" : "\"" + - Helper.escapePCodeString(name) + versionAdd + + return constants.getNamespace(index).getKindStr() + "(" + (name == null ? "null" : "\"" + + Helper.escapePCodeString(name) + versionAdd + "\"") + (sub > 0 ? ",\"" + sub + "\"" : "") + ")"; } @@ -331,6 +366,9 @@ public class Multiname { case MULTINAMELA: return getKindStr() + "(" + namespaceSetToString(constants, namespace_set_index) + ")"; case TYPENAME: + if (cyclic) { + return "CyclicTypeName"; + } String tret = getKindStr() + "("; tret += multinameToString(constants, qname_index, fullyQualifiedNames); tret += "<"; @@ -350,8 +388,8 @@ public class Multiname { } private String typeNameToStr(AVM2ConstantPool constants, List fullyQualifiedNames, boolean dontDeobfuscate, boolean withSuffix) { - if (constants.getMultiname(qname_index).name_index == name_index) { - return "ambiguousTypeName"; + if (cyclic) { + return "§§cyclic_typename()"; } StringBuilder typeNameStr = new StringBuilder(); typeNameStr.append(constants.getMultiname(qname_index).getName(constants, fullyQualifiedNames, dontDeobfuscate, withSuffix)); @@ -389,7 +427,7 @@ public class Multiname { if (nskind == Namespace.KIND_NAMESPACE || nskind == Namespace.KIND_PACKAGE_INTERNAL) { DottedChain dc = abc.findCustomNsOfMultiname(this); String nsname = dc != null ? dc.getLast() : null; - + if (nsname != null && !"AS3".equals(nsname)) { String identifier = dontDeobfuscate ? nsname : IdentifiersDeobfuscation.printIdentifier(true, nsname); if (identifier != null && !identifier.isEmpty()) { @@ -432,9 +470,9 @@ public class Multiname { if (cached != null) { return cached; } - + DottedChain nsName = getSimpleNamespaceName(constants); - if (nsName == null) { + if (nsName == null) { Namespace ns = getNamespace(constants); if (ns == null) { NamespaceSet nss = getNamespaceSet(constants); @@ -453,7 +491,7 @@ public class Multiname { if (nsName != null) { ret = nsName.add(name, withSuffix ? getNamespaceSuffix() : ""); } else { - ret = new DottedChain(new String[]{name}, new String[]{withSuffix ? getNamespaceSuffix() : ""}); + ret = new DottedChain(new String[]{name}, new String[]{withSuffix ? getNamespaceSuffix() : ""}); } constants.cacheMultinameWithNamespace(this, ret); return ret; @@ -466,19 +504,20 @@ public class Multiname { return constants.getNamespace(namespace_index); } } - + /** * Gets simple namespace name as dottedchain. Ignores swf api versioning. + * * @param constants - * @return + * @return */ - public DottedChain getSimpleNamespaceName(AVM2ConstantPool constants) { + public DottedChain getSimpleNamespaceName(AVM2ConstantPool constants) { if (hasOwnNamespace()) { if (namespace_index == 0 || namespace_index == -1) { return DottedChain.EMPTY; } return getNamespace(constants).getName(constants); - } + } if (hasOwnNamespaceSet()) { NamespaceSet nss = getNamespaceSet(constants); if (nss == null) { @@ -488,11 +527,12 @@ public class Multiname { } return null; } - + /** * Gets simplpe namespace kind. Ignores swf api versioning. + * * @param constants - * @return + * @return */ public int getSimpleNamespaceKind(AVM2ConstantPool constants) { if (hasOwnNamespace()) { @@ -500,7 +540,7 @@ public class Multiname { return 0; } return getNamespace(constants).kind; - } + } if (hasOwnNamespaceSet()) { NamespaceSet nss = getNamespaceSet(constants); if (nss == null) { @@ -510,7 +550,7 @@ public class Multiname { } return 0; } - + public List getApiVersions(AVM2ConstantPool constants) { if (hasOwnNamespace()) { return new ArrayList<>(); @@ -520,7 +560,7 @@ public class Multiname { } return new ArrayList<>(); } - + public boolean isApiVersioned(AVM2ConstantPool constants) { if (hasOwnNamespace()) { return false; @@ -656,4 +696,8 @@ public class Multiname { return true; } + + public boolean isCyclic() { + return cyclic; + } }