Added: AS2 - show deobfuscated class/package names in the class tree

This commit is contained in:
Jindra Petřík
2025-07-01 21:36:46 +02:00
parent 5ffc71848b
commit cab9e853b8
16 changed files with 170 additions and 16 deletions
@@ -622,6 +622,63 @@ public class IdentifiersDeobfuscation {
return writer;
}
/**
* Unescapes deobfuscated identifier
* @param s String
* @return Unescaped string
*/
public static String unescapeOIdentifier(String s) {
StringBuilder ret = new StringBuilder(s.length());
if (s.length() < 2) {
return s;
}
if (!(s.startsWith("\u00A7") && s.endsWith("\u00A7"))) {
return s;
}
for (int i = 1; i < s.length() - 1; i++) {
char c = s.charAt(i);
if (c == '\\') {
if (i + 1 < s.length() - 1) {
i++;
c = s.charAt(i);
if (c == 'n') {
ret.append("\n");
} else if (c == 'r') {
ret.append("\r");
} else if (c == 't') {
ret.append("\t");
} else if (c == 'b') {
ret.append("\b");
} else if (c == 'f') {
ret.append("\f");
} else if (c == '\\') {
ret.append("\\");
} else if (c == '\u00A7') {
ret.append("\u00A7");
} else if (c == 'x' && i + 2 < s.length() - 1) {
ret.append((char) Integer.parseInt(s.substring(i + 1, i + 3), 16));
i += 2;
} else if (c == '{') {
int endPos = s.indexOf("}", i);
if (endPos != -1) {
int numRepeat = Integer.parseInt(s.substring(i + 1, endPos));
i = endPos + 1;
c = s.charAt(i);
for (int j = 0; j < numRepeat; j++) {
ret.append(c);
}
}
}
}
} else {
ret.append(c);
}
}
return ret.toString();
}
/**
* Escapes obfuscated identifier.
*
@@ -3349,7 +3349,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
if (treeItem instanceof AS2Package) {
AS2Package pkg = (AS2Package) treeItem;
if (pkg.isFlat()) {
String[] parts = pkg.toString().split("\\.");
String[] parts = pkg.getName().split("\\.");
for (int i = 0; i < parts.length; i++) {
parts[i] = Helper.makeFileName(parts[i]);
}
@@ -21,10 +21,12 @@ import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.DottedIdentifier;
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.annotations.Table;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.util.ArrayList;
@@ -56,6 +58,7 @@ public class ExportAssetsTag extends SymbolClassTypeTag {
@SWFArray(value = "name", countField = "count")
@Table(value = "assets", itemName = "asset")
@DottedIdentifier
public List<String> names;
/**
@@ -161,7 +164,7 @@ public class ExportAssetsTag extends SymbolClassTypeTag {
Map<String, String> ret = super.getNameProperties();
if (names.size() == 1) {
ret.put("chid", "" + tags.get(0));
ret.put("ex", "" + names.get(0));
ret.put("ex", "" + DottedChain.parseNoSuffix(names.get(0)).toPrintableString(false));
}
return ret;
}
@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFArray;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.annotations.Table;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import java.io.IOException;
@@ -161,7 +162,7 @@ public class ImportAssets2Tag extends Tag implements ImportTag {
Map<String, String> ret = super.getNameProperties();
if (names.size() == 1) {
ret.put("chid", "" + tags.get(0));
ret.put("im", "" + names.get(0));
ret.put("im", "" + DottedChain.parseNoSuffix(names.get(0)).toPrintableString(false));
}
return ret;
}
@@ -25,6 +25,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFArray;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.annotations.Table;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.util.ArrayList;
@@ -137,7 +138,7 @@ public class ImportAssetsTag extends Tag implements ImportTag {
Map<String, String> ret = super.getNameProperties();
if (names.size() == 1) {
ret.put("chid", "" + tags.get(0));
ret.put("im", "" + names.get(0));
ret.put("im", "" + DottedChain.parseNoSuffix(names.get(0)).toPrintableString(false));
}
return ret;
}
@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.DottedIdentifier;
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
@@ -53,6 +54,7 @@ public class SymbolClassTag extends SymbolClassTypeTag {
@SWFArray(value = "name", countField = "numSymbols")
@Table(value = "symbols", itemName = "symbol")
@DottedIdentifier(as3 = true)
public List<String> names;
@Override
@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.tags.DefineScalingGridTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
@@ -98,10 +99,14 @@ public abstract class CharacterTag extends Tag implements CharacterIdTag {
ret.put("chid", "" + chid);
}
if (exportName != null) {
ret.put("exp", Helper.escapePCodeString(exportName));
ret.put("exp", DottedChain.parseNoSuffix(exportName).toPrintableString(false));
}
if (!classNames.isEmpty()) {
ret.put("cls", Helper.joinEscapePCodeString(", ", classNames));
List<String> escapedList = new ArrayList<>();
for (String className : classNames) {
escapedList.add(DottedChain.parseNoSuffix(className).toPrintableString(true));
}
ret.put("cls", String.join(", ", escapedList));
}
return ret;
}
@@ -16,10 +16,12 @@
*/
package com.jpexs.decompiler.flash.timeline;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import com.jpexs.decompiler.graph.DottedChain;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -187,7 +189,10 @@ public class AS2Package implements TreeItem {
@Override
public String toString() {
return name;
if (defaultPackage) {
return name;
}
return DottedChain.parseNoSuffix(name).toPrintableString(false);
}
/**
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2010-2025 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.types.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Field is dotted identifier.
*
* @author JPEXS
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DottedIdentifier {
boolean as3() default false;
}
@@ -149,7 +149,31 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return ret;
}
}
/**
* Parses a dotted chain from a deobfuscated string.
*
* @param name Name
* @return Dotted chain
*/
public static final DottedChain parsePrintable(String name) {
if (name == null) {
return DottedChain.EMPTY;
} else if (name.isEmpty()) {
return DottedChain.TOPLEVEL;
} else {
String[] parts = name.split("\\.");
List<PathPart> newParts = new ArrayList<>();
for (String part : parts) {
newParts.add(new PathPart(IdentifiersDeobfuscation.unescapeOIdentifier(part), false, ""));
}
DottedChain ret = new DottedChain();
ret.parts = newParts;
return ret;
}
}
/**
* Constructs a new dotted chain.
*