From f0f4467ed912019353747bbfd9b811dc438310aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 27 Jul 2025 16:26:43 +0200 Subject: [PATCH] Identifiers _SafeStr_XX, _SafeCls_XX, _SavePkg_XX can be used. --- .../flash/IdentifiersDeobfuscation.java | 96 +++++++++++++++---- .../com/jpexs/decompiler/flash/abc/ABC.java | 82 +++++++++++++--- .../flash/abc/avm2/AVM2Deobfuscation.java | 34 +++++-- .../locales/AdvancedSettingsDialog.properties | 2 +- 4 files changed, 175 insertions(+), 39 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java index 81afe300e..0a12a5a9b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java @@ -55,7 +55,19 @@ public class IdentifiersDeobfuscation { * Prefix to be put instead of obfuscated name. It will by suffixed with a * number. */ - public static final String SAFE_STR_PREFIX = "_SafeStr_"; + public static final String SAFE_STRING_PREFIX = "_SafeStr_"; + + /** + * Safe prefix for packages + */ + public static final String SAFE_PACKAGE_PREFIX = "_SafePkg_"; + + /** + * Safe prefix for classes + */ + public static final String SAFE_CLASS_PREFIX = "_SafeCls_"; + + /** * Random number generator. @@ -441,6 +453,13 @@ public class IdentifiersDeobfuscation { if (isReservedWord(s, as3)) { return false; } + + if (Configuration.autoDeobfuscateIdentifiers.get() + && (s.contains(SAFE_STRING_PREFIX) + || s.contains(SAFE_PACKAGE_PREFIX) + || s.contains(SAFE_CLASS_PREFIX))) { + return false; + } // simple fast test if (VALID_NAME_PATTERN_DOT.matcher(s).matches()) { @@ -462,6 +481,14 @@ public class IdentifiersDeobfuscation { * @return True if string is valid name */ public static boolean isValidName(boolean as3, String s, String... exceptions) { + + if (Configuration.autoDeobfuscateIdentifiers.get() + && (s.startsWith(SAFE_STRING_PREFIX) + || s.startsWith(SAFE_PACKAGE_PREFIX) + || s.startsWith(SAFE_CLASS_PREFIX))) { + return false; + } + for (String e : exceptions) { if (e.equals(s)) { return true; @@ -557,7 +584,7 @@ public class IdentifiersDeobfuscation { if (map.containsKey(s)) { writer.append(map.get(s)); } else { - String ret = IdentifiersDeobfuscation.SAFE_STR_PREFIX + map.size(); + String ret = IdentifiersDeobfuscation.SAFE_STRING_PREFIX + map.size(); map.put(s, ret); writer.append(ret); } @@ -597,6 +624,29 @@ public class IdentifiersDeobfuscation { used.add(s); return map.get(s); } + + if (s.startsWith(SAFE_STRING_PREFIX) + || s.startsWith(SAFE_PACKAGE_PREFIX) + || s.startsWith(SAFE_CLASS_PREFIX)) { + String foundKey = null; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().equals(s)) { + foundKey = entry.getKey(); + break; + } + } + if (foundKey == null) { + map.put(s, s); + return s; + } else { + if (foundKey.equals(s)) { + return s; + } + map.put(foundKey, SAFE_STRING_PREFIX + map.size()); + map.put(s, s); + return s; + } + } } if (s.startsWith("\u00A7") && s.endsWith("\u00A7")) { // Assuming already printed - TODO:detect better @@ -621,7 +671,7 @@ public class IdentifiersDeobfuscation { } if (Configuration.autoDeobfuscateIdentifiers.get()) { - String ret = IdentifiersDeobfuscation.SAFE_STR_PREFIX + map.size(); + String ret = IdentifiersDeobfuscation.SAFE_STRING_PREFIX + map.size(); map.put(s, ret); used.add(s); return ret; @@ -799,22 +849,32 @@ public class IdentifiersDeobfuscation { @SuppressWarnings("unchecked") public static GraphTextWriter writeCurrentScriptReplacements(GraphTextWriter writer, Set usedDeobfuscations, SWF swf) { - if (!usedDeobfuscations.isEmpty() && Configuration.autoDeobfuscateIdentifiers.get()) { - writer.newLine(); - List commentLines = new ArrayList<>(); - Map fullMap = swf.getObfuscatedIdentifiersMap(); - int i = 0; - for (String obfuscated : usedDeobfuscations) { - String deobfuscated = fullMap.get(obfuscated); - commentLines.add("@identifier " + deobfuscated + " = \"" + Helper.escapePCodeString(obfuscated) + "\""); - i++; - } - commentLines.sort(new NaturalOrderComparator()); - commentLines.add(0, AppResources.translate("decompilationWarning.obfuscatedIdentifiers")); - commentLines.add(1, AppResources.translate("decompilationWarning.replacementsFollow")); - String[] commentLinesArr = commentLines.toArray(new String[commentLines.size()]); - new DocCommentItem(commentLinesArr).appendTo(writer, LocalData.empty); + if (usedDeobfuscations.isEmpty()) { + return writer; } + if (!Configuration.autoDeobfuscateIdentifiers.get()) { + return writer; + } + + writer.newLine(); + List commentLines = new ArrayList<>(); + Map fullMap = swf.getObfuscatedIdentifiersMap(); + for (String obfuscated : usedDeobfuscations) { + String deobfuscated = fullMap.get(obfuscated); + if (obfuscated.equals(deobfuscated)) { + continue; + } + commentLines.add("@identifier " + deobfuscated + " = \"" + Helper.escapePCodeString(obfuscated) + "\""); + } + if (commentLines.isEmpty()) { + return writer; + } + commentLines.sort(new NaturalOrderComparator()); + commentLines.add(0, AppResources.translate("decompilationWarning.obfuscatedIdentifiers")); + commentLines.add(1, AppResources.translate("decompilationWarning.replacementsFollow")); + String[] commentLinesArr = commentLines.toArray(new String[commentLines.size()]); + new DocCommentItem(commentLinesArr).appendTo(writer, LocalData.empty); + return writer; } 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 668894eca..7b4b1c76c 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 @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.DeobfuscationListener; import com.jpexs.decompiler.flash.EndOfStreamException; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; +import static com.jpexs.decompiler.flash.IdentifiersDeobfuscation.SAFE_STRING_PREFIX; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; @@ -640,7 +641,7 @@ public class ABC implements Openable { } } - private void getObfuscatedIdentifier(int strIndex, Map ret) { + private void getObfuscatedIdentifier(int strIndex, Map map, String prefix) { if (strIndex >= constants.getStringCount()) { return; } @@ -648,7 +649,7 @@ public class ABC implements Openable { return; } String s = constants.getString(strIndex); - if (ret.containsKey(s)) { + if (map.containsKey(s)) { return; } AVM2Deobfuscation deobfuscation = getDeobfuscation(); @@ -656,10 +657,39 @@ public class ABC implements Openable { return; } - ret.put(s, IdentifiersDeobfuscation.SAFE_STR_PREFIX + ret.size()); + if (s.matches("^" + IdentifiersDeobfuscation.SAFE_STRING_PREFIX + "[0-9]+$") + || s.matches("^" + IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX + "[0-9]+$") + || s.matches("^" + IdentifiersDeobfuscation.SAFE_CLASS_PREFIX + "[0-9]+$")) { + String foundKey = null; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().equals(s)) { + foundKey = entry.getKey(); + break; + } + } + if (foundKey == null) { + map.put(s, s); + return; + } else { + if (foundKey.equals(s)) { + return; + } + String foundPrefix = SAFE_STRING_PREFIX; + if (s.startsWith(IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX)) { + foundPrefix = IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX; + } else if (s.startsWith(IdentifiersDeobfuscation.SAFE_CLASS_PREFIX)) { + foundPrefix = IdentifiersDeobfuscation.SAFE_CLASS_PREFIX; + } + map.put(foundKey, foundPrefix + map.size()); + map.put(s, s); + return; + } + } + + map.put(s, prefix + map.size()); } - private void getObfuscatedPackageIdentifier(int strIndex, Map ret) { + private void getObfuscatedPackageIdentifier(int strIndex, Map map) { if (strIndex >= constants.getStringCount()) { return; } @@ -667,7 +697,7 @@ public class ABC implements Openable { return; } String s = constants.getString(strIndex); - if (ret.containsKey(s)) { + if (map.containsKey(s)) { return; } AVM2Deobfuscation deobfuscation = getDeobfuscation(); @@ -677,30 +707,56 @@ public class ABC implements Openable { String[] parts = s.split("\\.", -1); List deobfuscatedList = new ArrayList<>(); for (String part : parts) { - if (!deobfuscation.isValidNSPart(part)) { - deobfuscatedList.add(IdentifiersDeobfuscation.SAFE_STR_PREFIX + ret.size()); - ret.put(part, IdentifiersDeobfuscation.SAFE_STR_PREFIX + ret.size()); + if (!deobfuscation.isValidNSPart(part)) { + if (part.matches("^" + IdentifiersDeobfuscation.SAFE_STRING_PREFIX + "[0-9]+$") + || part.matches("^" + IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX + "[0-9]+$") + || part.matches("^" + IdentifiersDeobfuscation.SAFE_CLASS_PREFIX + "[0-9]+$")) { + String foundKey = null; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().equals(part)) { + foundKey = entry.getKey(); + break; + } + } + if (foundKey == null) { + map.put(part, part); + } else { + if (!foundKey.equals(part)) { + String foundPrefix = SAFE_STRING_PREFIX; + if (part.startsWith(IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX)) { + foundPrefix = IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX; + } else if (part.startsWith(IdentifiersDeobfuscation.SAFE_CLASS_PREFIX)) { + foundPrefix = IdentifiersDeobfuscation.SAFE_CLASS_PREFIX; + } + map.put(foundKey, foundPrefix + map.size()); + map.put(part, part); + } + } + } else { + deobfuscatedList.add(IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX + map.size()); + map.put(part, IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX + map.size()); + } } else { deobfuscatedList.add(part); } } - ret.put(s, String.join(".", deobfuscatedList)); + map.put(s, String.join(".", deobfuscatedList)); } public void getObfuscatedIdentifiers(Map ret) { for (int i = 0; i < instance_info.size(); i++) { InstanceInfo insti = instance_info.get(i); if (insti.name_index != 0) { - getObfuscatedIdentifier(constants.getMultiname(insti.name_index).name_index, ret); + getObfuscatedIdentifier(constants.getMultiname(insti.name_index).name_index, ret, IdentifiersDeobfuscation.SAFE_CLASS_PREFIX); if (constants.getMultiname(insti.name_index).namespace_index != 0) { getObfuscatedPackageIdentifier(constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, ret); } } if (insti.super_index != 0) { - getObfuscatedIdentifier(constants.getMultiname(insti.super_index).name_index, ret); + getObfuscatedIdentifier(constants.getMultiname(insti.super_index).name_index, ret, IdentifiersDeobfuscation.SAFE_CLASS_PREFIX); } for (int iface : insti.interfaces) { - getObfuscatedIdentifier(constants.getMultiname(iface).name_index, ret); + getObfuscatedIdentifier(constants.getMultiname(iface).name_index, ret, IdentifiersDeobfuscation.SAFE_CLASS_PREFIX ); } } @@ -710,7 +766,7 @@ public class ABC implements Openable { if (m.kind == Multiname.MULTINAME && strIndex > 0 && "*".equals(constants.getString(strIndex))) { continue; } - getObfuscatedIdentifier(constants.getMultiname(i).name_index, ret); + getObfuscatedIdentifier(constants.getMultiname(i).name_index, ret, IdentifiersDeobfuscation.SAFE_STRING_PREFIX); } for (int i = 1; i < constants.getNamespaceCount(); i++) { if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { // only packages diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java index 27b56823d..38dd7da05 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java @@ -17,8 +17,12 @@ package com.jpexs.decompiler.flash.abc.avm2; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; +import static com.jpexs.decompiler.flash.IdentifiersDeobfuscation.SAFE_CLASS_PREFIX; +import static com.jpexs.decompiler.flash.IdentifiersDeobfuscation.SAFE_PACKAGE_PREFIX; +import static com.jpexs.decompiler.flash.IdentifiersDeobfuscation.SAFE_STRING_PREFIX; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -33,8 +37,8 @@ import java.util.regex.Pattern; * * @author JPEXS */ -public class AVM2Deobfuscation { - +public class AVM2Deobfuscation { + /** * Default size of random word. */ @@ -103,6 +107,13 @@ public class AVM2Deobfuscation { isValid = false; } + if (Configuration.autoDeobfuscateIdentifiers.get() + && (s.contains(SAFE_STRING_PREFIX) + || s.contains(SAFE_PACKAGE_PREFIX) + || s.contains(SAFE_CLASS_PREFIX))) { + return false; + } + if (isValid) { for (int i = 0; i < s.length(); i++) { if (s.charAt(i) > 127) { @@ -179,9 +190,9 @@ public class AVM2Deobfuscation { return ret; } - /** * Checks whether string at given index is valid package name + * * @param strIndex String index * @return True if valid */ @@ -195,7 +206,7 @@ public class AVM2Deobfuscation { } return isValidNSPart(s); } - + /** * Deobfuscates package name. * @@ -206,7 +217,7 @@ public class AVM2Deobfuscation { * @param renameType Rename type * @return Deobfuscated package name */ - public int deobfuscatePackageName(Map stringUsageTypes, Set stringUsages, HashMap namesMap, int strIndex, RenameType renameType) { + public int deobfuscatePackageName(Map stringUsageTypes, Set stringUsages, HashMap namesMap, int strIndex, RenameType renameType) { if (!isValidPackageName(strIndex)) { String s = constants.getString(strIndex); DottedChain sChain = DottedChain.parseWithSuffix(s); @@ -239,6 +250,7 @@ public class AVM2Deobfuscation { /** * Checks whether string at given index is valid name + * * @param strIndex String index * @return True when valid */ @@ -247,6 +259,14 @@ public class AVM2Deobfuscation { return true; } String s = constants.getString(strIndex); + + if (Configuration.autoDeobfuscateIdentifiers.get() + && (s.startsWith(SAFE_STRING_PREFIX) + || s.startsWith(SAFE_PACKAGE_PREFIX) + || s.startsWith(SAFE_CLASS_PREFIX))) { + return false; + } + boolean isValid = true; if (IdentifiersDeobfuscation.isReservedWord2(s)) { isValid = false; @@ -269,7 +289,7 @@ public class AVM2Deobfuscation { } return isValid; } - + /** * Deobfuscates name. * @@ -282,7 +302,7 @@ public class AVM2Deobfuscation { * @param renameType Rename type * @return Deobfuscated name string index */ - public int deobfuscateName(Map stringUsageTypes, Set stringUsages, Set namespaceUsages, HashMap namesMap, int strIndex, boolean firstUppercase, RenameType renameType) { + public int deobfuscateName(Map stringUsageTypes, Set stringUsages, Set namespaceUsages, HashMap namesMap, int strIndex, boolean firstUppercase, RenameType renameType) { if (!isValidName(strIndex)) { String s = constants.getString(strIndex); DottedChain newname; diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 7c0cadfc6..2b890173b 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -635,4 +635,4 @@ config.name.showHeapStatusWidget = Show heap status widget config.description.showHeapStatusWidget = Displays used memory widget in the titlebar. config.name.autoDeobfuscateIdentifiers = Deobfuscate identifiers -config.description.autoDeobfuscateIdentifiers = Obfuscated names that are not valid identifiers will be printed as _SafeStr_XX. +config.description.autoDeobfuscateIdentifiers = Obfuscated names that are not valid identifiers will be printed as _SafeStr_XX, _SafeCls_XX or _SafePkg_XX.