Identifiers _SafeStr_XX, _SafeCls_XX, _SavePkg_XX can be used.

This commit is contained in:
Jindra Petřík
2025-07-27 16:26:43 +02:00
parent ad53e53a5d
commit f0f4467ed9
4 changed files with 175 additions and 39 deletions

View File

@@ -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<String, String> 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<String> usedDeobfuscations, SWF swf) {
if (!usedDeobfuscations.isEmpty() && Configuration.autoDeobfuscateIdentifiers.get()) {
writer.newLine();
List<String> commentLines = new ArrayList<>();
Map<String, String> 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<String> commentLines = new ArrayList<>();
Map<String, String> 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;
}

View File

@@ -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<String, String> ret) {
private void getObfuscatedIdentifier(int strIndex, Map<String, String> 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<String, String> 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<String, String> ret) {
private void getObfuscatedPackageIdentifier(int strIndex, Map<String, String> 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<String> 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<String, String> 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<String, String> 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

View File

@@ -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<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, RenameType renameType) {
public int deobfuscatePackageName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<DottedChain, DottedChain> 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<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) {
public int deobfuscateName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) {
if (!isValidName(strIndex)) {
String s = constants.getString(strIndex);
DottedChain newname;