More documentation.

This commit is contained in:
Jindra Petřík
2024-08-08 19:27:14 +02:00
parent 68954e714d
commit b57e38e387
286 changed files with 11752 additions and 3576 deletions
@@ -17,22 +17,46 @@
package com.jpexs.decompiler.flash;
/**
*
* A handler for abort, retry, ignore, ignore all dialog
* @author JPEXS
*/
public interface AbortRetryIgnoreHandler {
/**
* Undefined result
*/
public static int UNDEFINED = -1;
/**
* Abort result
*/
public static int ABORT = 0;
/**
* Retry result
*/
public static int RETRY = 1;
/**
* Ignore result
*/
public static int IGNORE = 2;
/**
* Ignore all result
*/
public static int IGNORE_ALL = 3;
/**
* Handles the thrown exception
* @param thrown The thrown exception
* @return The result
*/
public int handle(Throwable thrown);
/**
* Returns a new instance of this handler
* @return A new instance of this handler
*/
public AbortRetryIgnoreHandler getNewInstance();
}
@@ -19,17 +19,31 @@ package com.jpexs.decompiler.flash;
import java.util.ResourceBundle;
/**
*
* Application resources
* @author JPEXS
*/
public class AppResources {
/**
* Resource bundle
*/
private static final ResourceBundle resourceBundle = ResourceBundle.getBundle("com.jpexs.decompiler.flash.locales.AppResources");
/**
* Translates a key
* @param key The key
* @return The translated key
*/
public static String translate(String key) {
return resourceBundle.getString(key);
}
/**
* Translates a key from a bundle
* @param bundle The bundle
* @param key The key
* @return The translated key
*/
public static String translate(String bundle, String key) {
ResourceBundle b = ResourceBundle.getBundle(bundle);
return b.getString(key);
@@ -20,43 +20,94 @@ import java.io.IOException;
import java.util.Properties;
/**
*
* Application information
* @author JPEXS
*/
public class ApplicationInfo {
/**
* Application name
*/
public static final String APPLICATION_NAME = "JPEXS Free Flash Decompiler";
/**
* CLI application name
*/
public static final String CLI_APPLICATION_NAME = "@|blue JPEXS|@ @|green Free|@ @|red Flash Decompiler|@";
/**
* Short application name
*/
public static final String SHORT_APPLICATION_NAME = "FFDec";
/**
* Vendor
*/
public static final String VENDOR = "JPEXS";
/**
* Library version
*/
public static String libraryVersion = "";
/**
* Version
*/
public static String version = "";
/**
* Revision
*/
public static String revision = "";
/**
* Version major
*/
public static int version_major = 4;
/**
* Version minor
*/
public static int version_minor = 0;
/**
* Version release
*/
public static int version_release = 0;
/**
* Version build
*/
public static int version_build = 0;
/**
* Nightly
*/
public static boolean nightly = false;
/**
* CLI application version name
*/
public static String cliApplicationVerName;
/**
* Application version name
*/
public static String applicationVerName;
/**
* Short application version name
*/
public static String shortApplicationVerName;
/**
* Git hub project
*/
public static final String GIT_HUB_PROJECT = "jindrapetrik/jpexs-decompiler";
/**
* Project page
*/
public static final String PROJECT_PAGE = "https://github.com/" + GIT_HUB_PROJECT;
/**
@@ -79,6 +130,9 @@ public class ApplicationInfo {
loadLibraryVersion();
}
/**
* Loads library version
*/
private static void loadLibraryVersion() {
Properties prop = new Properties();
try {
@@ -97,6 +151,9 @@ public class ApplicationInfo {
}
}
/**
* Loads properties.
*/
private static void loadProperties() {
Properties prop = new Properties();
try {
@@ -19,18 +19,28 @@ package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.SecondPassData;
import java.util.HashSet;
import java.util.Set;
/**
*
* Base local data
* @author JPEXS
*/
public abstract class BaseLocalData {
/**
* Line start instruction
*/
public GraphSourceItem lineStartInstruction;
/**
* Set of all switch parts
*/
public Set<GraphPart> allSwitchParts = new HashSet<>();
/**
* Second pass data
*/
public SecondPassData secondPassData = null;
}
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash;
import com.jpexs.helpers.SwfHeaderStreamSearch;
import com.jpexs.helpers.streams.SeekableInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
@@ -26,23 +27,41 @@ import java.util.Map;
import java.util.Set;
/**
*
* Binary search SWF bundle
* @author JPEXS
*/
public class BinarySWFBundle implements Bundle {
/**
* SWF search
*/
private final SWFSearch search;
/**
* Constructs a new BinarySWFBundle.
* @param is Input stream
* @param noCheck Do not check
* @param searchMode Search mode
* @throws IOException
*/
public BinarySWFBundle(InputStream is, boolean noCheck, SearchMode searchMode) throws IOException {
search = new SWFSearch(new SwfHeaderStreamSearch(is), noCheck, searchMode);
search.process();
}
/**
* Gets size of the search.
* @return Size of the search
*/
@Override
public int length() {
return search.length();
}
/**
* Gets keys.
* @return Keys
*/
@Override
public Set<String> getKeys() {
Set<String> ret = new LinkedHashSet<>();
@@ -52,6 +71,11 @@ public class BinarySWFBundle implements Bundle {
return ret;
}
/**
* Gets openable.
* @param key Key
* @return Openable
*/
@Override
public SeekableInputStream getOpenable(String key) {
if (!key.startsWith("[")) {
@@ -69,6 +93,10 @@ public class BinarySWFBundle implements Bundle {
}
}
/**
* Gets all.
* @return Map of string to seekable input stream
*/
@Override
public Map<String, SeekableInputStream> getAll() {
Map<String, SeekableInputStream> ret = new LinkedHashMap<>();
@@ -78,16 +106,30 @@ public class BinarySWFBundle implements Bundle {
return ret;
}
/**
* Gets extension.
* @return Extension
*/
@Override
public String getExtension() {
return "bin";
}
/**
* Checks if read only.
* @return True if read only, false otherwise
*/
@Override
public boolean isReadOnly() {
return true;
}
/**
* Replaces openable.
* @param key Key
* @param is Input stream
* @return True if successful, false otherwise
*/
@Override
public boolean putOpenable(String key, InputStream is) {
throw new UnsupportedOperationException("Save not supported for this type of bundle");
@@ -17,28 +17,64 @@
package com.jpexs.decompiler.flash;
import com.jpexs.helpers.streams.SeekableInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;
/**
*
* Bundle interface.
* A bundle is a collection of openable files. (e.g. SWC, ZIP, etc.)
* @author JPEXS
*/
public interface Bundle {
/**
* Gets number of openable files in the bundle.
* @return Number of openable files in the bundle
*/
public int length();
/**
* Gets keys of openable files in the bundle.
* @return Keys of openable files in the bundle
*/
public Set<String> getKeys();
/**
* Gets openable file by key.
* @param key Key
* @return Openable file
* @throws IOException
*/
public SeekableInputStream getOpenable(String key) throws IOException;
/**
* Gets all openable files in the bundle.
* @return Map from key to seekable input stream
* @throws IOException
*/
public Map<String, SeekableInputStream> getAll() throws IOException;
/**
* Gets extension of the bundle. (without dot)
* @return Extension of the bundle
*/
public String getExtension();
/**
* Checks if the bundle is read-only.
* @return True if the bundle is read-only, false otherwise
*/
public boolean isReadOnly();
/**
* Replace openable file by key.
* @param key Key
* @param is New input stream
* @return True if the file was replaced, false otherwise
* @throws IOException
*/
public boolean putOpenable(String key, InputStream is) throws IOException;
}
@@ -30,29 +30,34 @@ import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.helpers.ImmediateFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* Decompiler thread pool.
* @author JPEXS
*/
public class DecompilerPool {
/**
* Executor
*/
private final ThreadPoolExecutor executor;
/**
* Openable to futures map
*/
private Map<Openable, List<Future<HighlightedText>>> openableToFutures = new WeakHashMap<>();
/**
* Constructs a new DecompilerPool.
*/
public DecompilerPool() {
int threadCount = Configuration.getParallelThreadCount();
executor = new ThreadPoolExecutor(threadCount, threadCount,
@@ -60,6 +65,13 @@ public class DecompilerPool {
new LinkedBlockingQueue<>());
}
/**
* Submits a task.
* @param src Source
* @param actions Actions
* @param listener Listener
* @return Future
*/
public Future<HighlightedText> submitTask(ASMSource src, ActionList actions, ScriptDecompiledListener<HighlightedText> listener) {
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
@@ -92,6 +104,13 @@ public class DecompilerPool {
return submit(callable);
}
/**
* Submits a task.
* @param abcIndex ABC index
* @param pack Script pack
* @param listener Listener
* @return Future
*/
public Future<HighlightedText> submitTask(AbcIndexing abcIndex, ScriptPack pack, ScriptDecompiledListener<HighlightedText> listener) {
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
@@ -128,6 +147,11 @@ public class DecompilerPool {
return submit(callable);
}
/**
* Submits a task.
* @param callable Callable
* @return Future
*/
private Future<HighlightedText> submit(Callable<HighlightedText> callable) {
boolean parallel = Configuration.parallelSpeedUp.get();
if (parallel) {
@@ -149,6 +173,10 @@ public class DecompilerPool {
}
}
/**
* Gets statistics.
* @return Statistics
*/
public String getStat() {
return "core: " + executor.getCorePoolSize()
+ " size: " + executor.getPoolSize()
@@ -159,6 +187,13 @@ public class DecompilerPool {
+ " completed: " + executor.getCompletedTaskCount();
}
/**
* Decompiles ASM source.
* @param src ASM source
* @param actions Actions
* @return Highlighted text
* @throws InterruptedException
*/
public HighlightedText decompile(ASMSource src, ActionList actions) throws InterruptedException {
Future<HighlightedText> future = submitTask(src, actions, null);
SWF swf = src.getSwf();
@@ -183,6 +218,13 @@ public class DecompilerPool {
return null;
}
/**
* Decompiles a script pack.
* @param abcIndex ABC indexing
* @param pack Script pack
* @return Highlighted text
* @throws InterruptedException
*/
public HighlightedText decompile(AbcIndexing abcIndex, ScriptPack pack) throws InterruptedException {
Future<HighlightedText> future = submitTask(abcIndex, pack, null);
@@ -208,11 +250,19 @@ public class DecompilerPool {
return null;
}
/**
* Shuts down the pool.
* @throws InterruptedException
*/
public void shutdown() throws InterruptedException {
executor.shutdown();
executor.awaitTermination(100, TimeUnit.SECONDS);
}
/**
* Destroys a SWF.
* @param swf SWF
*/
public void destroySwf(SWF swf) {
List<Future<HighlightedText>> futures = openableToFutures.get(swf);
if (futures != null) {
@@ -17,10 +17,13 @@
package com.jpexs.decompiler.flash;
/**
*
* Listener for deobfuscation events.
* @author JPEXS
*/
public interface DeobfuscationListener {
/**
* Called when an item is deobfuscated.
*/
public void itemDeobfuscated();
}
@@ -17,14 +17,29 @@
package com.jpexs.decompiler.flash;
/**
*
* Listener for disassembly events.
* @author JPEXS
*/
public interface DisassemblyListener {
/**
* Called on progress of reading.
* @param pos Position
* @param total Total length
*/
public void progressReading(long pos, long total);
/**
* Called on progress of deobfuscating.
* @param pos Position
* @param total Total length
*/
public void progressToString(long pos, long total);
/**
* Called on progress of deobfuscating.
* @param pos Position
* @param total Total length
*/
public void progressDeobfuscating(long pos, long total);
}
@@ -19,11 +19,14 @@ package com.jpexs.decompiler.flash;
import java.io.IOException;
/**
*
* Exception thrown when end of stream is reached.
* @author JPEXS
*/
public class EndOfStreamException extends IOException {
/**
* Constructs a new EndOfStreamException.
*/
public EndOfStreamException() {
super("Premature end of the stream reached");
}
@@ -17,14 +17,33 @@
package com.jpexs.decompiler.flash;
/**
*
* Interface for listening to events.
* @author JPEXS
*/
public interface EventListener {
/**
* Handles exporting event.
* @param type Event type
* @param index Index of the exported item
* @param count Total count of exported items
* @param data Data
*/
public void handleExportingEvent(String type, int index, int count, Object data);
/**
* Handles exported event.
* @param type Event type
* @param index Index of the exported item
* @param count Total count of exported items
* @param data Data
*/
public void handleExportedEvent(String type, int index, int count, Object data);
/**
* Handles event.
* @param event Event name
* @param data Data
*/
public void handleEvent(String event, Object data);
}
@@ -17,32 +17,49 @@
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.graph.Loop;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
* Final decompilation processing local d ata.
* @author JPEXS
*/
public class FinalProcessLocalData {
/**
* Temporary registers used in the code.
*/
public final HashSet<Integer> temporaryRegisters;
/**
* Loops in the code.
*/
public final List<Loop> loops;
/**
* Register usage. Map of setLocal ip to set of getLocal ips.
*/
public Map<Integer, Set<Integer>> registerUsage;
public Set<Integer> getRegisterUsage(int regIndex) {
/**
* Returns register usage for given setLocal ip.
* @param setLocalIp SetLocal ip
* @return Set of getLocal ips
*/
public Set<Integer> getRegisterUsage(int setLocalIp) {
if (registerUsage == null) {
return new HashSet<>();
}
if (!registerUsage.containsKey(regIndex)) {
if (!registerUsage.containsKey(setLocalIp)) {
return new HashSet<>();
}
return registerUsage.get(regIndex);
return registerUsage.get(setLocalIp);
}
/**
* Constructs new FinalProcessLocalData.
* @param loops Loops in the code
*/
public FinalProcessLocalData(List<Loop> loops) {
temporaryRegisters = new HashSet<>();
registerUsage = new HashMap<>();
@@ -25,49 +25,90 @@ import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.Cache;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.*;
import java.util.regex.Pattern;
/**
*
* Identifiers deobfuscation.
* @author JPEXS
*/
public class IdentifiersDeobfuscation {
/**
* Random number generator.
*/
private static final Random rnd = new Random();
/**
* Default size of random string.
*/
private final int DEFAULT_FOO_SIZE = 10;
/**
* All variable names.
*/
public HashSet<String> allVariableNamesStr = new HashSet<>();
/**
* Type counts.
*/
private final HashMap<String, Integer> typeCounts = new HashMap<>();
/**
* AS2 name cache.
*/
private static final Cache<String, String> as2NameCache = Cache.getInstance(false, true, "as2_ident", true);
/**
* AS3 name cache.
*/
private static final Cache<String, String> as3NameCache = Cache.getInstance(false, true, "as3_ident", true);
/**
* Valid first characters.
*/
public static final String VALID_FIRST_CHARACTERS = "\\p{Lu}\\p{Ll}\\p{Lt}\\p{Lm}\\p{Lo}_$";
/**
* Valid next characters.
*/
public static final String VALID_NEXT_CHARACTERS = VALID_FIRST_CHARACTERS + "\\p{Nl}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}";
/**
* Valid name pattern.
*/
private static final Pattern VALID_NAME_PATTERN = Pattern.compile("^[a-zA-Z_\\$][a-zA-Z0-9_\\$]*$");
/**
* Identifier pattern.
*/
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("^[" + VALID_FIRST_CHARACTERS + "][" + VALID_NEXT_CHARACTERS + "]*$");
/**
* Valid name pattern with dot.
*/
private static final Pattern VALID_NAME_PATTERN_DOT = Pattern.compile("^[a-zA-Z_\\$][a-zA-Z0-9_.\\$]*$");
/**
* Identifier pattern with dot.
*/
private static final Pattern IDENTIFIER_PATTERN_DOT = Pattern.compile("^[" + VALID_FIRST_CHARACTERS + "][" + VALID_NEXT_CHARACTERS + ".]*$");
/**
* Random name generator characters.
*/
public static final String FOO_CHARACTERS = "bcdfghjklmnpqrstvwz";
/**
* Random name generator join characters.
*/
public static final String FOO_JOIN_CHARACTERS = "aeiouy";
// http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00000477.html
/**
* Reserved words in AS2.
* http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00000477.html
*/
public static final String[] reservedWordsAS2 = {
// is "add" really a keyword? documentation says yes, but I can create "add" variable in CS6...
// "add",
@@ -99,7 +140,10 @@ public class IdentifiersDeobfuscation {
"typeof", "undefined", "var", "void", "while", "with"
};
// http://www.adobe.com/devnet/actionscript/learning/as3-fundamentals/syntax.html
/**
* Reserved words in AS3.
* http://www.adobe.com/devnet/actionscript/learning/as3-fundamentals/syntax.html
*/
public static final String[] reservedWordsAS3 = {
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "else",
"extends", "false", "finally", "for", "function", "if", "implements", "import", "in", "instanceof",
@@ -111,7 +155,9 @@ public class IdentifiersDeobfuscation {
"void", "while", "with"
};
// TODO, why do we have 2 different list? Moved from AVM2Deobfuscation
/**
* TODO, why do we have 2 different list? Moved from AVM2Deobfuscation
*/
public static final String[] reservedWordsAS3_2 = {
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else",
"extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof",
@@ -119,9 +165,16 @@ public class IdentifiersDeobfuscation {
"return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
"with", "dynamic", "default", "final", "in", "static"};
//syntactic keywords - can be used as identifiers, but that have special meaning in certain contexts
/**
* Syntactic keywords - can be used as identifiers, but that have special meaning in certain contexts
*/
public static final String[] syntacticKeywordsAS3 = {"each", "get", "set", "namespace", "include", "dynamic", "final", "native", "override", "static"};
/**
* Checks if string is reserved word.
* @param s String
* @param as3 Is ActionScript3
*/
public static boolean isReservedWord(String s, boolean as3) {
if (s == null) {
return false;
@@ -136,7 +189,11 @@ public class IdentifiersDeobfuscation {
return false;
}
// TODO: Why do we need this method???
/**
* TODO: Why do we need this method???
* @param s String
* @return True if string is reserved word
*/
public static boolean isReservedWord2(String s) {
if (s == null) {
return false;
@@ -151,6 +208,12 @@ public class IdentifiersDeobfuscation {
return false;
}
/**
* Generates random string.
* @param firstUppercase First character uppercase
* @param rndSize Random size
* @return Random string
*/
public static String fooString(boolean firstUppercase, int rndSize) {
int len = 3 + rnd.nextInt(rndSize - 3);
StringBuilder sb = new StringBuilder(len);
@@ -170,6 +233,14 @@ public class IdentifiersDeobfuscation {
return sb.toString();
}
/**
* Deobfuscates instance names.
* @param as3 Is ActionScript3
* @param namesMap Names map
* @param renameType Rename type
* @param tags Tags
* @param selected Preselected names
*/
public void deobfuscateInstanceNames(boolean as3, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Iterable<Tag> tags, Map<DottedChain, DottedChain> selected) {
for (Tag t : tags) {
if (t instanceof DefineSpriteTag) {
@@ -197,6 +268,15 @@ public class IdentifiersDeobfuscation {
}
}
/**
* Deobfuscates package names.
* @param as3 Is ActionScript3
* @param pkg Package
* @param namesMap Names map
* @param renameType Rename type
* @param selected Preselected names
* @return Deobfuscated package
*/
public DottedChain deobfuscatePackage(boolean as3, DottedChain pkg, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Map<DottedChain, DottedChain> selected) {
if (namesMap.containsKey(pkg)) {
return namesMap.get(pkg);
@@ -221,6 +301,15 @@ public class IdentifiersDeobfuscation {
return null;
}
/**
* Deobfuscates name with package.
* @param as3 Is ActionScript3
* @param n Name
* @param namesMap Names map
* @param renameType Rename type
* @param selected Preselected names
* @return Deobfuscated name
*/
public String deobfuscateNameWithPackage(boolean as3, String n, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Map<DottedChain, DottedChain> selected) {
DottedChain nChain = DottedChain.parseNoSuffix(n);
DottedChain pkg = nChain.getWithoutLast();
@@ -251,6 +340,12 @@ public class IdentifiersDeobfuscation {
return null;
}
/**
* Checks if string is valid slash path.
* @param s String
* @param exceptions Exceptions
* @return True if string is valid slash path
*/
private static boolean isValidSlashPath(String s, String... exceptions) {
String[] slashParts = s.split("\\/");
if (s.isEmpty()) {
@@ -274,6 +369,12 @@ public class IdentifiersDeobfuscation {
return true;
}
/**
* Checks if string is valid name with slash.
* @param s String
* @param exceptions Exceptions
* @return True if string is valid name with slash
*/
public static boolean isValidNameWithSlash(String s, String... exceptions) {
if (s.contains(":")) {
String[] pathVar = s.split(":");
@@ -291,6 +392,13 @@ public class IdentifiersDeobfuscation {
}
}
/**
* Checks if string is valid name with dot.
* @param as3 Is ActionScript3
* @param s String
* @param exceptions Exceptions
* @return True if string is valid name with dot
*/
public static boolean isValidNameWithDot(boolean as3, String s, String... exceptions) {
for (String e : exceptions) {
if (e.equals(s)) {
@@ -313,6 +421,13 @@ public class IdentifiersDeobfuscation {
return false;
}
/**
* Checks if string is valid name.
* @param as3 Is ActionScript3
* @param s String
* @param exceptions Exceptions
* @return True if string is valid name
*/
public static boolean isValidName(boolean as3, String s, String... exceptions) {
for (String e : exceptions) {
if (e.equals(s)) {
@@ -335,6 +450,17 @@ public class IdentifiersDeobfuscation {
return false;
}
/**
* Deobfuscates name.
* @param as3 Is ActionScript3
* @param s String
* @param firstUppercase First character uppercase
* @param usageType Usage type
* @param namesMap Names map
* @param renameType Rename type
* @param selected Preselected names
* @return Deobfuscated name
*/
public String deobfuscateName(boolean as3, String s, boolean firstUppercase, String usageType, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Map<DottedChain, DottedChain> selected) {
if (usageType == null) {
usageType = "name";
@@ -380,6 +506,12 @@ public class IdentifiersDeobfuscation {
return null;
}
/**
* Appends obfuscated identifier.
* @param s String
* @param writer Writer
* @return Writer
*/
public static GraphTextWriter appendObfuscatedIdentifier(String s, GraphTextWriter writer) {
writer.append("\u00A7");
escapeOIdentifier(s, writer);
@@ -387,7 +519,7 @@ public class IdentifiersDeobfuscation {
}
/**
* Ensures identifier is valid and if not, uses paragraph syntax
* Ensures identifier is valid and if not, uses paragraph syntax.
*
* @param as3 Is ActionScript3
* @param s Identifier
@@ -426,6 +558,12 @@ public class IdentifiersDeobfuscation {
return ret;
}
/**
* Escapes obfuscated identifier.
* @param s String
* @param writer Writer
* @return Writer
*/
public static GraphTextWriter escapeOIdentifier(String s, GraphTextWriter writer) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
@@ -465,6 +603,11 @@ public class IdentifiersDeobfuscation {
return writer;
}
/**
* Escapes obfuscated identifier.
* @param s String
* @return Escaped string
*/
public static String escapeOIdentifier(String s) {
StringBuilder ret = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
@@ -505,6 +648,9 @@ public class IdentifiersDeobfuscation {
return ret.toString();
}
/**
* Clears cache.
*/
public static void clearCache() {
as2NameCache.clear();
as3NameCache.clear();
@@ -18,43 +18,79 @@ package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.iggy.conversion.IggySwfBundle;
import com.jpexs.helpers.Path;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
/**
*
* Information about openable source.
* @author JPEXS
*/
public class OpenableSourceInfo {
/**
* Input stream of the source
*/
private final InputStream inputStream;
/**
* File path of the source
*/
private String file;
/**
* Title of the file
*/
private String fileTitle;
/**
* Whether to auto-detect bundle
*/
private final boolean detectBundle;
/**
* Whether the source is empty
*/
private boolean empty = false;
/**
* Kind of the source
*/
private OpenableSourceKind kind;
/**
* Constructs OpenableSourceInfo with empty source
* @param fileTitle Title of the file
*/
public OpenableSourceInfo(String fileTitle) {
this(null, null, fileTitle, false);
empty = true;
}
/**
* Check if the source is empty
* @return true if the source is empty
*/
public boolean isEmpty() {
return empty;
}
/**
* Constructs OpenableSourceInfo with input stream
* @param inputStream Input stream of the source
* @param file File path of the source
* @param fileTitle Title of the file
*/
public OpenableSourceInfo(InputStream inputStream, String file, String fileTitle) {
this(inputStream, file, fileTitle, true);
}
/**
* Constructs OpenableSourceInfo with input stream
* @param inputStream Input stream of the source
* @param file File path of the source
* @param fileTitle Title of the file
* @param detectBundle Whether to auto-detect bundle
*/
public OpenableSourceInfo(InputStream inputStream, String file, String fileTitle, boolean detectBundle) {
this.inputStream = inputStream;
this.file = file;
@@ -63,10 +99,17 @@ public class OpenableSourceInfo {
detectKind();
}
/**
* Gets kind of the source.
* @return Kind of the source
*/
public OpenableSourceKind getKind() {
return kind;
}
/**
* Detects kind of the source.
*/
private void detectKind() {
if (isBundle()) {
kind = OpenableSourceKind.BUNDLE;
@@ -77,32 +120,54 @@ public class OpenableSourceInfo {
}
}
/**
* Gets input stream of the source.
* @return Input stream of the source
*/
public InputStream getInputStream() {
return inputStream;
}
/**
* Gets file path of the source.
* @return File path of the source
*/
public String getFile() {
return file;
}
/**
* Sets file path of the source.
* @param file File path of the source
*/
public void setFile(String file) {
this.file = file;
detectKind();
empty = false;
}
/**
* Sets title of the file.
*
* @param fileTitle File title
*/
public void setFileTitle(String fileTitle) {
this.fileTitle = fileTitle;
}
/**
* Gets title of the file.
*
* @return File title
*/
public String getFileTitle() {
return fileTitle;
}
/**
* Get title of the file
* Gets title of the file.
*
* @return file title
* @return File title
*/
public String getFileTitleOrName() {
if (fileTitle != null) {
@@ -111,11 +176,15 @@ public class OpenableSourceInfo {
return file;
}
/**
* Checks if the source is a bundle.
* @return True if the source is a bundle
*/
public boolean isBundle() {
if (inputStream == null && file != null) {
File fileObj = new File(file);
String fileName = fileObj.getName();
if (fileName.startsWith("asdec_") && fileName.endsWith(".tmp")) {
if (fileName.startsWith("asdec_") && fileName.endsWith(".tmp")) { //FIXME: is this still needed?
return false;
}
String extension = Path.getExtension(fileObj);
@@ -124,6 +193,13 @@ public class OpenableSourceInfo {
return false;
}
/**
* Gets bundle from the source.
* @param noCheck Whether to check the bundle
* @param searchMode Search mode
* @return Bundle or null
* @throws IOException
*/
public Bundle getBundle(boolean noCheck, SearchMode searchMode) throws IOException {
if (!isBundle()) {
return null;
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash;
/**
*
* Openable source kind.
* @author JPEXS
*/
public enum OpenableSourceKind {
@@ -17,15 +17,26 @@
package com.jpexs.decompiler.flash;
/**
*
* Parse exception.
* @author JPEXS
*/
public abstract class ParseException extends Exception {
/**
* Line number where the exception occurred.
*/
public long line;
/**
* Text of the exception.
*/
public String text;
/**
* Constructs a new parse exception.
* @param text
* @param line
*/
public ParseException(String text, long line) {
super("ParseException:" + text + " on line " + line);
this.line = line;
@@ -17,45 +17,82 @@
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.tags.Tag;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* Tag list that is read-only.
* @author JPEXS
*/
public class ReadOnlyTagList implements Iterable<Tag> {
/**
* Empty read-only tag list.
*/
public static final ReadOnlyTagList EMPTY = new ReadOnlyTagList(new ArrayList<>());
/**
* List of tags.
*/
private final List<Tag> list;
/**
* Constructs read-only tag list.
* @param list List of tags
*/
public ReadOnlyTagList(List<Tag> list) {
this.list = list;
}
/**
* Returns iterator for tags.
* @return Iterator for tags
*/
@Override
public Iterator<Tag> iterator() {
return list.iterator();
}
/**
* Returns number of tags.
* @return Number of tags
*/
public int size() {
return list.size();
}
/**
* Returns true if list is empty.
* @return True if list is empty
*/
public boolean isEmpty() {
return list.isEmpty();
}
/**
* Returns tag at index.
* @param index Index
* @return Tag
*/
public Tag get(int index) {
return list.get(index);
}
/**
* Returns index of tag.
* @param tag Tag
* @return Index of tag or -1 if not found
*/
public int indexOf(Tag tag) {
return list.indexOf(tag);
}
/**
* Converts list to array list.
* @return Array list
*/
public ArrayList<Tag> toArrayList() {
return new ArrayList<>(list);
}
@@ -19,20 +19,36 @@ package com.jpexs.decompiler.flash;
import java.io.IOException;
/**
*
* Task that can be retried on error.
* @author JPEXS
*/
public class RetryTask {
/**
* Runnable that can throw IOException.
*/
private final RunnableIOEx r;
/**
* Handler for retrying.
*/
private final AbortRetryIgnoreHandler handler;
/**
* Constructs a new RetryTask.
* @param r Runnable that can throw IOException
* @param handler Handler for retrying
*/
public RetryTask(RunnableIOEx r, AbortRetryIgnoreHandler handler) {
this.r = r;
this.handler = handler;
}
/**
* Runs the task.
* @throws IOException
* @throws InterruptedException
*/
public void run() throws IOException, InterruptedException {
boolean retry;
do {
@@ -19,11 +19,16 @@ package com.jpexs.decompiler.flash;
import java.io.IOException;
/**
*
* Runnable with IOException and InterruptedException.
* @author JPEXS
*/
@FunctionalInterface
public interface RunnableIOEx {
/**
* Run method.
* @throws IOException
* @throws InterruptedException
*/
public void run() throws IOException, InterruptedException;
}
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash;
/**
*
* Interface for runnable with result.
* @param <T> Type of result
* @author JPEXS
*/
@@ -16,31 +16,48 @@
*/
package com.jpexs.decompiler.flash;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
*
* SWC file.
* @author JPEXS
*/
public class SWC extends ZippedBundle {
/**
* Constructs SWC from input stream.
* @param is Input stream
* @throws IOException
*/
public SWC(InputStream is) throws IOException {
super(is);
}
/**
* Constructs SWC from file.
* @param filename File
* @throws IOException
*/
public SWC(File filename) throws IOException {
super(filename);
}
/**
* Initializes SWC bundle.
* @param is Input stream
* @param filename File
* @throws IOException
*/
@Override
protected void initBundle(InputStream is, File filename) throws IOException {
super.initBundle(is, filename);
@@ -76,6 +93,10 @@ public class SWC extends ZippedBundle {
}
}
/**
* Returns extension of SWC file.
* @return Extension
*/
@Override
public String getExtension() {
return "swc";
File diff suppressed because it is too large Load Diff
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash;
/**
*
* SWF compression types.
* @author JPEXS
*/
public enum SWFCompression {
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash;
/**
*
* SWF header information.
* @author JPEXS
*/
public class SWFHeader {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -16,11 +16,8 @@
*/
package com.jpexs.decompiler.flash;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.MemoryInputStream;
import com.jpexs.helpers.PosMarkedInputStream;
import com.jpexs.helpers.ProgressListener;
import com.jpexs.helpers.Searchable;
import com.jpexs.helpers.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
@@ -29,43 +26,82 @@ import java.util.Map;
import java.util.Set;
/**
*
* SWF search class.
* @author JPEXS
*/
public class SWFSearch {
/**
* Searchable object
*/
protected Searchable s;
/**
* No check for validity
*/
private final boolean noCheck;
/**
* Search mode
*/
private final SearchMode searchMode;
/**
* Already processed
*/
private boolean processed = false;
/**
* Progress listeners
*/
private final Set<ProgressListener> listeners = new HashSet<>();
/**
* SWF streams
*/
private final Map<Long, MemoryInputStream> swfStreams = new LinkedHashMap<>();
/**
* Constructs SWF search object.
* @param s Searchable object
* @param noCheck No check for validity
* @param searchMode Search mode
*/
public SWFSearch(Searchable s, boolean noCheck, SearchMode searchMode) {
this.s = s;
this.noCheck = noCheck;
this.searchMode = searchMode;
}
/**
* Adds progress listener.
* @param l
*/
public void addProgressListener(ProgressListener l) {
listeners.add(l);
}
/**
* Removes progress listener.
* @param l
*/
public void removeProgressListener(ProgressListener l) {
listeners.remove(l);
}
/**
* Sets progress.
* @param p Progress
*/
private void setProgress(int p) {
for (ProgressListener l : listeners) {
l.progress(p);
}
}
/**
* Processes SWF search.
*/
public void process() {
Map<Long, InputStream> ret;
ret = s.search(new ProgressListener() {
@@ -144,6 +180,13 @@ public class SWFSearch {
processed = true;
}
/**
* Gets SWF stream.
* @param listener Progress listener
* @param address Address
* @return SWF stream
* @throws IOException
*/
public MemoryInputStream get(ProgressListener listener, long address) throws IOException {
if (!processed) {
return null;
@@ -154,10 +197,18 @@ public class SWFSearch {
return swfStreams.get(address);
}
/**
* Gets list of addresses.
* @return List of addresses
*/
public Set<Long> getAddresses() {
return swfStreams.keySet();
}
/**
* Gets number of SWF streams.
* @return Number of SWF streams
*/
public int length() {
if (!processed) {
return 0;
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash;
/**
*
* Search mode for searching in SWF file.
* @author JPEXS
*/
public enum SearchMode {
@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphTargetItem;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
@@ -28,71 +29,167 @@ import java.util.List;
import java.util.Map;
/**
*
* Local data for source generator.
* @author JPEXS
*/
public class SourceGeneratorLocalData implements Serializable {
/**
* Map of variable name to register number.
*/
public HashMap<String, Integer> registerVars;
/**
* In function level.
*/
public Integer inFunction;
/**
* In method.
*/
public Boolean inMethod;
/**
* For in level.
*/
public Integer forInLevel;
// TODO: handle AVM2 separately
//TODO: handle AVM2 separately
/**
* List of AVM2 exceptions.
*/
public List<ABCException> exceptions = new ArrayList<>();
/**
* List of finally Ids
*/
public List<Integer> finallyCatches = new ArrayList<>();
/**
* List of finally opened loops
*/
public List<List<Long>> finallyOpenedLoops = new ArrayList<>();
/**
* Counter of finally uses
*/
public Map<Integer, Integer> finallyCounter = new HashMap<>();
/**
* Register number for finally block
*/
public int finallyRegister = -1;
/**
* Current class base name
*/
public String currentClassBaseName; //FIXME! Suffixed or not?
/**
* Super class name
*/
public String superClass = null;
/**
* Super package name
*/
public DottedChain superPkg = null;
/**
* Activation register number
*/
public int activationReg = 0;
/**
* Call stack
*/
public List<MethodBody> callStack = new ArrayList<>();
/**
* MethodBody trait usages. Map of MethodBody to list of used trait indexes.
*/
public Map<MethodBody, List<Integer>> traitUsages = new HashMap<>();
/**
* Current package
*/
public DottedChain pkg = DottedChain.TOPLEVEL;
/**
* Scope stack
*/
public List<GraphTargetItem> scopeStack = new ArrayList<>();
/**
* Current script
*/
public ScriptInfo currentScript;
/**
* Current script index
*/
public int scriptIndex;
/**
* Is in sub method
*/
public boolean subMethod = false;
/**
* Current private namespace id
*/
public int privateNs = 0;
/**
* Current protected namespace id
*/
public int protectedNs = 0;
/**
* Is in static method
*/
public boolean isStatic = false;
/**
* Opened loop ids
*/
public List<Long> openedLoops = new ArrayList<>();
/**
* Opened loop ids for catch blocks
*/
public List<List<Long>> catchesOpenedLoops = new ArrayList<>();
/**
* Temp registers used in catch blocks
*/
public List<Integer> catchesTempRegs = new ArrayList<>();
/**
* Document class
*/
public String documentClass;
/**
* Is second run
*/
public boolean secondRun = false;
/**
* Gets full class name.
* @return Full class name
*/
public String getFullClass() {
return pkg == null ? currentClassBaseName : pkg.addWithSuffix(currentClassBaseName).toRawString();
}
/**
* Constructs new SourceGeneratorLocalData.
* @param registerVars Map of variable name to register number
* @param inFunction In function level
* @param inMethod In method
* @param forInLevel For in level
*/
public SourceGeneratorLocalData(HashMap<String, Integer> registerVars, Integer inFunction, Boolean inMethod, Integer forInLevel) {
this.registerVars = registerVars;
this.inFunction = inFunction;
@@ -19,7 +19,7 @@ package com.jpexs.decompiler.flash;
import java.io.IOException;
/**
*
* Exception thrown when SWF file cannot be opened.
* @author JPEXS
*/
public class SwfOpenException extends IOException {
@@ -19,10 +19,14 @@ package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.tags.Tag;
/**
*
* Listener for tag removal.
* @author JPEXS
*/
public interface TagRemoveListener {
/**
* Called when a tag is removed.
* @param tag Removed tag
*/
public void tagRemoved(Tag tag);
}
@@ -17,10 +17,15 @@
package com.jpexs.decompiler.flash;
/**
*
* URL resolver interface.
* @author JPEXS
*/
public interface UrlResolver {
/**
* Resolves URL to SWF object.
* @param url URL
* @return SWF object or null if not valid
*/
public SWF resolveUrl(String url);
}
@@ -17,24 +17,43 @@
package com.jpexs.decompiler.flash;
/**
*
* Exception thrown when a value is too large for a specific type.
* @author JPEXS
*/
public class ValueTooLargeException extends IllegalArgumentException {
/**
* Type of the value
*/
private final String type;
/**
* Value that is too large
*/
private final Object value;
/**
* Constructs a new ValueTooLargeException with the specified type and value.
* @param type Type of the value
* @param value Value that is too large
*/
public ValueTooLargeException(String type, Object value) {
super("Value is too large for " + type + ": " + value);
this.type = type;
this.value = value;
}
/**
* Gets the type of the value.
* @return Type of the value
*/
public String getType() {
return type;
}
/**
* Gets the value that is too large.
* @return Value that is too large
*/
public Object getValue() {
return value;
}
@@ -17,18 +17,33 @@
package com.jpexs.decompiler.flash;
/**
*
* FFDec version information.
* @author JPEXS
*/
public class Version {
/**
* Git tag name
*/
public String tagName;
/**
* Version name
*/
public String versionName;
/**
* Release date
*/
public String releaseDate;
/**
* Is this a prerelease version?
*/
public boolean prerelease;
/**
* Description
*/
public String description;
}
@@ -20,12 +20,8 @@ import com.jpexs.helpers.Helper;
import com.jpexs.helpers.MemoryInputStream;
import com.jpexs.helpers.ReReadableInputStream;
import com.jpexs.helpers.streams.SeekableInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -35,31 +31,65 @@ import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
*
* Bundle implementation for ZIP files.
* @author JPEXS
*/
public class ZippedBundle implements Bundle {
/**
*
*/
protected Set<String> keySet = new HashSet<>();
/**
* File input stream for the ZIP file.
*/
protected FileInputStream fis;
/**
* Re-readable input stream for the ZIP file.
*/
protected ReReadableInputStream is;
/**
* File name of the ZIP file.
*/
protected File filename;
/**
* Constructs a new ZippedBundle from an input stream.
* @param is Input stream
* @throws IOException
*/
public ZippedBundle(InputStream is) throws IOException {
this(is, null);
}
/**
* Constructs a new ZippedBundle from a file.
* @param filename File
* @throws IOException
*/
public ZippedBundle(File filename) throws IOException {
this(null, filename);
}
/**
* Constructs a new ZippedBundle from an input stream and a file.
* @param is Input stream
* @param filename File
* @throws IOException
*/
protected ZippedBundle(InputStream is, File filename) throws IOException {
initBundle(is, filename);
}
/**
* Initializes the bundle.
* @param is Input stream
* @param filename File
* @throws IOException
*/
protected void initBundle(InputStream is, File filename) throws IOException {
if (filename != null) {
fis = new FileInputStream(filename);
@@ -80,16 +110,30 @@ public class ZippedBundle implements Bundle {
}
}
/**
* Gets the number of entries in the bundle.
* @return
*/
@Override
public int length() {
return keySet.size();
}
/**
* Gets the keys in the bundle.
* @return
*/
@Override
public Set<String> getKeys() {
return keySet;
}
/**
* Gets the input stream for a key.
* @param key Key
* @return Input stream
* @throws IOException
*/
@Override
public SeekableInputStream getOpenable(String key) throws IOException {
if (!keySet.contains(key)) {
@@ -115,6 +159,11 @@ public class ZippedBundle implements Bundle {
//return cachedSWFs.get(key);
}
/**
* Gets all input streams in the bundle.
* @return Map of key to input stream
* @throws IOException
*/
@Override
public Map<String, SeekableInputStream> getAll() throws IOException {
Map<String, SeekableInputStream> ret = new HashMap<>();
@@ -124,16 +173,32 @@ public class ZippedBundle implements Bundle {
return ret;
}
/**
* Gets the extension of the bundle.
* @return Extension
*/
@Override
public String getExtension() {
return "zip";
}
/**
* Gets whether the bundle is read-only.
* @return Whether the bundle is read-only
*/
@Override
public boolean isReadOnly() {
return this.filename == null || !this.filename.canWrite();
}
/**
* Replaces the input stream for a key.
* @param key Key
* @param swfIs New input stream
* @return Whether the operation was successful
* @throws IOException
*/
@Override
public boolean putOpenable(String key, InputStream swfIs) throws IOException {
if (this.isReadOnly()) {
File diff suppressed because it is too large Load Diff
@@ -18,47 +18,61 @@ package com.jpexs.decompiler.flash.abc;
import com.jpexs.decompiler.flash.EndOfStreamException;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.abc.types.Decimal;
import com.jpexs.decompiler.flash.abc.types.Float4;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.types.ValueKind;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.abc.types.*;
import com.jpexs.decompiler.flash.abc.types.traits.*;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType;
import com.jpexs.helpers.MemoryInputStream;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
*
* ABC input stream.
* @author JPEXS
*/
public class ABCInputStream implements AutoCloseable {
/**
* Class has protected namespace
*/
private static final int CLASS_PROTECTED_NS = 8;
/**
* Trait has metadata
*/
private static final int ATTR_METADATA = 4;
/**
* Input stream
*/
private final MemoryInputStream is;
/**
* Buffer output stream
*/
private ByteArrayOutputStream bufferOs = null;
/**
* Debug read
*/
public static final boolean DEBUG_READ = false;
/**
* Dump info
*/
public DumpInfo dumpInfo;
/**
* String data buffer
*/
private byte[] stringDataBuffer = new byte[256];
/**
* Starts buffering.
*/
public void startBuffer() {
if (bufferOs == null) {
bufferOs = new ByteArrayOutputStream();
@@ -67,6 +81,10 @@ public class ABCInputStream implements AutoCloseable {
}
}
/**
* Stops buffering and returns buffered bytes.
* @return Buffered bytes
*/
public byte[] stopBuffer() {
if (bufferOs == null) {
return SWFInputStream.BYTE_ARRAY_EMPTY;
@@ -76,12 +94,16 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Creates new ABC input stream
* @param is Input stream
*/
public ABCInputStream(MemoryInputStream is) {
this.is = is;
}
/**
* Sets position in bytes in the stream
* Sets position in bytes in the stream.
*
* @param pos Number of bytes
* @throws java.io.IOException
@@ -90,10 +112,23 @@ public class ABCInputStream implements AutoCloseable {
is.seek(pos);
}
/**
* Creates new dump level.
* @param name Name
* @param type Type
* @return New DumpInfo
*/
public DumpInfo newDumpLevel(String name, String type) {
return newDumpLevel(name, type, DumpInfoSpecialType.NONE);
}
/**
* Creates new dump level.
* @param name Name
* @param type Type
* @param specialType Special type
* @return New DumpInfo
*/
public DumpInfo newDumpLevel(String name, String type, DumpInfoSpecialType specialType) {
if (dumpInfo != null) {
long startByte = is.getPos();
@@ -108,10 +143,17 @@ public class ABCInputStream implements AutoCloseable {
return dumpInfo;
}
/**
* Ends dump level.
*/
public void endDumpLevel() {
endDumpLevel(null);
}
/**
* Ends dump level.
* @param value Value
*/
public void endDumpLevel(Object value) {
if (dumpInfo != null) {
dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte;
@@ -120,6 +162,10 @@ public class ABCInputStream implements AutoCloseable {
}
}
/**
* Ends dump level until specified dump info.
* @param di Dump info
*/
public void endDumpLevelUntil(DumpInfo di) {
if (di != null) {
while (dumpInfo != null && dumpInfo != di) {
@@ -128,6 +174,11 @@ public class ABCInputStream implements AutoCloseable {
}
}
/**
* Reads byte from the stream.
* @return Byte
* @throws IOException
*/
private int readInternal() throws IOException {
int i = is.read();
if (i == -1) {
@@ -144,6 +195,12 @@ public class ABCInputStream implements AutoCloseable {
return i;
}
/**
* Reads byte from the stream.
* @param name Name
* @return Byte
* @throws IOException
*/
public int read(String name) throws IOException {
newDumpLevel(name, "byte");
int ret = readInternal();
@@ -151,6 +208,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads bytes from the stream.
* @param b Bytes
* @return Number of bytes read
* @throws IOException
*/
private int read(byte[] b) throws IOException {
int currBytesRead = is.read(b);
if (DEBUG_READ) {
@@ -174,6 +237,12 @@ public class ABCInputStream implements AutoCloseable {
return currBytesRead;
}
/**
* Reads U8 from the stream.
* @param name Name
* @return U8
* @throws IOException
*/
public int readU8(String name) throws IOException {
newDumpLevel(name, "U8");
int ret = readInternal();
@@ -181,6 +250,11 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads U32 from the stream.
* @return U32
* @throws IOException
*/
private long readU32Internal() throws IOException {
int i;
long ret = 0;
@@ -198,6 +272,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads U32 from the stream.
* @param name Name
* @return U32
* @throws IOException
*/
public long readU32(String name) throws IOException {
newDumpLevel(name, "U32");
long ret = readU32Internal();
@@ -205,12 +285,23 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads U30 from the stream.
* @return U30
* @throws IOException
*/
private int readU30Internal() throws IOException {
long u32 = readU32Internal();
//no bits above bit 30
return (int) (u32 & 0x3FFFFFFF);
}
/**
* Reads U30 from the stream.
* @param name Name
* @return U30
* @throws IOException
*/
public int readU30(String name) throws IOException {
newDumpLevel(name, "U30");
int ret = readU30Internal();
@@ -218,6 +309,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads S24 from the stream.
* @param name Name
* @return S24
* @throws IOException
*/
public int readS24(String name) throws IOException {
newDumpLevel(name, "S24");
int ret = (readInternal()) + (readInternal() << 8) + (readInternal() << 16);
@@ -230,6 +327,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads U16 from the stream.
* @param name Name
* @return U16
* @throws IOException
*/
public int readU16(String name) throws IOException {
newDumpLevel(name, "U16");
int ret = (readInternal()) + (readInternal() << 8);
@@ -237,6 +340,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads S32 from the stream.
* @param name Name
* @return S32
* @throws IOException
*/
public int readS32(String name) throws IOException {
int i;
long ret = 0;
@@ -262,10 +371,20 @@ public class ABCInputStream implements AutoCloseable {
return (int) ret;
}
/**
* Gets available bytes in the stream.
* @return Available bytes
* @throws IOException
*/
public int available() throws IOException {
return is.available();
}
/**
* Reads long from the stream.
* @return Long
* @throws IOException
*/
private long readLong() throws IOException {
safeRead(8, stringDataBuffer);
byte[] readBuffer = stringDataBuffer;
@@ -279,6 +398,12 @@ public class ABCInputStream implements AutoCloseable {
+ ((readBuffer[0] & 255)));
}
/**
* Reads double from the stream.
* @param name Name
* @return Double
* @throws IOException
*/
public double readDouble(String name) throws IOException {
newDumpLevel(name, "Double");
long el = readLong();
@@ -287,12 +412,24 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Safely reads bytes from the stream.
* @param count Count
* @param data Data
* @throws IOException
*/
private void safeRead(int count, byte[] data) throws IOException {
for (int i = 0; i < count; i++) {
data[i] = (byte) readInternal();
}
}
/**
* Reads namespace from the stream.
* @param name Name
* @return Namespace
* @throws IOException
*/
public Namespace readNamespace(String name) throws IOException {
newDumpLevel(name, "Namespace");
int kind = read("kind");
@@ -307,6 +444,12 @@ public class ABCInputStream implements AutoCloseable {
return new Namespace(kind, name_index);
}
/**
* Reads multiname from the stream.
* @param name Name
* @return Multiname
* @throws IOException
*/
public Multiname readMultiname(String name) throws IOException {
int kind = readU8("kind");
Multiname result = null;
@@ -344,6 +487,12 @@ public class ABCInputStream implements AutoCloseable {
return result;
}
/**
* Reads method info from the stream.
* @param name Name
* @return Method info
* @throws IOException
*/
public MethodInfo readMethodInfo(String name) throws IOException {
newDumpLevel(name, "method_info");
int param_count = readU30("param_count");
@@ -376,6 +525,12 @@ public class ABCInputStream implements AutoCloseable {
return new MethodInfo(param_types, ret_type, name_index, flags, optional, param_names);
}
/**
* Reads trait from the stream.
* @param name Name
* @return Trait
* @throws IOException
*/
public Trait readTrait(String name) throws IOException {
newDumpLevel(name, "Trait");
long pos = getPosition();
@@ -437,6 +592,12 @@ public class ABCInputStream implements AutoCloseable {
return trait;
}
/**
* Reads traits from the stream.
* @param name Name
* @return Traits
* @throws IOException
*/
public Traits readTraits(String name) throws IOException {
newDumpLevel(name, "Traits");
int count = readU30("count");
@@ -448,6 +609,12 @@ public class ABCInputStream implements AutoCloseable {
return traits;
}
/**
* Reads bytes from the stream.
* @param count Count
* @return Bytes
* @throws IOException
*/
private byte[] readBytesInternal(int count) throws IOException {
byte[] ret = new byte[count];
for (int i = 0; i < count; i++) {
@@ -456,6 +623,14 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads bytes from the stream.
* @param count Count
* @param name Name
* @param specialType Special type
* @return Bytes
* @throws IOException
*/
public byte[] readBytes(int count, String name, DumpInfoSpecialType specialType) throws IOException {
newDumpLevel(name, "Bytes", specialType);
byte[] ret = readBytesInternal(count);
@@ -463,6 +638,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads decimal from the stream.
* @param name Name
* @return Decimal
* @throws IOException
*/
public Decimal readDecimal(String name) throws IOException {
newDumpLevel(name, "Decimal");
byte[] data = readBytesInternal(16);
@@ -470,6 +651,12 @@ public class ABCInputStream implements AutoCloseable {
return new Decimal(data);
}
/**
* Reads float from the stream.
* @param name Name
* @return Float
* @throws IOException
*/
public Float readFloat(String name) throws IOException {
newDumpLevel(name, "Float");
int intBits = (readInternal()) + (readInternal() << 8);
@@ -478,6 +665,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads float4 from the stream.
* @param name Name
* @return Float4
* @throws IOException
*/
public Float4 readFloat4(String name) throws IOException {
newDumpLevel(name, "Float4");
float f1 = readFloat("value1");
@@ -489,6 +682,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads instance info from the stream.
* @param name Name
* @return Instance info
* @throws IOException
*/
public InstanceInfo readInstanceInfo(String name) throws IOException {
newDumpLevel(name, "instance_info");
InstanceInfo ret = new InstanceInfo(null); // do not create Traits in constructor
@@ -509,6 +708,12 @@ public class ABCInputStream implements AutoCloseable {
return ret;
}
/**
* Reads string from the stream.
* @param name Name
* @return String
* @throws IOException
*/
public String readString(String name) throws IOException {
newDumpLevel(name, "String");
int length = readU30Internal();
@@ -529,14 +734,17 @@ public class ABCInputStream implements AutoCloseable {
return r;
}
/*public void markStart(){
bytesRead=0;
}*/
/**
* Gets current position in the stream.
* @return Position
*/
public long getPosition() {
return is.getPos();
}
/**
* Closes the stream.
*/
@Override
public void close() {
}
@@ -20,31 +20,42 @@ import com.jpexs.decompiler.flash.abc.types.ClassInfo;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* ABC method indexing.
* @author JPEXS
*/
public class ABCMethodIndexing {
/**
* ABC
*/
private final ABC abc;
/**
* Method body index for method info
*/
private Map<MethodInfo, Integer> bodyIdxFromMethod = new HashMap<>();
/**
* Constructs ABC method indexing.
* @param abc ABC
*/
public ABCMethodIndexing(ABC abc) {
this.abc = abc;
createBodyIdxFromMethodIdxMap(abc);
}
/**
* Creates body index from method index map.
* @param abc ABC
*/
public final void createBodyIdxFromMethodIdxMap(ABC abc) {
List<MethodBody> bodies = abc.bodies;
Map<MethodInfo, Integer> map = new HashMap<>(bodies.size());
@@ -56,6 +67,11 @@ public class ABCMethodIndexing {
bodyIdxFromMethod = map;
}
/**
* Finds method body index for method info.
* @param methodInfo Method info
* @return Method body index or -1 if not found
*/
public int findMethodBodyIndex(MethodInfo methodInfo) {
Integer bi = bodyIdxFromMethod.get(methodInfo);
if (bi == null) {
@@ -65,6 +81,11 @@ public class ABCMethodIndexing {
return bi;
}
/**
* Finds method body index for method info.
* @param methodInfo Method info index
* @return Method body index or -1 if not found
*/
public int findMethodBodyIndex(int methodInfo) {
if (methodInfo < 0 || methodInfo >= abc.method_info.size()) {
return -1;
@@ -74,6 +95,11 @@ public class ABCMethodIndexing {
return findMethodBodyIndex(mi);
}
/**
* Finds method body for method info.
* @param methodInfo Method info
* @return Method body or null if not found
*/
public MethodBody findMethodBody(MethodInfo methodInfo) {
int bi = findMethodBodyIndex(methodInfo);
if (bi != -1) {
@@ -83,6 +109,11 @@ public class ABCMethodIndexing {
return null;
}
/**
* Finds method body for method info.
* @param methodInfo Method info index
* @return Method body or null if not found
*/
public MethodBody findMethodBody(int methodInfo) {
int bi = findMethodBodyIndex(methodInfo);
if (bi != -1) {
@@ -92,6 +123,12 @@ public class ABCMethodIndexing {
return null;
}
/**
* Finds method traits.
* @param pack Script pack
* @param bodyIndex Method body index
* @return Method traits
*/
public List<Trait> findMethodTraits(ScriptPack pack, int bodyIndex) {
int methodInfo = abc.bodies.get(bodyIndex).method_info;
List<Trait> traits = abc.script_info.get(pack.scriptIndex).traits.traits;
@@ -104,6 +141,13 @@ public class ABCMethodIndexing {
return resultTraits;
}
/**
* Finds traits.
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info index
* @param result Result list
*/
private static void findTraits(ABC abc, Trait trait, int methodInfo, List<Trait> result) {
if (trait instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) trait;
@@ -19,15 +19,24 @@ package com.jpexs.decompiler.flash.abc;
import java.io.IOException;
/**
*
* ABC open exception.
* @author JPEXS
*/
public class ABCOpenException extends IOException {
/**
* Constructs a new ABCOpenException with the specified detail message.
* @param message Detail message
*/
public ABCOpenException(String message) {
super(message);
}
/**
* Constructs a new ABCOpenException with the specified detail message and cause.
* @param message Detail message
* @param cause Cause
*/
public ABCOpenException(String message, Throwable cause) {
super(message, cause);
}
@@ -16,57 +16,85 @@
*/
package com.jpexs.decompiler.flash.abc;
import com.jpexs.decompiler.flash.abc.types.Decimal;
import com.jpexs.decompiler.flash.abc.types.Float4;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.abc.types.*;
import com.jpexs.decompiler.flash.abc.types.traits.*;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.io.IOException;
import java.io.OutputStream;
/**
*
* ABC output stream.
* @author JPEXS
*/
public class ABCOutputStream extends OutputStream {
/**
* Output stream
*/
private final OutputStream os;
/**
* Current position
*/
private long position = 0L;
/**
* Constructs ABC output stream.
* @param os Output stream
*/
public ABCOutputStream(OutputStream os) {
this.os = os;
}
/**
* Returns current position.
* @return Current position
*/
public long getPosition() {
return position;
}
/**
* Writes a byte to the output stream.
* @param b The byte to write.
* @throws IOException
*/
@Override
public void write(int b) throws IOException {
os.write(b);
position++;
}
/**
* Writes a byte array to the output stream.
* @param data The data to write.
* @throws IOException
*/
@Override
public void write(byte[] data) throws IOException {
os.write(data);
position += data.length;
}
/**
* Writes a byte array to the output stream.
* @param b The data to write.
* @param off The start offset in the data.
* @param len The number of bytes to write.
* @throws IOException
*/
@Override
public void write(byte[] b, int off, int len) throws IOException {
os.write(b, off, len);
position += len;
}
/**
* Writes U30 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeU30(long value) throws IOException {
writeS32(value);
/*boolean loop = true;
@@ -91,6 +119,11 @@ public class ABCOutputStream extends OutputStream {
*/
}
/**
* Writes U32 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeU32(long value) throws IOException {
boolean loop = true;
value &= 0xFFFFFFFFL;
@@ -107,6 +140,11 @@ public class ABCOutputStream extends OutputStream {
} while (loop);
}
/**
* Writes S24 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeS24(long value) throws IOException {
int ret = (int) (value & 0xff);
write(ret);
@@ -118,6 +156,11 @@ public class ABCOutputStream extends OutputStream {
write(ret);
}
/**
* Writes S32 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeS32(long value) throws IOException {
boolean belowZero = value < 0;
/*if (belowZero) {
@@ -150,6 +193,11 @@ public class ABCOutputStream extends OutputStream {
} while (loop);
}
/**
* Writes long to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeLong(long value) throws IOException {
byte[] writeBuffer = new byte[8];
writeBuffer[7] = (byte) (value >>> 56);
@@ -163,14 +211,29 @@ public class ABCOutputStream extends OutputStream {
write(writeBuffer);
}
/**
* Writes double to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeDouble(double value) throws IOException {
writeLong(Double.doubleToLongBits(value));
}
/**
* Writes float to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeFloat(float value) throws IOException {
writeU16(Float.floatToIntBits(value));
}
/**
* Writes float4 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeFloat4(Float4 value) throws IOException {
writeFloat(value.values[0]);
writeFloat(value.values[1]);
@@ -178,21 +241,41 @@ public class ABCOutputStream extends OutputStream {
writeFloat(value.values[3]);
}
/**
* Writes U8 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeU8(int value) throws IOException {
write(value);
}
/**
* Writes U16 to the output stream.
* @param value Value to write
* @throws IOException
*/
public void writeU16(int value) throws IOException {
write(value & 0xff);
write((value >> 8) & 0xff);
}
/**
* Writes String to the output stream.
* @param s String to write
* @throws IOException
*/
public void writeString(String s) throws IOException {
byte[] sbytes = Utf8Helper.getBytes(s);
writeU30(sbytes.length);
write(sbytes);
}
/**
* Writes Namespace to the output stream.
* @param ns Namespace to write
* @throws IOException
*/
public void writeNamespace(Namespace ns) throws IOException {
write(ns.kind);
boolean found = false;
@@ -208,6 +291,11 @@ public class ABCOutputStream extends OutputStream {
}
}
/**
* Writes Multiname to the output stream.
* @param m Multiname to write
* @throws IOException
*/
public void writeMultiname(Multiname m) throws IOException {
writeU8(m.kind);
if ((m.kind == Multiname.QNAME) || (m.kind == Multiname.QNAMEA)) {
@@ -230,6 +318,11 @@ public class ABCOutputStream extends OutputStream {
// kind==0x11,0x12 nothing CONSTANT_RTQNameL and CONSTANT_RTQNameLA.
}
/**
* Writes MethodInfo to the output stream.
* @param mi MethodInfo to write
* @throws IOException
*/
public void writeMethodInfo(MethodInfo mi) throws IOException {
writeU30(mi.param_types.length);
writeU30(mi.ret_type);
@@ -253,6 +346,11 @@ public class ABCOutputStream extends OutputStream {
}
}
/**
* Writes Trait to the output stream.
* @param t Trait to write
* @throws IOException
*/
public void writeTrait(Trait t) throws IOException {
writeU30(t.name_index);
write((t.kindFlags << 4) + t.kindType);
@@ -288,6 +386,11 @@ public class ABCOutputStream extends OutputStream {
}
}
/**
* Writes Traits to the output stream.
* @param t Traits to write
* @throws IOException
*/
public void writeTraits(Traits t) throws IOException {
writeU30(t.traits.size());
for (int i = 0; i < t.traits.size(); i++) {
@@ -295,6 +398,11 @@ public class ABCOutputStream extends OutputStream {
}
}
/**
* Writes InstanceInfo to the output stream.
* @param ii InstanceInfo to write
* @throws IOException
*/
public void writeInstanceInfo(InstanceInfo ii) throws IOException {
writeU30(ii.name_index);
writeU30(ii.super_index);
@@ -310,10 +418,20 @@ public class ABCOutputStream extends OutputStream {
writeTraits(ii.instance_traits);
}
/**
* Writes Decimal to the output stream.
* @param value Decimal to write
* @throws IOException
*/
public void writeDecimal(Decimal value) throws IOException {
write(value.data);
}
/**
* Gets U30 byte length.
* @param value Value
* @return
*/
public static int getU30ByteLength(long value) {
boolean belowZero = value < 0;
int bitcount = 0;
@@ -16,20 +16,43 @@
*/
package com.jpexs.decompiler.flash.abc;
/**
* Represents ABC version
*/
public class ABCVersion implements Comparable<ABCVersion> {
/**
* Major version
*/
public int major = 46;
/**
* Minor version
*/
public int minor = 16;
/**
* Constructs new ABCVersion
*/
public ABCVersion() {
}
/**
* Constructs new ABCVersion
* @param major Major version
* @param minor Minor version
*/
public ABCVersion(int major, int minor) {
this.major = major;
this.minor = minor;
}
/**
* Compares ABCVersion with another ABCVersion
* @param o the object to be compared.
* @return Negative number if this version is lower, 0 if versions are equal, positive number if this version is higher
*/
@Override
public int compareTo(ABCVersion o) {
if (major != o.major) {
@@ -38,11 +61,19 @@ public class ABCVersion implements Comparable<ABCVersion> {
return minor - o.minor;
}
/**
* Returns string representation of ABCVersion
* @return
*/
@Override
public String toString() {
return "" + major + "." + minor;
}
/**
* Returns hash code of ABCVersion
* @return
*/
@Override
public int hashCode() {
int hash = 7;
@@ -51,6 +82,11 @@ public class ABCVersion implements Comparable<ABCVersion> {
return hash;
}
/**
* Equals method
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Target ABC version
* Target ABC version.
*
* @author JPEXS
*/
@@ -30,13 +30,34 @@ import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface ABCVersionRequirements {
/**
* Minimum minor version.
* @return
*/
int minMinor() default 0;
/**
* Maximum minor version.
* @return
*/
int maxMinor() default 0;
/**
* Maximum major version.
* @return
*/
int maxMajor() default 0;
/**
* Minimum major version.
* @return
*/
int minMajor() default 0;
/**
* Exact minor version.
* @return
*/
int exactMinor() default 0;
}
@@ -21,56 +21,90 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
import com.jpexs.decompiler.flash.abc.avm2.CodeStats;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.*;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.ScopeStack;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
* AVM2 local data.
* @author JPEXS
*/
public class AVM2LocalData extends BaseLocalData {
/**
* Whether the method is static
*/
public Boolean isStatic;
/**
* Current class index
*/
public Integer classIndex;
/**
* Local registers values
*/
public HashMap<Integer, GraphTargetItem> localRegs;
/**
* Scope stack
*/
public ScopeStack scopeStack;
/**
* Local scope stack
*/
public ScopeStack localScopeStack;
/**
* Current method body
*/
public MethodBody methodBody;
/**
* Current call stack
*/
public List<MethodBody> callStack;
/**
* Current ABC file
*/
public ABC abc;
/**
* ABCIndexing
*/
public AbcIndexing abcIndex;
/**
* Local register names
*/
public HashMap<Integer, String> localRegNames;
/**
* Local register types
*/
public HashMap<Integer, GraphTargetItem> localRegTypes;
/**
* Fully qualified names
*/
public List<DottedChain> fullyQualifiedNames;
/**
* Parsed exceptions
*/
public List<ABCException> parsedExceptions = new ArrayList<>();
/**
* Parsed exception ids
*/
public List<Integer> parsedExceptionIds = new ArrayList<>();
//public Map<Integer, List<Integer>> finallyJumps;
/**
* Mapped jumps from pushbyte xx part to apropriate lookupswitch branch
*/
@@ -81,56 +115,114 @@ public class AVM2LocalData extends BaseLocalData {
*/
public Map<GraphPart, Integer> finallyJumpsToFinallyIndex = new HashMap<>();
/**
* Mapping from finally exception index to default part
*/
public Map<Integer, GraphPart> finallyIndexToDefaultGraphPart = new HashMap<>();
//exception index => switchPart
/**
* Mapping from exception index to switch part
*/
public Map<Integer, GraphPart> ignoredSwitches = new HashMap<>();
/**
* exception index -> switch defaultPart
* Mapping from exception index to switch defaultPart
*/
public Map<Integer, GraphPart> defaultParts = new HashMap<>();
/**
* Mapping from finally index to register index
*/
public Map<Integer, Integer> switchedRegs = new HashMap<>();
/**
* Mapping from finally index push default part
*/
public Map<Integer, GraphPart> pushDefaultPart = new HashMap<>();
/**
* Mapping from finally index to finally kind
* See AVM2Graph.FINALLY_KIND_* constants.
*/
public Map<Integer, Integer> finallyKinds = new HashMap<>();
/**
* exception index -> switch throw part
* Mapping from finally exception index to switch throw part
*/
public Map<Integer, GraphPart> finallyThrowParts = new HashMap<>();
/**
* Mapping from finally exception index to target part
*/
public Map<Integer, GraphPart> finallyTargetParts = new HashMap<>();
//switchedPart -> index of nextpart
/**
* Mapping from switchedPart to index of nextpart
*/
public Map<GraphPart, Integer> defaultWays = new HashMap<>();
/**
* Current script index
*/
public Integer scriptIndex;
/**
* Local registers assignment ips. Maps register index to ip where it was assigned.
*/
public HashMap<Integer, Integer> localRegAssignmentIps;
/**
* Current instruction pointer
*/
public Integer ip;
/**
* AVM2 code
*/
public AVM2Code code;
/**
* Whether this has default to primitive
*/
public boolean thisHasDefaultToPrimitive;
/**
* Mapping from setLocal position to getLocal positions
*/
public Map<Integer, Set<Integer>> setLocalPosToGetLocalPos = new HashMap<>();
/**
* Code stats
*/
public CodeStats codeStats;
/**
* Indices of finally exceptions with double push
*/
public Set<Integer> finallyIndicesWithDoublePush = new HashSet<>();
/**
* Whether we are in get loops
*/
public boolean inGetLoops = false;
/**
* Set of seen methods
*/
public Set<Integer> seenMethods = new HashSet<>();
/**
* Constructs a new AVM2LocalData
*/
public AVM2LocalData() {
}
/**
* Returns set of getLocal positions for given setLocal position
* @param setLocalPos SetLocal position
* @return Set of getLocal positions
*/
public Set<Integer> getSetLocalUsages(int setLocalPos) {
if (setLocalPosToGetLocalPos == null) {
return new HashSet<>();
@@ -141,6 +233,10 @@ public class AVM2LocalData extends BaseLocalData {
return setLocalPosToGetLocalPos.get(setLocalPos);
}
/**
* Constructs a new AVM2LocalData from another AVM2LocalData
* @param localData Another AVM2LocalData
*/
public AVM2LocalData(AVM2LocalData localData) {
allSwitchParts = localData.allSwitchParts;
isStatic = localData.isStatic;
@@ -181,18 +277,34 @@ public class AVM2LocalData extends BaseLocalData {
seenMethods = localData.seenMethods;
}
/**
* Returns constant pool
* @return Constant pool
*/
public AVM2ConstantPool getConstants() {
return abc.constants;
}
/**
* Returns ABC method infos
* @return List of MethodInfo
*/
public List<MethodInfo> getMethodInfo() {
return abc.method_info;
}
/**
* Returns ABC instance infos
* @return List of InstanceInfo
*/
public List<InstanceInfo> getInstanceInfo() {
return abc.instance_info;
}
/**
* Returns ABC script infos
* @return List of ScriptInfo
*/
public List<ScriptInfo> getScriptInfo() {
return abc.script_info;
}
@@ -17,36 +17,64 @@
package com.jpexs.decompiler.flash.abc;
import com.jpexs.decompiler.graph.DottedChain;
import java.io.Serializable;
import java.util.Objects;
/**
*
* Class path
* @author JPEXS
*/
public class ClassPath implements Serializable {
/**
* Package name
*/
public final DottedChain packageStr;
/**
* Class name
*/
public final String className;
/**
* Namespace suffix
*/
public final String namespaceSuffix;
/**
* Constructs a new class path
* @param packageStr Package name
* @param className Class name
* @param namespaceSuffix Namespace suffix
*/
public ClassPath(DottedChain packageStr, String className, String namespaceSuffix) {
this.packageStr = packageStr == null ? DottedChain.TOPLEVEL : packageStr;
this.className = className;
this.namespaceSuffix = namespaceSuffix;
}
/**
* To string
* @return String
*/
@Override
public String toString() {
return packageStr.add(className, namespaceSuffix).toPrintableString(true);
}
/**
* To raw string
* @return Raw string
*/
public String toRawString() {
return packageStr.add(className, namespaceSuffix).toRawString();
}
/**
* Hash code
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 3;
@@ -56,6 +84,11 @@ public class ClassPath implements Serializable {
return hash;
}
/**
* Equals
* @param obj
* @return True if equals
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -21,30 +21,61 @@ import java.io.InputStream;
import java.io.OutputStream;
/**
*
* Output stream that copies data to another output stream.
* @author JPEXS
*/
public class CopyOutputStream extends OutputStream {
/**
* Output stream to copy data to.
*/
private final OutputStream os;
/**
* Input stream to compare data with.
*/
private final InputStream is;
/**
* Position in the output stream.
*/
private long pos = 0;
/**
* Size of the temporary buffer.
*/
private final int TEMPSIZE = 5;
/**
* Temporary buffer.
*/
private final int[] temp = new int[TEMPSIZE];
/**
* Position in the temporary buffer.
*/
private int tempPos = 0;
/**
* Number of bytes to ignore at the beginning.
*/
public int ignoreFirst = 0;
/**
* Constructs a new CopyOutputStream.
* @param os Output stream to copy data to
* @param is Input stream to compare data with
*/
public CopyOutputStream(OutputStream os, InputStream is) {
this.os = os;
this.is = is;
}
/**
* Writes a byte to the output stream.
* @param b Byte to write
* @throws IOException If an I/O error occurs
*/
@Override
public void write(int b) throws IOException {
temp[tempPos] = b;
@@ -19,11 +19,15 @@ package com.jpexs.decompiler.flash.abc;
import com.jpexs.helpers.Helper;
/**
*
* Streams are not the same exception.
* @author JPEXS
*/
public class NotSameException extends RuntimeException {
/**
* Constructs a new NotSameException with specified position.
* @param pos Position
*/
public NotSameException(long pos) {
super("Streams are not the same at pos " + Helper.formatHex((int) pos, 8));
}
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash.abc;
/**
*
* Rename type enum.
* @author JPEXS
*/
public enum RenameType {
@@ -16,31 +16,20 @@
*/
package com.jpexs.decompiler.flash.abc;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.ConvertException;
import com.jpexs.decompiler.flash.abc.avm2.OffsetUpdater;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions;
import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugFileIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugLineIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
import com.jpexs.decompiler.flash.abc.avm2.model.GlobalAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.ConvertData;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.*;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
@@ -58,62 +47,91 @@ import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* Script pack class.
* A script pack is a collection of traits that are in the same script.
* It can be a simple script pack (contains only one externally visible definition)
* or a compound script pack (contains more than one externally visible definitions).
* @author JPEXS
*/
public class ScriptPack extends AS3ClassTreeItem {
/**
* Logger
*/
private static final Logger logger = Logger.getLogger(ScriptPack.class.getName());
/**
* ABC file
*/
public final ABC abc;
/**
* All ABC files
*/
public List<ABC> allABCs;
/**
* Script index
*/
public final int scriptIndex;
/**
* Trait indices
*/
public final List<Integer> traitIndices;
/**
* Class path
*/
private final ClassPath path;
/**
* Is the scriptpack simple? ScriptPack can be either simple or compound.
* Whether the scriptpack is simple. ScriptPack can be either simple or compound.
* Compound = Contains more than one externally visible definitions.
*/
public boolean isSimple = false;
/**
* Whether the script initializer is empty
*/
public boolean scriptInitializerIsEmpty = false;
/**
* Gets openable.
* @return Openable
*/
@Override
public Openable getOpenable() {
return abc.getOpenable();
}
/**
* Gets class path.
* @return Class path
*/
public ClassPath getClassPath() {
return path;
}
/**
* Constructs a new script pack.
* @param path Class path
* @param abc ABC file
* @param allAbcs All ABC files
* @param scriptIndex Script index
* @param traitIndices Trait indices
*/
public ScriptPack(ClassPath path, ABC abc, List<ABC> allAbcs, int scriptIndex, List<Integer> traitIndices) {
super(path.className, path.namespaceSuffix, path);
this.abc = abc;
@@ -123,6 +141,10 @@ public class ScriptPack extends AS3ClassTreeItem {
this.allABCs = allAbcs;
}
/**
* Gets path package.
* @return Dotted chain
*/
public DottedChain getPathPackage() {
DottedChain packageName = DottedChain.TOPLEVEL;
for (int t : traitIndices) {
@@ -135,6 +157,10 @@ public class ScriptPack extends AS3ClassTreeItem {
return packageName;
}
/**
* Gets public trait.
* @return Trait or null if not found
*/
public Trait getPublicTrait() {
for (int t : traitIndices) {
Multiname name = abc.script_info.get(scriptIndex).traits.traits.get(t).getName(abc);
@@ -146,6 +172,10 @@ public class ScriptPack extends AS3ClassTreeItem {
return null;
}
/**
* Gets path script name.
* @return Script name
*/
public String getPathScriptName() {
String scriptName = "script_" + scriptIndex;
for (int t : traitIndices) {
@@ -158,6 +188,12 @@ public class ScriptPack extends AS3ClassTreeItem {
return scriptName;
}
/**
* Gets export file.
* @param directory Directory
* @param extension Extension including dot
* @return File
*/
public File getExportFile(String directory, String extension) {
String scriptName = getPathScriptName();
@@ -167,6 +203,12 @@ public class ScriptPack extends AS3ClassTreeItem {
return new File(fileName);
}
/**
* Gets export file.
* @param directory Directory
* @param exportSettings Export settings
* @return File
*/
public File getExportFile(String directory, ScriptExportSettings exportSettings) {
if (exportSettings.singleFile) {
return null;
@@ -175,6 +217,16 @@ public class ScriptPack extends AS3ClassTreeItem {
return getExportFile(directory, exportSettings.getFileExtension());
}
/**
* Converts the script pack.
* @param abcIndex Abc indexing
* @param writer Writer
* @param traits Traits
* @param convertData Convert data
* @param exportMode Export mode
* @param parallel Parallel
* @throws InterruptedException
*/
public void convert(AbcIndexing abcIndex, final NulWriter writer, final List<Trait> traits, final ConvertData convertData, final ScriptExportMode exportMode, final boolean parallel) throws InterruptedException {
int sinit_index = abc.script_info.get(scriptIndex).init_index;
@@ -208,6 +260,16 @@ public class ScriptPack extends AS3ClassTreeItem {
}
}
/**
* Append script to writer.
* @param abcIndex Abc indexing
* @param writer Writer
* @param traits Traits
* @param convertData Convert data
* @param exportMode Export mode
* @param parallel Parallel
* @throws InterruptedException
*/
private void appendTo(AbcIndexing abcIndex, GraphTextWriter writer, List<Trait> traits, ConvertData convertData, ScriptExportMode exportMode, boolean parallel) throws InterruptedException {
boolean first = true;
//script initializer
@@ -269,6 +331,17 @@ public class ScriptPack extends AS3ClassTreeItem {
}
}
/**
* Converts the script pack to source.
* @param abcIndex Abc indexing
* @param writer Writer
* @param traits Traits
* @param convertData Convert data
* @param exportMode Export mode
* @param parallel Parallel
* @param ignoreFrameScripts Whether to ignore frame scripts
* @throws InterruptedException
*/
public void toSource(AbcIndexing abcIndex, GraphTextWriter writer, final List<Trait> traits, final ConvertData convertData, final ScriptExportMode exportMode, final boolean parallel, boolean ignoreFrameScripts) throws InterruptedException {
writer.suspendMeasure();
int timeout = Configuration.decompilationTimeoutFile.get();
@@ -310,6 +383,16 @@ public class ScriptPack extends AS3ClassTreeItem {
appendTo(abcIndex, writer, traits, convertData, exportMode, parallel);
}
/**
* Exports the script pack.
* @param abcIndex Abc indexing
* @param file File
* @param exportSettings Export settings
* @param parallel Parallel
* @return File
* @throws IOException
* @throws InterruptedException
*/
public File export(AbcIndexing abcIndex, File file, ScriptExportSettings exportSettings, boolean parallel) throws IOException, InterruptedException {
if (!exportSettings.singleFile) {
if (file.exists() && !Configuration.overwriteExistingFiles.get()) {
@@ -336,6 +419,10 @@ public class ScriptPack extends AS3ClassTreeItem {
return file;
}
/**
* Hash code.
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 7;
@@ -345,6 +432,11 @@ public class ScriptPack extends AS3ClassTreeItem {
return hash;
}
/**
* Equals.
* @param obj Object
* @return True if equals
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -363,6 +455,10 @@ public class ScriptPack extends AS3ClassTreeItem {
return Objects.equals(path, other.path);
}
/**
* Gets modified flag.
* @return True if modified
*/
@Override
public boolean isModified() {
if (scriptIndex >= abc.script_info.size()) {
@@ -371,6 +467,9 @@ public class ScriptPack extends AS3ClassTreeItem {
return abc.script_info.get(scriptIndex).isModified();
}
/**
* Clears modified flag.
*/
public void clearModified() {
if (scriptIndex >= abc.script_info.size()) {
return;
@@ -378,10 +477,21 @@ public class ScriptPack extends AS3ClassTreeItem {
abc.script_info.get(scriptIndex).setModified(false);
}
/**
* Label with address.
*/
private class Label {
/**
* Address
*/
public long addr;
/**
* Constructs a new label.
* @param addr Address
*/
public Label(long addr) {
this.addr = addr;
}
@@ -398,7 +508,12 @@ public class ScriptPack extends AS3ClassTreeItem {
public void injectDebugInfo(File directoryPath) {
injectDebugInfo(directoryPath, "main");
}
/**
* Injects debugfile, debugline instructions into the code.
* @param directoryPath Directory path
* @param swfHash SWF identifier
*/
public void injectDebugInfo(File directoryPath, String swfHash) {
Map<Integer, Map<Integer, Integer>> bodyToPosToLine = new HashMap<>();
Map<Integer, Map<Integer, Integer>> bodyLineToPos = new HashMap<>();
@@ -694,6 +809,11 @@ public class ScriptPack extends AS3ClassTreeItem {
((Tag) abc.parentTag).setModified(true);
}
/**
* Injects P-code debugfile, debugline instructions into the code.
* @param abcIndex Abc indexing
* @param swfHash SWF identifier
*/
public void injectPCodeDebugInfo(int abcIndex, String swfHash) {
Map<Integer, String> bodyToIdentifier = new HashMap<>();
@@ -779,6 +899,10 @@ public class ScriptPack extends AS3ClassTreeItem {
((Tag) abc.parentTag).setModified(true);
}
/**
* Gets method ids.
* @param methodInfos Result list of MethodIds
*/
public void getMethodInfos(List<MethodId> methodInfos) {
int script_init = abc.script_info.get(scriptIndex).init_index;
methodInfos.add(new MethodId(GraphTextWriter.TRAIT_SCRIPT_INITIALIZER, -1, script_init));
@@ -790,6 +914,11 @@ public class ScriptPack extends AS3ClassTreeItem {
}
}
/**
* Deletes the script pack.
* @param abc ABC file
* @param d Whether to delete
*/
public void delete(ABC abc, boolean d) {
ScriptInfo si = abc.script_info.get(scriptIndex);
if (isSimple) {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -21,42 +21,76 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.RenameType;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.regex.Pattern;
/**
*
* AVM2 deobfuscation.
* @author JPEXS
*/
public class AVM2Deobfuscation {
/**
* Default size of random word.
*/
private static final int DEFAULT_FOO_SIZE = 10;
/**
* Valid characters for first character of name.
*/
public static final String VALID_FIRST_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
/**
* Valid characters for next characters of name.
*/
public static final String VALID_NEXT_CHARACTERS = VALID_FIRST_CHARACTERS + "0123456789";
/**
* Valid characters for namespace.
*/
public static final String VALID_NS_CHARACTERS = ".:";
/**
* SWF file.
*/
private final SWF swf;
/**
* AVM2 constant pool.
*/
private final AVM2ConstantPool constants;
/**
* Usage types count.
*/
private final Map<String, Integer> usageTypesCount = new HashMap<>();
/**
* Flash proxy namespace.
*/
public static final DottedChain FLASH_PROXY = new DottedChain(new String[]{"flash", "utils", "flash_proxy"});
/**
* Built-in namespace.
*/
public static final DottedChain BUILTIN = new DottedChain(new String[]{"-"});
/**
* Constructs AVM2 deobfuscation.
* @param swf SWF
* @param constants AVM2 constant pool
*/
public AVM2Deobfuscation(SWF swf, AVM2ConstantPool constants) {
this.swf = swf;
this.constants = constants;
}
/**
* Checks if string is valid namespace part.
* @param s String
* @return True if string is valid namespace part
*/
private boolean isValidNSPart(String s) {
boolean isValid = true;
if (IdentifiersDeobfuscation.isReservedWord2(s)) {
@@ -80,6 +114,11 @@ public class AVM2Deobfuscation {
return isValid;
}
/**
* Gets built-in namespace.
* @param ns Namespace
* @return Built-in namespace
*/
public DottedChain builtInNs(String ns) {
if (ns == null) {
return null;
@@ -93,6 +132,16 @@ public class AVM2Deobfuscation {
return null;
}
/**
* Generates random string.
* @param deobfuscated Deobfuscated names
* @param orig Original name
* @param firstUppercase First uppercase
* @param usageType Usage type
* @param renameType Rename type
* @param autoAdd Auto add
* @return Random string
*/
private String fooString(HashMap<DottedChain, DottedChain> deobfuscated, String orig, boolean firstUppercase, String usageType, RenameType renameType, boolean autoAdd) {
if (usageType == null) {
usageType = "name";
@@ -122,6 +171,15 @@ public class AVM2Deobfuscation {
return ret;
}
/**
* Deobfuscates package name.
* @param stringUsageTypes String usage types
* @param stringUsages String usages
* @param namesMap Names map
* @param strIndex String index
* @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) {
if (strIndex <= 0) {
return strIndex;
@@ -160,6 +218,17 @@ public class AVM2Deobfuscation {
return strIndex;
}
/**
* Deobfuscates name.
* @param stringUsageTypes String usage types
* @param stringUsages String usages
* @param namespaceUsages Namespace usages
* @param namesMap Names map
* @param strIndex String index
* @param firstUppercase First uppercase
* @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) {
if (strIndex <= 0) {
return strIndex;
@@ -18,27 +18,42 @@ package com.jpexs.decompiler.flash.abc.avm2;
import com.jpexs.decompiler.flash.FinalProcessLocalData;
import com.jpexs.decompiler.graph.Loop;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
* AVM2 final process local data.
* @author JPEXS
*/
public class AVM2FinalProcessLocalData extends FinalProcessLocalData {
/**
* Local register names - register number to name mapping.
*/
public HashMap<Integer, String> localRegNames;
/**
* Set local position to get local position mapping.
*/
public Map<Integer, Set<Integer>> setLocalPosToGetLocalPos = new HashMap<>();
/**
* Constructs AVM2 final process local data.
* @param loops List of loops
* @param localRegNames Local register names - register number to name mapping
* @param setLocalPosToGetLocalPos Set local position to get local position mapping
*/
public AVM2FinalProcessLocalData(List<Loop> loops, HashMap<Integer, String> localRegNames, Map<Integer, Set<Integer>> setLocalPosToGetLocalPos) {
super(loops);
this.localRegNames = localRegNames;
this.setLocalPosToGetLocalPos = setLocalPosToGetLocalPos;
}
/**
* Gets getlocal positions for setlocal position.
* @param setLocalPos Setlocal position
* @return Set of getlocal positions
*/
public Set<Integer> getSetLocalUsages(int setLocalPos) {
if (setLocalPosToGetLocalPos == null) {
return new HashSet<>();
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash.abc.avm2;
/**
*
* Type of AVM2 runtime enum.
* @author JPEXS
*/
public enum AVM2Runtime {
@@ -17,17 +17,32 @@
package com.jpexs.decompiler.flash.abc.avm2;
/**
*
* Info of AVM2 runtime.
* @author JPEXS
*/
public class AVM2RuntimeInfo {
/**
* AVM2 runtime.
*/
public AVM2Runtime runtime;
/**
* AVM2 version.
*/
public int version;
/**
* Debug flag.
*/
public boolean debug;
/**
* Constructs AVM2 runtime info.
* @param runtime AVM2 runtime
* @param version AVM2 version
* @param debug Debug flag
*/
public AVM2RuntimeInfo(AVM2Runtime runtime, int version, boolean debug) {
this.runtime = runtime;
this.version = version;
@@ -20,30 +20,59 @@ import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
*
* Code statistics.
* @author JPEXS
*/
public class CodeStats {
/**
* Maximum stack size
*/
public int maxstack = 0;
/**
* Maximum scope size
*/
public int maxscope = 0;
/**
* Number of local registers
*/
public int maxlocal = 1;
/**
* Initial scope size
*/
public int initscope = 0;
/**
* Has set_dxns instruction
*/
public boolean has_set_dxns = false;
/**
* Has activation instruction
*/
public boolean has_activation = false;
/**
* Instruction statistics
*/
public InstructionStats[] instructionStats;
/**
* Converts statistics to string.
* @param writer Writer
* @param abc ABC
* @param fullyQualifiedNames Fully qualified names
* @return Writer
*/
public GraphTextWriter toString(GraphTextWriter writer, ABC abc, List<DottedChain> fullyQualifiedNames) {
writer.appendNoHilight("Stats: maxstack=" + maxstack + ", maxscope=" + maxscope + ", maxlocal=" + maxlocal).newLine();
int i = 0;
@@ -59,9 +88,16 @@ public class CodeStats {
return writer;
}
/**
* Constructs code statistics.
*/
public CodeStats() {
}
/**
* Constructs code statistics.
* @param code AVM2 code
*/
public CodeStats(AVM2Code code) {
instructionStats = new InstructionStats[code.code.size()];
for (int i = 0; i < code.code.size(); i++) {
@@ -17,13 +17,22 @@
package com.jpexs.decompiler.flash.abc.avm2;
/**
*
* Convert exception.
* @author JPEXS
*/
public class ConvertException extends RuntimeException {
/**
* Line number
*/
public int line;
/**
* Constructs convert exception.
* @param s Message
* @param line Line number
*/
public ConvertException(String s, int line) {
super(s + " on line " + line);
this.line = line;
@@ -18,18 +18,30 @@ package com.jpexs.decompiler.flash.abc.avm2;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import java.util.List;
/**
*
* Conversion output.
* @author JPEXS
*/
public class ConvertOutput {
/**
* Translate stack
*/
public TranslateStack stack;
/**
* Output
*/
public List<GraphTargetItem> output;
/**
* Constructs conversion output.
* @param stack Translate stack
* @param output Output
*/
public ConvertOutput(TranslateStack stack, List<GraphTargetItem> output) {
this.stack = stack;
this.output = output;
@@ -20,17 +20,28 @@ import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
/**
*
* Translate stack that counts the number of items pushed and not popped.
* @author JPEXS
*/
public class FixItemCounterTranslateStack extends TranslateStack {
/**
*
*/
private int fixItemCount = Integer.MAX_VALUE;
/**
* Constructs a new FixItemCounterTranslateStack
* @param path Path
*/
public FixItemCounterTranslateStack(String path) {
super(null); //null path => do not add PushItems
}
/**
* Pops an item from the stack
* @return The popped item
*/
@Override
public GraphTargetItem pop() {
GraphTargetItem result = super.pop();
@@ -41,6 +52,11 @@ public class FixItemCounterTranslateStack extends TranslateStack {
return result;
}
/**
* Removes the element at the specified index
* @param index the index of the element to be removed
* @return The removed element
*/
@Override
public synchronized GraphTargetItem remove(int index) {
if (index < fixItemCount) {
@@ -49,10 +65,18 @@ public class FixItemCounterTranslateStack extends TranslateStack {
return super.remove(index);
}
/**
* All items were never popped.
* @return True if all items were never popped
*/
public boolean allItemsFixed() {
return size() <= fixItemCount;
}
/**
* Gets the number of items that were never popped
* @return The number of items that were never popped
*/
public int getFixItemCount() {
return fixItemCount;
}
@@ -19,23 +19,45 @@ package com.jpexs.decompiler.flash.abc.avm2;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
/**
*
* Instruction statistics.
* @author JPEXS
*/
public class InstructionStats {
/**
* Whether the instruction has been seen while walking the instruction list
*/
public boolean seen = false;
/**
* Stack position before the instruction
*/
public int stackpos = 0;
/**
* Scope position before the instruction
*/
public int scopepos = 0;
/**
* Stack position after the instruction
*/
public int stackpos_after = 0;
/**
* Scope position after the instruction
*/
public int scopepos_after = 0;
/**
* Instruction
*/
public AVM2Instruction ins;
/**
* Constructs a new InstructionStats object
* @param ins Instruction
*/
public InstructionStats(AVM2Instruction ins) {
this.ins = ins;
}
@@ -17,12 +17,15 @@
package com.jpexs.decompiler.flash.abc.avm2;
/**
*
* Invalid instruction arguments exception.
* @author JPEXS
*/
public class InvalidInstructionArguments extends RuntimeException {
public class InvalidInstructionArgumentsException extends RuntimeException {
public InvalidInstructionArguments() {
/**
* Constructs new InvalidInstructionArgumentsException
*/
public InvalidInstructionArgumentsException() {
super("Invalid method arguments");
}
}
@@ -20,30 +20,61 @@ import java.util.HashMap;
import java.util.Stack;
/**
*
* Local data area for AVM2 method execution.
* @author JPEXS
*/
public class LocalDataArea {
/**
* Name of the method that is currently being executed.
*/
public String methodName;
/**
* Operand stack.
*/
public Stack<Object> operandStack = new Stack<>();
/**
* Scope stack.
*/
public Stack<Object> scopeStack = new Stack<>();
/**
* Local registers values - maps register index to value.
*/
public HashMap<Integer, Object> localRegisters = new HashMap<>();
/**
* Offset of jump
*/
public Long jump;
/**
* Return value of the method.
*/
public Object returnValue;
/**
* Runtime info.
*/
public AVM2RuntimeInfo runtimeInfo;
/**
* Domain memory.
*/
private byte[] domainMemory;
/**
* Constructs a new LocalDataArea.
*/
public LocalDataArea() {
}
/**
* Gets domain memory.
* @return Domain memory bytes.
*/
public byte[] getDomainMemory() {
if (domainMemory == null) {
domainMemory = new byte[1024]; // in flash player this is the default size
@@ -52,14 +83,25 @@ public class LocalDataArea {
return domainMemory;
}
/**
* Gets runtime.
* @return Runtime.
*/
public AVM2Runtime getRuntime() {
return runtimeInfo == null ? AVM2Runtime.UNKNOWN : runtimeInfo.runtime;
}
/**
* Gets debug flag.
* @return True if debug flag is set.
*/
public boolean isDebug() {
return runtimeInfo != null && runtimeInfo.debug;
}
/**
* Clears the local data area.
*/
public void clear() {
operandStack.clear();
scopeStack.clear();
@@ -16,8 +16,12 @@
*/
package com.jpexs.decompiler.flash.abc.avm2;
/**
* Number context.
*/
public class NumberContext {
//Rounding modes
public static final int ROUND_CEILING = 0;
public static final int ROUND_UP = 1;
public static final int ROUND_HALF_UP = 2;
@@ -26,28 +30,54 @@ public class NumberContext {
public static final int ROUND_DOWN = 5;
public static final int ROUND_FLOOR = 6;
//Usage modes
public static final int USE_NUMBER = 0;
public static final int USE_DECIMAL = 1;
public static final int USE_DOUBLE = 2;
public static final int USE_INT = 3;
public static final int USE_UINT = 4;
/**
* Usage of the number.
*/
private int usage = USE_NUMBER;
/**
* Precision of the number.
*/
private int precision = 34;
/**
* Rounding of the number.
*/
private int rounding = ROUND_HALF_EVEN;
/**
* Creates a new number context.
*
* @param usage Usage of the number.
* @param precision Precision of the number.
* @param rounding Rounding of the number.
*/
public NumberContext(int usage, int precision, int rounding) {
this.usage = usage;
this.precision = precision;
this.rounding = rounding;
}
/**
* Creates a new number context.
*
* @param param Parameter.
*/
public NumberContext(int param) {
this.usage = param & 7;
this.rounding = (param >> 3) & 7;
this.precision = param >> 6;
}
/**
* Sets the usage of the number.
* @param usage
*/
public void setUsage(int usage) {
if (usage > 6 || usage < 0) {
throw new IllegalArgumentException("Invalid usage value :" + usage);
@@ -55,14 +85,26 @@ public class NumberContext {
this.usage = usage;
}
/**
* Gets the usage of the number.
* @return Usage of the number.
*/
public int getUsage() {
return usage;
}
/**
* Gets the precision of the number.
* @return Precision of the number.
*/
public int getPrecision() {
return precision;
}
/**
* Sets the precision of the number.
* @param precision Precision of the number.
*/
public void setPrecision(int precision) {
if (precision > 34) {
throw new IllegalArgumentException("Maximum value of precision is 34");
@@ -70,6 +112,10 @@ public class NumberContext {
this.precision = precision;
}
/**
* Converts the number context to a parameter.
* @return Parameter.
*/
public int toParam() {
int ret = usage;
if (usage == USE_NUMBER || usage == USE_DECIMAL) {
@@ -17,12 +17,25 @@
package com.jpexs.decompiler.flash.abc.avm2;
/**
*
* Offset updater interface.
* Used to update offsets in instructions and operands.
* @author JPEXS
*/
public interface OffsetUpdater {
/**
* Updates instruction offset.
* @param addr Address of the instruction
* @return New address of the instruction
*/
public long updateInstructionOffset(long addr);
/**
* Updates operand offset.
* @param jumpAddr Address of the jump instruction
* @param targetAddress Address of the target instruction
* @param offset Operand (offset) of the jump instruction
* @return New operand offset
*/
public int updateOperandOffset(long jumpAddr, long targetAddress, int offset);
}
@@ -17,14 +17,21 @@
package com.jpexs.decompiler.flash.abc.avm2;
/**
*
* Exception thrown when unknown instruction code is found.
* @author JPEXS
*/
public class UnknownInstructionCode extends RuntimeException {
public class UnknownInstructionCodeException extends RuntimeException {
/**
* Instruction code
*/
public int code;
public UnknownInstructionCode(int code) {
/**
* Constructs a new UnknownInstructionCodeException.
* @param code Instruction code
*/
public UnknownInstructionCodeException(int code) {
super("Unknown instruction code: 0x" + Integer.toHexString(code));
this.code = code;
}
@@ -36,11 +36,8 @@ import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.NotCompileTimeItem;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -48,7 +45,7 @@ import java.util.Map;
/**
*
* AVM2 Deobfuscator removing single get / set registers
* AVM2 Deobfuscator removing single get / set registers.
*
* Example: getlocal_1, getlocal_2, (kill 1), (kill 2), setlocal_2, setlocal_1
*
@@ -56,12 +53,32 @@ import java.util.Map;
*/
public class AVM2DeobfuscatorGetSet extends SWFDecompilerAdapter {
/**
* Undefined item
*/
private static final UndefinedAVM2Item UNDEFINED_ITEM = new UndefinedAVM2Item(null, null);
/**
* Not compile time undefined item
*/
private static final NotCompileTimeItem NOT_COMPILE_TIME_UNDEFINED_ITEM = new NotCompileTimeItem(null, null, UNDEFINED_ITEM);
/**
* Execution limit
*/
private final int executionLimit = 30000;
/**
* Remove obfuscation get sets
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param body Method body
* @param inlineIns Inline instructions
* @return True if removed
* @throws InterruptedException
*/
protected boolean removeObfuscationGetSets(int classIndex, boolean isStatic, int scriptIndex, ABC abc, MethodBody body, List<AVM2Instruction> inlineIns) throws InterruptedException {
AVM2Code code = body.getCode();
if (code.code.isEmpty()) {
@@ -99,10 +116,26 @@ public class AVM2DeobfuscatorGetSet extends SWFDecompilerAdapter {
return false;
}
/**
* Remove unreachable instructions.
* @param code AVM2 code
* @param body Method body
* @throws InterruptedException
*/
protected void removeUnreachableInstructions(AVM2Code code, MethodBody body) throws InterruptedException {
code.removeDeadCode(body);
}
/**
* Create new local data.
* @param scriptIndex Script index
* @param abc ABC
* @param cpool Constant pool
* @param body Method body
* @param isStatic Is static
* @param classIndex Class index
* @return AVM2 local data
*/
protected AVM2LocalData newLocalData(int scriptIndex, ABC abc, AVM2ConstantPool cpool, MethodBody body, boolean isStatic, int classIndex) {
AVM2LocalData localData = new AVM2LocalData();
localData.isStatic = isStatic;
@@ -124,6 +157,12 @@ public class AVM2DeobfuscatorGetSet extends SWFDecompilerAdapter {
return localData;
}
/**
* Initialize local registers.
* @param localData AVM2 local data
* @param localReservedCount Local reserved count
* @param maxRegs Maximum registers
*/
protected void initLocalRegs(AVM2LocalData localData, int localReservedCount, int maxRegs) {
for (int i = 0; i < localReservedCount; i++) {
localData.localRegs.put(i, NOT_COMPILE_TIME_UNDEFINED_ITEM);
@@ -133,6 +172,15 @@ public class AVM2DeobfuscatorGetSet extends SWFDecompilerAdapter {
}
}
/**
* Execute instructions.
* @param body Method body
* @param code AVM2 code
* @param localData AVM2 local data
* @param idx Index
* @param endIdx End index
* @throws InterruptedException
*/
private void executeInstructions(MethodBody body, AVM2Code code, AVM2LocalData localData, int idx, int endIdx) throws InterruptedException {
List<GraphTargetItem> output = new ArrayList<>();
@@ -217,6 +265,18 @@ public class AVM2DeobfuscatorGetSet extends SWFDecompilerAdapter {
}
}
/**
* AVM2 code remove traps.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -25,12 +25,13 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Stub for deobfuscator merging jump parts
* Stub for deobfuscator merging jump parts.
*
* @author JPEXS
*/
@@ -101,6 +102,19 @@ A: jump B
C: blk_4
*/
/**
* Removes dead code and merges jump parts.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -165,6 +179,12 @@ C: blk_4
new AVM2DeobfuscatorJumps().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
}
/**
* Gets real refs.
* @param refs Refs
* @param ip IP
* @return Real refs
*/
private int realRefs(Map<Integer, List<Integer>> refs, int ip) {
int refCount = 0;
for (int r : refs.get(ip)) {
@@ -25,6 +25,7 @@ import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -38,6 +39,18 @@ import java.util.Map;
*/
public class AVM2DeobfuscatorJumps extends SWFDecompilerAdapter {
/**
* Removes jumps/ifs targeting other jumps.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
@@ -37,20 +37,10 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -64,6 +54,11 @@ import java.util.logging.Logger;
*/
public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
/**
* Gets registers used in the code.
* @param code AVM2 code
* @return Set of registers
*/
private Set<Integer> getRegisters(AVM2Code code) {
Set<Integer> regs = new HashSet<>();
for (AVM2Instruction ins : code.code) {
@@ -88,6 +83,18 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
return regs;
}
/**
* Removes single assigned local registers.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
//System.err.println("regdeo:" + path);
@@ -155,6 +162,20 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
//System.err.println("/deo");
}
/**
* Replaces single use registers.
* @param singleRegisters Single registers
* @param setInss Set instructions
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param cpool Constant pool
* @param trait Trait
* @param minfo Method info
* @param body Method body
* @throws InterruptedException
*/
private void replaceSingleUseRegisters(Map<Integer, GraphTargetItem> singleRegisters, List<AVM2Instruction> setInss, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -182,6 +203,16 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
}
}
/**
* Gets first register id which has setter.
* @param assignment Assignment
* @param body Method body
* @param abc ABC
* @param ignoredRegisters Ignored registers
* @param ignoredGets Ignored gets
* @return first register id
* @throws InterruptedException
*/
private int getFirstRegisterSetter(Reference<AVM2Instruction> assignment, MethodBody body, ABC abc, Set<Integer> ignoredRegisters, Set<Integer> ignoredGets) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -192,6 +223,21 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
return visitCode(assignment, new HashSet<>(), new Stack<>(), body, abc, code, 0, code.code.size() - 1, ignoredRegisters, ignoredGets);
}
/**
* Visits code.
* @param assignment Assignment
* @param visited Visited
* @param stack Stack
* @param body Method body
* @param abc ABC
* @param code AVM2 code
* @param idx Index
* @param endIdx End index
* @param ignored Ignored
* @param ignoredGets Ignored gets
* @return Register id
* @throws InterruptedException
*/
private int visitCode(Reference<AVM2Instruction> assignment, Set<Integer> visited, Stack<Object> stack, MethodBody body, ABC abc, AVM2Code code, int idx, int endIdx, Set<Integer> ignored, Set<Integer> ignoredGets) throws InterruptedException {
LocalDataArea localData = new LocalDataArea();
initLocalRegs(localData, body.getLocalReservedCount(), body.max_regs, false);
@@ -35,21 +35,10 @@ import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
@@ -61,6 +50,11 @@ import java.util.Set;
*/
public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld {
/**
* Gets all register ids.
* @param code AVM2 code
* @return Set of register ids
*/
private Set<Integer> getRegisters(AVM2Code code) {
Set<Integer> regs = new HashSet<>();
for (AVM2Instruction ins : code.code) {
@@ -85,6 +79,18 @@ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld {
return regs;
}
/**
* Removes single assigned local registers.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
//System.err.println("regdeo:" + path);
@@ -152,6 +158,19 @@ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld {
originalBody.setCode(body.getCode());
}
/**
* Gets first register with setter.
* @param assignment Assignment
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param body Method body
* @param ignoredRegisters Ignored registers
* @param ignoredGets Ignored gets
* @return Register id
* @throws InterruptedException
*/
private int getFirstRegisterSetter(Reference<AVM2Instruction> assignment, int classIndex, boolean isStatic, int scriptIndex, ABC abc, MethodBody body, Set<Integer> ignoredRegisters, Set<Integer> ignoredGets) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -163,26 +182,66 @@ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld {
return visitCode(assignment, visited, new TranslateStack("deo"), classIndex, isStatic, body, scriptIndex, abc, code, 0, code.code.size() - 1, ignoredRegisters, ignoredGets);
}
/**
* Exception target ip pair.
*/
private class ExceptionTargetIpPair {
/**
* Exception.
*/
private final ABCException exception;
/**
* Target ip.
*/
private final int targetIp;
/**
* Constructs a new instance of ExceptionTargetIpPair.
* @param exception Exception
* @param targetIp Target ip
*/
public ExceptionTargetIpPair(ABCException exception, int targetIp) {
this.exception = exception;
this.targetIp = targetIp;
}
/**
* Gets exception.
* @return Exception
*/
public ABCException getException() {
return exception;
}
/**
* Gets target ip.
* @return Target ip
*/
public int getTargetIp() {
return targetIp;
}
}
/**
* Visits code.
* @param assignment Assignment
* @param visited Visited
* @param stack Stack
* @param classIndex Class index
* @param isStatic Is static
* @param body Method body
* @param scriptIndex Script index
* @param abc ABC
* @param code AVM2 code
* @param idx Index
* @param endIdx End index
* @param ignored Ignored
* @param ignoredGets Ignored gets
* @return Register id
* @throws InterruptedException
*/
private int visitCode(Reference<AVM2Instruction> assignment, Set<Integer> visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, Set<Integer> ignored, Set<Integer> ignoredGets) throws InterruptedException {
Map<Integer, List<ExceptionTargetIpPair>> exceptionStartToTargets = new HashMap<>();
@@ -20,38 +20,10 @@ import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea;
import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2ExecutionException;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions;
import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DivideIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.ModuloIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitAndIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitOrIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitXorIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterEqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessEqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessThanIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.StrictEqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewArrayIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns;
@@ -59,18 +31,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushDoubleIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushFalseIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushTrueIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceOrConvertTypeIns;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
@@ -78,19 +39,34 @@ import com.jpexs.decompiler.flash.ecma.NotCompileTime;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.helpers.collections.FixItemCounterStack;
import com.jpexs.decompiler.graph.TranslateException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
*
* Simple AVM2 deobfuscator.
* @author JPEXS
*/
public class AVM2DeobfuscatorSimple extends AVM2DeobfuscatorZeroJumpsNullPushes {
/**
* Maximum number of instructions to execute in one pass
*/
private final int executionLimit = 30000;
/**
* Removes obfuscation ifs.
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param body Method body
* @param inlineIns Inline instruction
* @return True if code was modified
* @throws InterruptedException
*/
protected boolean removeObfuscationIfs(int classIndex, boolean isStatic, int scriptIndex, ABC abc, MethodBody body, AVM2Instruction inlineIns) throws InterruptedException {
AVM2Code code = body.getCode();
if (code.code.isEmpty()) {
@@ -128,6 +104,13 @@ public class AVM2DeobfuscatorSimple extends AVM2DeobfuscatorZeroJumpsNullPushes
return false;
}
/**
* Initializes local registers.
* @param localData Local data
* @param localReservedCount Count of reserved local registers
* @param maxRegs Maximum registers
* @param executeFromFirst Execute from first
*/
protected void initLocalRegs(LocalDataArea localData, int localReservedCount, int maxRegs, boolean executeFromFirst) {
for (int i = 0; i < localReservedCount; i++) {
localData.localRegisters.put(i, NotCompileTime.INSTANCE);
@@ -137,6 +120,20 @@ public class AVM2DeobfuscatorSimple extends AVM2DeobfuscatorZeroJumpsNullPushes
}
}
/**
* Executes instructions.
* @param staticRegs Static registers
* @param body Method body
* @param abc ABC
* @param code AVM2 code
* @param localData Local data
* @param idx Start index
* @param endIdx End index
* @param result Execution result
* @param inlineIns Inline instruction
* @return True if code was modified
* @throws InterruptedException
*/
private boolean executeInstructions(Map<Integer, Object> staticRegs, MethodBody body, ABC abc, AVM2Code code, LocalDataArea localData, int idx, int endIdx, ExecutionResult result, AVM2Instruction inlineIns) throws InterruptedException {
int instructionsProcessed = 0;
@@ -348,6 +345,18 @@ public class AVM2DeobfuscatorSimple extends AVM2DeobfuscatorZeroJumpsNullPushes
return modified;
}
/**
* Simple deobfuscation.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -357,12 +366,24 @@ public class AVM2DeobfuscatorSimple extends AVM2DeobfuscatorZeroJumpsNullPushes
removeNullPushes(code, body);
}
/**
* Execution result.
*/
class ExecutionResult {
/**
* Ip
*/
public int idx = -1;
/**
* Number of instructions processed
*/
public int instructionsProcessed = -1;
/**
* Stack
*/
public Stack<Object> stack = new Stack<>();
}
}
@@ -21,39 +21,10 @@ import com.jpexs.decompiler.flash.abc.AVM2LocalData;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
import com.jpexs.decompiler.flash.abc.avm2.FixItemCounterTranslateStack;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions;
import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DivideIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.ModuloIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitAndIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitNotIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitOrIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitXorIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterEqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessEqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessThanIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.StrictEqualsIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewArrayIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns;
@@ -67,59 +38,48 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIn
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushDoubleIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushFalseIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushTrueIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceOrConvertTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.TypeOfIns;
import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewFunctionAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.*;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.NotCompileTimeItem;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
* Simple deobfuscator for AVM2 code. (Old version)
* @author JPEXS
*/
public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPushes {
/**
* Undefined item
*/
private static final UndefinedAVM2Item UNDEFINED_ITEM = new UndefinedAVM2Item(null, null);
/**
* Not compile time undefined item
*/
private static final NotCompileTimeItem NOT_COMPILE_TIME_UNDEFINED_ITEM = new NotCompileTimeItem(null, null, UNDEFINED_ITEM);
/**
* Execution limit
*/
private final int executionLimit = 30000;
/**
* Creates a push instruction from a graph target item.
* @param cpool Constant pool
* @param graphTargetItem Graph target item
* @return Push instruction
*/
protected AVM2Instruction makePush(AVM2ConstantPool cpool, GraphTargetItem graphTargetItem) {
if (graphTargetItem instanceof IntegerValueAVM2Item) {
IntegerValueAVM2Item iv = (IntegerValueAVM2Item) graphTargetItem;
@@ -143,6 +103,17 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
return null;
}
/**
* Removes obfuscation ifs.
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param body Method body
* @param inlineIns Inline instructions
* @return True if removed, false otherwise
* @throws InterruptedException
*/
protected boolean removeObfuscationIfs(int classIndex, boolean isStatic, int scriptIndex, ABC abc, MethodBody body, List<AVM2Instruction> inlineIns) throws InterruptedException {
AVM2Code code = body.getCode();
if (code.code.isEmpty()) {
@@ -196,6 +167,16 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
return false;
}
/**
* Creates a new local data.
* @param scriptIndex Script index
* @param abc ABC
* @param cpool Constant pool
* @param body Method body
* @param isStatic Is static
* @param classIndex Class index
* @return New local data
*/
protected AVM2LocalData newLocalData(int scriptIndex, ABC abc, AVM2ConstantPool cpool, MethodBody body, boolean isStatic, int classIndex) {
AVM2LocalData localData = new AVM2LocalData();
localData.isStatic = isStatic;
@@ -217,6 +198,12 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
return localData;
}
/**
* Initializes local registers.
* @param localData Local data
* @param localReservedCount Count of reserved local registers
* @param maxRegs Maximal register id
*/
protected void initLocalRegs(AVM2LocalData localData, int localReservedCount, int maxRegs) {
for (int i = 0; i < localReservedCount; i++) {
localData.localRegs.put(i, NOT_COMPILE_TIME_UNDEFINED_ITEM);
@@ -226,6 +213,23 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
}
}
/**
* Executes instructions.
* @param importantOffsets Important offsets
* @param staticRegs Static registers
* @param body Method body
* @param abc ABC
* @param code AVM2 code
* @param localData Local data
* @param idx Index
* @param endIdx End index
* @param result Execution result
* @param inlineIns Inline instructions
* @param jumpTargets Jump targets
* @param minChangedIpRef Minimal changed IP reference
* @return True if executed, false otherwise
* @throws InterruptedException
*/
private boolean executeInstructions(Set<Long> importantOffsets, Map<Integer, GraphTargetItem> staticRegs, MethodBody body, ABC abc, AVM2Code code, AVM2LocalData localData, int idx, int endIdx, ExecutionResult result, List<AVM2Instruction> inlineIns, List<Integer> jumpTargets, Reference<Integer> minChangedIpRef) throws InterruptedException {
List<GraphTargetItem> output = new ArrayList<>();
@@ -535,6 +539,18 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
return false;
}
/**
* Simple deobfuscates AVM2 code.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -544,12 +560,24 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
removeNullPushes(code, body);
}
/**
* Execution result.
*/
class ExecutionResult {
/**
* Ip
*/
public int idx = -1;
/**
* Number of instructions processed
*/
public int instructionsProcessed = -1;
public TranslateStack stack = new TranslateStack("?");
/**
* Stack
*/
public Stack<Object> stack = new Stack<>();
}
}
@@ -22,34 +22,39 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushDoubleIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushFalseIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNanIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushTrueIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUIntIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.*;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import com.jpexs.helpers.Reference;
import java.util.Set;
/**
*
* Deobfuscator for removing zero jumps and null pushes.
* @author JPEXS
*/
public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
/**
* Removes zero jumps from the code.
* @param code AVM2 code
* @param body Method body
* @return True if any zero jumps were removed
* @throws InterruptedException
*/
protected boolean removeZeroJumps(AVM2Code code, MethodBody body) throws InterruptedException {
return removeZeroJumps(code, body, new Reference<>(-1));
}
/**
* Removes zero jumps from the code.
* @param code AVM2 code
* @param body Method body
* @param minChangedIpRef Reference to the minimum changed instruction pointer
* @return True if any zero jumps were removed
* @throws InterruptedException
*/
protected boolean removeZeroJumps(AVM2Code code, MethodBody body, Reference<Integer> minChangedIpRef) throws InterruptedException {
boolean result = false;
int minChangedIp = -1;
@@ -74,6 +79,11 @@ public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
return result;
}
/**
* Checks if the instruction is a simple push.
* @param def Instruction definition
* @return True if the instruction is a simple push
*/
private boolean isSimplePush(InstructionDefinition def) {
return (def instanceof PushByteIns
|| def instanceof PushDoubleIns
@@ -88,6 +98,13 @@ public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
|| def instanceof PushUndefinedIns);
}
/**
* Removes null pushes from the code.
* @param code AVM2 code
* @param body Method body
* @return True if any null pushes were removed
* @throws InterruptedException
*/
protected boolean removeNullPushes(AVM2Code code, MethodBody body) throws InterruptedException {
boolean result = false;
Set<Long> offsets = code.getImportantOffsets(body, true);
@@ -136,6 +153,18 @@ public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
return result;
}
/**
* Removes zero jumps and null pushes from the code.
* @param path Path
* @param classIndex Class index
* @param isStatic Is static
* @param scriptIndex Script index
* @param abc ABC
* @param trait Trait
* @param methodInfo Method info
* @param body Method body
* @throws InterruptedException
*/
@Override
public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
@@ -23,12 +23,22 @@ import com.jpexs.decompiler.flash.abc.types.NamespaceSet;
import com.jpexs.decompiler.flash.abc.usages.multinames.MultinameUsage;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.Tag;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* Fixes collisions of multinames in ABC files.
*/
public class AbcMultiNameCollisionFixer {
/**
* Fixes collisions of multinames in SWF file.
*
* @param swf SWF file
* @return Number of fixed collisions
*/
public int fixCollisions(SWF swf) {
int ret = 0;
for (ABCContainerTag tag : swf.getAbcList()) {
@@ -37,6 +47,12 @@ public class AbcMultiNameCollisionFixer {
return ret;
}
/**
* Fixes collisions of multinames in ABC file.
*
* @param abc ABC file
* @return Number of fixed collisions
*/
public int fixCollisions(ABC abc) {
Set<MultinameUsage> collidingUsages = abc.getCollidingMultinameUsages();
Set<Integer> collidingMultinameIndices = new HashSet<>();
@@ -17,19 +17,32 @@
package com.jpexs.decompiler.flash.abc.avm2.deobfuscation;
/**
*
* Level of deobfuscation enum.
* @author JPEXS
*/
public enum DeobfuscationLevel {
LEVEL_REMOVE_DEAD_CODE(1),
LEVEL_REMOVE_TRAPS(2);
/**
* Level of deobfuscation as number
*/
private final int level;
/**
* Get level of deobfuscation as number
* @return
*/
public int getLevel() {
return level;
}
/**
* Get deobfuscation level by level number.
* @param level Level number
* @return Deobfuscation level or null if not found
*/
public static DeobfuscationLevel getByLevel(int level) {
switch (level) {
case 1:
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash.abc.avm2.deobfuscation;
/**
*
* Deobfuscation scope enum.
* @author JPEXS
*/
public enum DeobfuscationScope {
@@ -0,0 +1,4 @@
/**
* Deobfuscating AVM2 bytecode.
*/
package com.jpexs.decompiler.flash.abc.avm2.deobfuscation;
@@ -17,11 +17,15 @@
package com.jpexs.decompiler.flash.abc.avm2.exceptions;
/**
*
* Exception thrown when an error occurs during AVM2 execution.
* @author JPEXS
*/
public class AVM2ExecutionException extends Exception {
/**
* Constructs new AVM2ExecutionException with the specified detail message.
* @param message
*/
public AVM2ExecutionException(String message) {
super(message);
}
@@ -17,19 +17,37 @@
package com.jpexs.decompiler.flash.abc.avm2.exceptions;
/**
*
* Range error exception.
* @author JPEXS
*/
public class AVM2RangeErrorException extends AVM2ExecutionException {
/**
* Constructs new AVM2RangeErrorException with the specified error code.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
*/
public AVM2RangeErrorException(int code, boolean debug) {
super(codeToMessage(code, debug, null));
}
/**
* Constructs new AVM2RangeErrorException with the specified error code and parameters.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
* @param params Parameters for the error message
*/
public AVM2RangeErrorException(int code, boolean debug, Object[] params) {
super(codeToMessage(code, debug, params));
}
/**
* Converts error code to error message.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
* @param params Parameters for the error message
* @return Error message
*/
private static String codeToMessage(int code, boolean debug, Object[] params) {
String msg = null;
/*switch (code) {
@@ -17,19 +17,37 @@
package com.jpexs.decompiler.flash.abc.avm2.exceptions;
/**
*
* AVM2 TypeError exception.
* @author JPEXS
*/
public class AVM2TypeErrorException extends AVM2ExecutionException {
/**
* Constructs new AVM2TypeErrorException with the specified error code.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
*/
public AVM2TypeErrorException(int code, boolean debug) {
super(codeToMessage(code, debug, null));
}
/**
* Constructs new AVM2TypeErrorException with the specified error code and parameters.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
* @param params Parameters for the error message
*/
public AVM2TypeErrorException(int code, boolean debug, Object[] params) {
super(codeToMessage(code, debug, params));
}
/**
* Converts error code to error message.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
* @param params Parameters for the error message
* @return Error message
*/
private static String codeToMessage(int code, boolean debug, Object[] params) {
String msg = null;
/*switch (code) {
@@ -17,25 +17,52 @@
package com.jpexs.decompiler.flash.abc.avm2.exceptions;
/**
*
* AVM2 VerifyError exception.
* @author JPEXS
*/
public class AVM2VerifyErrorException extends AVM2ExecutionException {
/**
* Illegal opcode error code.
*/
public static final int ILLEGAL_OPCODE = 1011;
/**
* Branch target is not a valid instruction error code.
*/
public static final int BRANCH_TARGET_INVALID_INSTRUCTION = 1021;
/**
* Cpool index out of range error code.
*/
public static final int CPOOL_INDEX_OUT_OF_RANGE = 1032;
/**
* Constructs new AVM2VerifyErrorException with the specified error code.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
*/
public AVM2VerifyErrorException(int code, boolean debug) {
super(codeToMessage(code, debug, null));
}
/**
* Constructs new AVM2VerifyErrorException with the specified error code and parameters.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
* @param params Parameters for the error message
*/
public AVM2VerifyErrorException(int code, boolean debug, Object[] params) {
super(codeToMessage(code, debug, params));
}
/**
* Converts error code to error message.
* @param code Error code
* @param debug If true, the error message will contain a description of the error
* @param params Parameters for the error message
* @return Error message
*/
private static String codeToMessage(int code, boolean debug, Object[] params) {
String msg = null;
switch (code) {
@@ -0,0 +1,4 @@
/**
* Exceptions for AVM2 execution.
*/
package com.jpexs.decompiler.flash.abc.avm2.exceptions;
@@ -17,51 +17,99 @@
package com.jpexs.decompiler.flash.abc.avm2.fastavm2;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* Instruction item for fast AVM2 list.
* @author JPEXS
*/
public class AVM2InstructionItem {
/**
* Instruction
*/
public AVM2Instruction ins;
/**
* Previous instruction
*/
public AVM2InstructionItem prev;
/**
* Next instruction
*/
public AVM2InstructionItem next;
/**
* Jump target
*/
private AVM2InstructionItem jumpTarget;
/**
* Instructions that jump here
*/
public Set<AVM2InstructionItem> jumpsHere;
/**
* Instructions that this instruction is the last instruction of
*/
public Set<AVM2InstructionItem> lastInsOf;
/**
* Last instruction container
*/
private List<AVM2InstructionItem> containerLastInstructions;
// 1 means reachable, 2 means reachable and processed
//
/**
* Whether this instruction is reachable.
* 1 means reachable, 2 means reachable and processed.
*/
int reachable;
/**
* Excluded
*/
public boolean excluded;
/**
* Constructs a new AVM2InstructionItem
* @param ins Instruction
*/
public AVM2InstructionItem(AVM2Instruction ins) {
this.ins = ins;
}
/**
* Checks whether this instruction is a jump target.
* @return Whether this instruction is a jump target
*/
public boolean isJumpTarget() {
return jumpsHere != null && !jumpsHere.isEmpty();
}
/**
* Gets the number of jumps to this instruction.
* @return Number of jumps to this instruction
*/
public int jumpsHereSize() {
return jumpsHere == null ? 0 : jumpsHere.size();
}
/**
* Checks whether this instruction is the last instruction of a container.
* @return Whether this instruction is the last instruction of a container
*/
public boolean isContainerLastInstruction() {
return lastInsOf != null && !lastInsOf.isEmpty();
}
/**
* Remove jump target.
*/
public void removeJumpTarget() {
if (jumpTarget == null) {
return;
@@ -74,14 +122,26 @@ public class AVM2InstructionItem {
jumpTarget = null;
}
/**
* Get jump target.
* @return Instruction item
*/
public AVM2InstructionItem getJumpTarget() {
return jumpTarget;
}
/**
* Get jump target instruction.
* @return Instruction
*/
public AVM2Instruction getJumpTargetInstruction() {
return jumpTarget == null ? null : jumpTarget.ins;
}
/**
* Set jump target.
* @param item Instruction item
*/
public void setJumpTarget(AVM2InstructionItem item) {
removeJumpTarget();
@@ -97,10 +157,17 @@ public class AVM2InstructionItem {
jumpTarget = item;
}
/**
* Get last instructions of container.
* @return List of instruction items
*/
public List<AVM2InstructionItem> getContainerLastInstructions() {
return containerLastInstructions;
}
/**
* Remove last instructions of container.
*/
public void removeContainerLastInstructions() {
if (containerLastInstructions == null) {
return;
@@ -115,6 +182,11 @@ public class AVM2InstructionItem {
containerLastInstructions = null;
}
/**
* Replace container last instruction.
* @param oldItem Old instruction item
* @param newItem New instruction item
*/
public void replaceContainerLastInstruction(AVM2InstructionItem oldItem, AVM2InstructionItem newItem) {
if (containerLastInstructions == null) {
return;
@@ -132,6 +204,10 @@ public class AVM2InstructionItem {
}
}
/**
* Set container last instructions.
* @param lastInstructions
*/
public void setContainerLastInstructions(List<AVM2InstructionItem> lastInstructions) {
removeContainerLastInstructions();
@@ -142,6 +218,10 @@ public class AVM2InstructionItem {
containerLastInstructions = lastInstructions;
}
/**
* Ensure last instruction is non-null.
* @return Set of instruction items
*/
private Set<AVM2InstructionItem> ensureLastInstructionOf() {
if (lastInsOf == null) {
lastInsOf = new HashSet<>();
@@ -150,6 +230,10 @@ public class AVM2InstructionItem {
return lastInsOf;
}
/**
* Checks whether this instruction is excluded.
* @return Whether this instruction is excluded
*/
public boolean isExcluded() {
return excluded;
}
@@ -24,28 +24,39 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
* Fast AVM2 instruction list.
* @author JPEXS
*/
public class FastAVM2List implements Collection<AVM2InstructionItem> {
/**
* Size of the list
*/
private int size;
/**
* First item
*/
private AVM2InstructionItem firstItem;
/**
* Map of instructions to items
*/
private final Map<AVM2Instruction, AVM2InstructionItem> actionItemMap;
/**
* Set of items
*/
private final Set<AVM2InstructionItem> actionItemSet;
/**
* Constructs a new FastAVM2List from a method body
* @param body Method body
*/
public FastAVM2List(MethodBody body) {
// exceptions todo
AVM2Code avm2code = body.getCode();
@@ -60,11 +71,23 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
getJumps(avm2code, actionItemMap);
}
/**
* Inserts item before another item
* @param item Item to insert before
* @param action Action to insert
* @return
*/
public final AVM2InstructionItem insertItemBefore(AVM2InstructionItem item, AVM2Instruction action) {
AVM2InstructionItem newItem = new AVM2InstructionItem(action);
return insertItemBefore(item, newItem);
}
/**
* Inserts item before another item
* @param item Item to insert before
* @param newItem New item
* @return New item
*/
public final AVM2InstructionItem insertItemBefore(AVM2InstructionItem item, AVM2InstructionItem newItem) {
insertItemAfter(item.prev, newItem);
if (item == firstItem) {
@@ -74,11 +97,23 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return newItem;
}
/**
* Inserts item after another item
* @param item Item to insert after
* @param action Action to insert
* @return
*/
public final AVM2InstructionItem insertItemAfter(AVM2InstructionItem item, AVM2Instruction action) {
AVM2InstructionItem newItem = new AVM2InstructionItem(action);
return insertItemAfter(item, newItem);
}
/**
* Inserts item after another item
* @param item Item to insert after
* @param newItem New item
* @return New item
*/
public final AVM2InstructionItem insertItemAfter(AVM2InstructionItem item, AVM2InstructionItem newItem) {
if (item == null && firstItem == null) {
firstItem = newItem;
@@ -103,6 +138,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return newItem;
}
/**
* Removes item
* @param item Item to remove
* @return Next item
*/
public AVM2InstructionItem removeItem(AVM2InstructionItem item) {
AVM2InstructionItem next = null;
if (item == firstItem) {
@@ -143,6 +183,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return next;
}
/**
* Removes item.
* @param index Index of item to remove
* @param count Number of items to remove
*/
public void removeItem(int index, int count) {
FastAVM2ListIterator iterator = new FastAVM2ListIterator(this, index);
for (int i = 0; i < count; i++) {
@@ -151,11 +196,21 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
}
}
/**
* Gets item at index
* @param index Index
* @return Item
*/
public AVM2InstructionItem get(int index) {
FastAVM2ListIterator iterator = new FastAVM2ListIterator(this, index);
return iterator.next();
}
/**
* Replace jump targets.
* @param target Target
* @param newTarget New target
*/
public void replaceJumpTargets(AVM2InstructionItem target, AVM2InstructionItem newTarget) {
if (target.jumpsHere != null) {
for (AVM2InstructionItem item : new ArrayList<>(target.jumpsHere)) {
@@ -164,6 +219,13 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
}
}
/**
* Gets nerby address.
* @param instructions Instructions
* @param address Address
* @param next Next
* @return
*/
private long getNearAddress(List<AVM2Instruction> instructions, long address, boolean next) {
int min = 0;
int max = instructions.size() - 1;
@@ -185,6 +247,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
: (max >= 0 ? instructions.get(max).getAddress() : -1);
}
/**
* Gets jumps.
* @param actions Actions
* @param actionItemMap Action item map
*/
private void getJumps(AVM2Code actions, Map<AVM2Instruction, AVM2InstructionItem> actionItemMap) {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -208,6 +275,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Updates action addresses and lengths.
*/
private void updateActionAddressesAndLengths() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -223,6 +293,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Updates jumps.
*/
private void updateJumps() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -249,6 +322,10 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Updates container sizes.
*/
private void updateContainerSizes() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -273,6 +350,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Gets container.
* @param item Item
* @return Container
*/
public AVM2InstructionItem getContainer(AVM2InstructionItem item) {
while (!(item.ins instanceof GraphSourceItemContainer) && item != firstItem) {
item = item.prev;
@@ -285,6 +367,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return null;
}
/**
* Removes zero jumps.
*/
public void removeZeroJumps() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -304,6 +389,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Removes unreachable actions.
*/
public void removeUnreachableActions() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -322,6 +410,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Removes included actions.
*/
public void removeIncludedActions() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -338,6 +429,12 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Gets unreachable action count.
* @param jump Jump
* @param jumpTarget Jump target
* @return
*/
public int getUnreachableActionCount(AVM2InstructionItem jump, AVM2InstructionItem jumpTarget) {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -359,6 +456,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return count;
}
/**
* Clears reachable flags.
*/
private void clearReachableFlags() {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -371,6 +471,10 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Sets excluded flags.
* @param value Value
*/
public void setExcludedFlags(boolean value) {
AVM2InstructionItem item = firstItem;
if (item == null) {
@@ -383,6 +487,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
} while (item != firstItem);
}
/**
* Updates reachable flags.
* @param jump Jump
* @param jumpTarget Jump target
*/
private void updateReachableFlags(AVM2InstructionItem jump, AVM2InstructionItem jumpTarget) {
if (firstItem == null) {
return;
@@ -446,6 +555,10 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
}
}
/**
* Updates actions.
* @param body Method body
*/
public void updateActions(MethodBody body) {
AVM2Code result = new AVM2Code(size);
AVM2InstructionItem item = firstItem;
@@ -466,28 +579,53 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
updateContainerSizes();
}
/**
* Gets first item.
* @return First item
*/
public AVM2InstructionItem first() {
return firstItem;
}
/**
* Gets last item.
* @return Last item
*/
public AVM2InstructionItem last() {
return firstItem == null ? null : firstItem.prev;
}
/**
* Converts to method body.
* @param body Method body
*/
public void toMethodBody(MethodBody body) {
updateActions(body);
}
/**
* Gets size.
* @return Size
*/
@Override
public int size() {
return size;
}
/**
* Checks if empty.
* @return Whether empty
*/
@Override
public boolean isEmpty() {
return size == 0;
}
/**
* Checks if contains.
* @param o element whose presence in this collection is to be tested
* @return Whether contains
*/
@Override
public boolean contains(Object o) {
if (o instanceof AVM2InstructionItem) {
@@ -499,11 +637,19 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return false;
}
/**
* Gets iterator.
* @return Iterator
*/
@Override
public FastAVM2ListIterator iterator() {
return new FastAVM2ListIterator(this);
}
/**
* Converts to array.
* @return Array
*/
@Override
public Object[] toArray() {
Object[] result = new Object[size];
@@ -522,6 +668,14 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return null;
}
/**
* Converts to array.
* @param a the array into which the elements of this collection are to be
* stored, if it is big enough; otherwise, a new array of the same
* runtime type is allocated for this purpose.
* @return Array
* @param <T>
*/
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
@@ -543,12 +697,22 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return null;
}
/**
* Adds an element to this collection.
* @param e element whose presence in this collection is to be ensured
* @return Whether added
*/
@Override
public boolean add(AVM2InstructionItem e) {
insertItemAfter(null, e);
return true;
}
/**
* Removes a single instance of the specified element from this collection,
* @param o element to be removed from this collection, if present
* @return Whether removed
*/
@Override
public boolean remove(Object o) {
AVM2InstructionItem item = null;
@@ -566,6 +730,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return true;
}
/**
* Checks whether this collection contains all of the elements in the specified collection.
* @param c collection to be checked for containment in this collection
* @return Whether contains all
*/
@Override
public boolean containsAll(Collection<?> c) {
for (Object c1 : c) {
@@ -577,6 +746,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return true;
}
/**
* Adds all of the elements in the specified collection to this collection.
* @param c collection containing elements to be added to this collection
* @return Whether added all
*/
@Override
public boolean addAll(Collection<? extends AVM2InstructionItem> c) {
for (AVM2InstructionItem c1 : c) {
@@ -586,6 +760,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return true;
}
/**
* Removes all of this collection's elements that are also contained in the specified collection.
* @param c collection containing elements to be removed from this collection
* @return Whether removed all
*/
@Override
public boolean removeAll(Collection<?> c) {
boolean result = false;
@@ -596,6 +775,11 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return result;
}
/**
* Retains only the elements in this collection that are contained in the specified collection.
* @param c collection containing elements to be retained in this collection
* @return Whether retained all
*/
@Override
public boolean retainAll(Collection<?> c) {
AVM2InstructionItem item = firstItem;
@@ -616,6 +800,9 @@ public class FastAVM2List implements Collection<AVM2InstructionItem> {
return modified;
}
/**
* Clears this collection.
*/
@Override
public void clear() {
firstItem = null;
@@ -17,25 +17,44 @@
package com.jpexs.decompiler.flash.abc.avm2.fastavm2;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import java.util.Iterator;
/**
*
* Iterator for FastAVM2List
* @author JPEXS
*/
public final class FastAVM2ListIterator implements Iterator<AVM2InstructionItem> {
/**
* Current item
*/
private AVM2InstructionItem item;
/**
* List
*/
private final FastAVM2List list;
/**
* If the iterator has been started
*/
private boolean started = false;
/**
* Constructs a new iterator.
* @param list List
*/
FastAVM2ListIterator(FastAVM2List list) {
item = list.first();
this.list = list;
}
/**
* Constructs a new iterator.
* @param list List
* @param index Index
*/
FastAVM2ListIterator(FastAVM2List list, int index) {
item = list.first();
this.list = list;
@@ -48,11 +67,19 @@ public final class FastAVM2ListIterator implements Iterator<AVM2InstructionItem>
}
}
/**
* Checks if there is a next item.
* @return True if there is a next item
*/
@Override
public boolean hasNext() {
return item != null && (!started || item != list.first());
}
/**
* Gets the next item.
* @return Next item
*/
@Override
public AVM2InstructionItem next() {
AVM2InstructionItem result = item;
@@ -65,6 +92,10 @@ public final class FastAVM2ListIterator implements Iterator<AVM2InstructionItem>
return result;
}
/**
* Gets the previous item.
* @return Previous item
*/
public AVM2InstructionItem prev() {
item = item.prev;
if (item == list.first()) {
@@ -77,6 +108,10 @@ public final class FastAVM2ListIterator implements Iterator<AVM2InstructionItem>
return item;
}
/**
* Sets the current item.
* @param item Item
*/
public void setCurrent(AVM2InstructionItem item) {
this.item = item;
if (item == list.first()) {
@@ -84,23 +119,43 @@ public final class FastAVM2ListIterator implements Iterator<AVM2InstructionItem>
}
}
/**
* Removes the current item.
*/
@Override
public void remove() {
item = list.removeItem(item.prev);
}
/**
* Adds an item.
* @param ins Instruction
*/
public void add(AVM2Instruction ins) {
item = list.insertItemAfter(item.prev, ins).next;
}
/**
* Adds an item.
* @param insItem Instruction item
*/
public void add(AVM2InstructionItem insItem) {
item = list.insertItemAfter(item.prev, insItem).next;
}
/**
* Adds an item before the current item.
* @param insItem Instruction item
*/
public void addBefore(AVM2InstructionItem insItem) {
list.insertItemBefore(item.prev, insItem);
}
/**
* Get the item at the specified index.
* @param index Index
* @return Item
*/
public AVM2InstructionItem peek(int index) {
AVM2InstructionItem item = this.item;
for (int i = 0; i < index; i++) {
@@ -0,0 +1,4 @@
/**
* Fast AVM2 instruction list implementation.
*/
package com.jpexs.decompiler.flash.abc.avm2.fastavm2;
@@ -30,13 +30,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugLineIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NopIns;
@@ -47,111 +41,70 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns;
import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ConstructAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.FilteredCheckAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.GetLexAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewActivationAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewFunctionAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NextNameAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NextValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetSlotAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.WithEndAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.WithObjectAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.FilterAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.*;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.*;
import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphException;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphPartChangeException;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.SecondPassData;
import com.jpexs.decompiler.graph.StopPartKind;
import com.jpexs.decompiler.graph.ThrowState;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.AnyItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.CommaExpressionItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.ExitItem;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.GotoItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.LoopItem;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.decompiler.graph.*;
import com.jpexs.decompiler.graph.model.*;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* AVM2 graph.
* @author JPEXS
*/
public class AVM2Graph extends Graph {
/**
* AVM2 code
*/
private final AVM2Code avm2code;
/**
* ABC
*/
private final ABC abc;
/**
* Method body
*/
private final MethodBody body;
/**
* ABC indexing
*/
private final AbcIndexing abcIndex;
/**
* Logger
*/
private final Logger logger = Logger.getLogger(AVM2Graph.class.getName());
//Kinds of finally blocks
final int FINALLY_KIND_STACK_BASED = 0;
final int FINALLY_KIND_REGISTER_BASED = 1;
final int FINALLY_KIND_INLINED = 2;
final int FINALLY_KIND_UNKNOWN = -1;
/**
* Get AVM2 code
* @return AVM2 code
*/
public AVM2Code getCode() {
return avm2code;
}
/**
* Get exceptions
* @param body Method body
* @return Exceptions
*/
private static List<GraphException> getExceptionEntries(MethodBody body) {
List<GraphException> ret = new ArrayList<>();
AVM2Code code = body.getCode();
@@ -161,29 +114,39 @@ public class AVM2Graph extends Graph {
return ret;
}
/**
* Constructs AVM2 graph
* @param abcIndex ABC indexing
* @param code AVM2 code
* @param abc ABC
* @param body Method body
* @param isStatic Is static
* @param scriptIndex Script index
* @param classIndex Class index
* @param localRegs Local registers
* @param scopeStack Scope stack
* @param localScopeStack Local scope stack
* @param localRegNames Local register names
* @param fullyQualifiedNames Fully qualified names
* @param localRegAssigmentIps Local register assignment IPs
*/
public AVM2Graph(AbcIndexing abcIndex, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, ScopeStack scopeStack, ScopeStack localScopeStack, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames, HashMap<Integer, Integer> localRegAssigmentIps) {
super(new AVM2GraphSource(code, isStatic, scriptIndex, classIndex, localRegs, abc, body, localRegNames, fullyQualifiedNames, localRegAssigmentIps), getExceptionEntries(body));
this.avm2code = code;
this.abc = abc;
this.body = body;
this.abcIndex = abcIndex;
/*heads = makeGraph(code, new ArrayList<GraphPart>(), body);
this.code = code;
this.abc = abc;
this.body = body;
for (GraphPart head : heads) {
fixGraph(head);
makeMulti(head, new ArrayList<GraphPart>());
}*/
}
/**
* Checks whether part can be break candidate
* @param localData Local data
* @param part Part
* @param throwStates List of throw states
* @return True if part can be break candidate, false otherwise
*/
@Override
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part, List<ThrowState> throwStates) {
/*AVM2LocalData aLocalData = (AVM2LocalData) localData;
if (aLocalData.finallyTargetParts.containsValue(part)) {
return false;
}*/
for (ThrowState ts : throwStates) {
if (ts.targetPart == part) {
return false;
@@ -192,6 +155,14 @@ public class AVM2Graph extends Graph {
return true;
}
/**
* Check before get loops
* @param localData Local data
* @param path Path
* @param allParts All parts
* @param throwStates Throw states
* @throws InterruptedException
*/
@Override
protected void beforeGetLoops(BaseLocalData localData, String path, Set<GraphPart> allParts, List<ThrowState> throwStates) throws InterruptedException {
AVM2LocalData avm2LocalData = ((AVM2LocalData) localData);
@@ -221,11 +192,24 @@ public class AVM2Graph extends Graph {
avm2LocalData.inGetLoops = true;
}
/**
* Check after get loops
* @param localData Local data
* @param path Path
* @param allParts All parts
* @throws InterruptedException
*/
@Override
protected void afterGetLoops(BaseLocalData localData, String path, Set<GraphPart> allParts) throws InterruptedException {
((AVM2LocalData) localData).inGetLoops = false;
}
/**
* Gets ignored switches
* @param localData Local data
* @param allParts All parts
* @throws InterruptedException
*/
private void getIgnoredSwitches(AVM2LocalData localData, Set<GraphPart> allParts) throws InterruptedException {
for (int e = 0; e < body.exceptions.length; e++) {
@@ -494,6 +478,17 @@ public class AVM2Graph extends Graph {
}
}
/**
* Walk local registers usage
* @param throwStates List of throw states
* @param localData Local data
* @param getLocalPos Get local position
* @param startPart Start part
* @param part Part
* @param visited Visited
* @param ip IP
* @param searchRegId Search register ID
*/
private void walkLocalRegsUsage(List<ThrowState> throwStates, AVM2LocalData localData, Set<Integer> getLocalPos, GraphPart startPart, GraphPart part, Set<GraphPart> visited, int ip, int searchRegId) {
if (visited.contains(part) && part != startPart) {
return;
@@ -584,7 +579,17 @@ public class AVM2Graph extends Graph {
}
}
//TODO: optimize this to make it faster!!!
/**
* Calculates local registers usage.
*
* TODO: optimize this to make it faster!!!
* @param throwStates List of throw states
* @param localData Local data
* @param ignoredSwitches Ignored switches
* @param path Path
* @param allParts All parts
* @return Map of getLocal to setLocal set
*/
public Map<Integer, Set<Integer>> calculateLocalRegsUsage(List<ThrowState> throwStates, AVM2LocalData localData, Set<Integer> ignoredSwitches, String path, Set<GraphPart> allParts) {
logger.log(Level.FINE, "--- {0} ---", path);
Map<Integer, Set<Integer>> setLocalPosToGetLocalPos = new TreeMap<>();
@@ -627,6 +632,29 @@ public class AVM2Graph extends Graph {
return setLocalPosToGetLocalPos;
}
/**
* Translates via Graph - decompiles.
* @param secondPassData Second pass data
* @param callStack Call stack
* @param abcIndex ABC indexing
* @param path Path
* @param code AVM2 code
* @param abc ABC
* @param body Method body
* @param isStatic Is static
* @param scriptIndex Script index
* @param classIndex Class index
* @param localRegs Local registers
* @param scopeStack Scope stack
* @param localRegNames Local register names
* @param localRegTypes Local register types
* @param fullyQualifiedNames Fully qualified names
* @param staticOperation Unused
* @param localRegAssigmentIps Local register assignment IPs
* @param thisHasDefaultToPrimitive This has default to primitive
* @return List of graph target items
* @throws InterruptedException
*/
public static List<GraphTargetItem> translateViaGraph(SecondPassData secondPassData, List<MethodBody> callStack, AbcIndexing abcIndex, String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, ScopeStack scopeStack, HashMap<Integer, String> localRegNames, HashMap<Integer, GraphTargetItem> localRegTypes, List<DottedChain> fullyQualifiedNames, int staticOperation, HashMap<Integer, Integer> localRegAssigmentIps, boolean thisHasDefaultToPrimitive) throws InterruptedException {
ScopeStack localScopeStack = new ScopeStack();
AVM2Graph g = new AVM2Graph(abcIndex, code, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localScopeStack, localRegNames, fullyQualifiedNames, localRegAssigmentIps);
@@ -657,6 +685,10 @@ public class AVM2Graph extends Graph {
return g.translate(localData, staticOperation, path);
}
/**
* Check graph.
* @param allBlocks All blocks
*/
@Override
protected void checkGraph(List<GraphPart> allBlocks) {
for (ABCException ex : body.exceptions) {
@@ -674,41 +706,13 @@ public class AVM2Graph extends Graph {
}
}
private void findNearestPartOutsideTry(AVM2LocalData localData, GraphPart part, int tryEndIp, Set<GraphPart> visited, Set<GraphPart> result) {
if (visited.contains(part)) {
return;
}
if (part.start >= tryEndIp || part.end >= tryEndIp) {
result.add(part);
return;
}
if (localData.finallyJumps.containsKey(part)) {
GraphPart afterSwitchPart = localData.finallyJumps.get(part);
GraphPart switchPart = null;
for (GraphPart r : afterSwitchPart.refs) {
if (localData.ignoredSwitches.containsValue(r)) {
switchPart = r;
}
}
if (switchPart != null) {
return;
}
}
for (GraphPart n : part.nextParts) {
findNearestPartOutsideTry(localData, n, tryEndIp, visited, result);
}
}
private Set<GraphPart> findNearestPartOutsideTry(AVM2LocalData localData, GraphPart start, int tryEndIp) {
Set<GraphPart> result = new HashSet<>();
findNearestPartOutsideTry(localData, start, tryEndIp, new HashSet<>(), result);
return result;
}
/**
* Finds lookup switch with get local.
* @param registerId Register ID
* @param part Part
* @param visited Visited
* @return Graph part
*/
private GraphPart findLookupSwitchWithGetLocal(int registerId, GraphPart part, Set<GraphPart> visited) {
if (visited.contains(part)) {
return null;
@@ -735,10 +739,25 @@ public class AVM2Graph extends Graph {
return null;
}
/**
* Finds lookup switch with get local.
* @param registerId Register ID
* @param part Part
* @return Graph part
*/
private GraphPart findLookupSwitchWithGetLocal(int registerId, GraphPart part) {
return findLookupSwitchWithGetLocal(registerId, part, new HashSet<>());
}
/**
* Finds all pops.
* @param localData Local data
* @param stackLevel Stack level
* @param part Part
* @param foundIps Found IPs
* @param foundParts Found parts
* @param visited Visited
*/
private void findAllPops(AVM2LocalData localData, int stackLevel, GraphPart part, List<Integer> foundIps, List<GraphPart> foundParts, Set<GraphPart> visited) {
if (visited.contains(part)) {
return;
@@ -758,6 +777,12 @@ public class AVM2Graph extends Graph {
}
}
/**
* Gets real references.
* Real = start >= 0
* @param part
* @return
*/
private List<GraphPart> getRealRefs(GraphPart part) {
List<GraphPart> ret = new ArrayList<>();
for (GraphPart r : part.refs) {
@@ -768,31 +793,14 @@ public class AVM2Graph extends Graph {
return ret;
}
private GraphPart firstPartOutsideWalk(GraphPart part, int startIp, int endIp, Set<GraphPart> visited) {
if (visited.contains(part)) {
return null;
}
visited.add(part);
if (part.start < startIp || part.start >= endIp) {
return part;
}
for (GraphPart n : part.nextParts) {
GraphPart r = firstPartOutsideWalk(n, startIp, endIp, visited);
if (r != null) {
return r;
}
}
return null;
}
private GraphPart searchFirstPartOutSideTryCatchSimple(ABCException ex, Collection<? extends GraphPart> allParts) {
int startIp = code.adr2pos(ex.start, true);
int endIp = code.adr2pos(ex.end, true);
GraphPart startPart = searchPart(startIp, allParts);
return firstPartOutsideWalk(startPart, startIp, endIp, new HashSet<>());
}
/**
* Search first part outside try-catch.
* @param localData Local data
* @param ex Exception
* @param loops Loops
* @param allParts All parts
* @return Graph part
*/
private GraphPart searchFirstPartOutSideTryCatch(AVM2LocalData localData, ABCException ex, List<Loop> loops, Collection<? extends GraphPart> allParts) {
LinkedHashSet<GraphPart> reachable = new LinkedHashSet<>();
int startIp = localData.code.adr2pos(ex.start, true);
@@ -820,6 +828,11 @@ public class AVM2Graph extends Graph {
return null;
}
/**
* Gets nearest non-empty part.
* @param part Part
* @return Graph part
*/
private GraphPart nearestNonEmptyPart(GraphPart part) {
while (isPartEmpty(part)) {
part = part.nextParts.get(0);
@@ -827,6 +840,16 @@ public class AVM2Graph extends Graph {
return part;
}
/**
* Gets catched exception IDs.
* @param part Part
* @param previouslyCatchedExceptionIds Previously catched exception IDs
* @param catchedExceptionIds Catched exception IDs
* @param finallyIndex Finally index
* @param allParts All parts
* @param loops Loops
* @param localData Local data
*/
private void getCatchedExceptionIds(GraphPart part, List<Integer> previouslyCatchedExceptionIds, List<Integer> catchedExceptionIds, Reference<Integer> finallyIndex, Collection<? extends GraphPart> allParts,
List<Loop> loops, AVM2LocalData localData) {
@@ -935,6 +958,12 @@ public class AVM2Graph extends Graph {
}
}
/**
* Finds nearest part outside catch.
* @param tryTarget Try target
* @param catchParts Catch parts
* @return Graph part
*/
private GraphPart findNearestPartOutsideCatch(GraphPart tryTarget, Set<GraphPart> catchParts) {
for (GraphPart p : catchParts) {
for (GraphPart n : p.nextParts) {
@@ -946,6 +975,27 @@ public class AVM2Graph extends Graph {
return null;
}
/**
* Checks try.
* @param currentRet Current return
* @param foundGotos Found gotos
* @param partCodes Part codes
* @param partCodePos Part code position
* @param visited Visited
* @param localData Local data
* @param part Part
* @param stopPart Stop part
* @param stopPartKind Stop part kind
* @param loops Loops
* @param throwStates Throw states
* @param allParts All parts
* @param stack Stack
* @param staticOperation Unused
* @param path Path
* @param recursionLevel Recursion level
* @return True if try is found
* @throws InterruptedException
*/
private boolean checkTry(List<GraphTargetItem> currentRet, List<GotoItem> foundGotos, Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos, Set<GraphPart> visited, AVM2LocalData localData, GraphPart part, List<GraphPart> stopPart, List<StopPartKind> stopPartKind, List<Loop> loops, List<ThrowState> throwStates, Set<GraphPart> allParts, TranslateStack stack, int staticOperation, String path, int recursionLevel) throws InterruptedException {
if (localData.parsedExceptions == null) {
localData.parsedExceptions = new ArrayList<>();
@@ -1413,6 +1463,12 @@ public class AVM2Graph extends Graph {
return false;
}
/**
* Checks whether the part can handle visited.
* @param localData Local data
* @param part Graph part
* @return True if the part can handle visited
*/
@Override
protected boolean canHandleVisited(BaseLocalData localData, GraphPart part) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
@@ -1428,6 +1484,14 @@ public class AVM2Graph extends Graph {
return true;
}
/**
* Checks whether the part can handle loop.
* @param localData Local data
* @param part Graph part
* @param loops List of loops
* @param throwStates List of throw states
* @return True if the part can handle loop
*/
@Override
protected boolean canHandleLoop(BaseLocalData localData, GraphPart part, List<Loop> loops, List<ThrowState> throwStates) {
Loop toBeLoop = null;
@@ -1466,6 +1530,30 @@ public class AVM2Graph extends Graph {
return true;
}
/**
* Check part output.
* @param currentRet Current return
* @param foundGotos Found gotos
* @param partCodes Part codes
* @param partCodePos Part code position
* @param visited Visited
* @param code Code
* @param localData Local data
* @param allParts All parts
* @param stack Stack
* @param parent Parent part
* @param part Part
* @param stopPart Stop part
* @param stopPartKind Stop part kind
* @param loops Loops
* @param throwStates Throw states
* @param currentLoop Current loop
* @param staticOperation Unused
* @param path Path
* @param recursionLevel Recursion level
* @return True to stop processing. False to continue.
* @throws InterruptedException
*/
@Override
protected boolean checkPartOutput(List<GraphTargetItem> currentRet, List<GotoItem> foundGotos,
Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos,
@@ -1481,6 +1569,31 @@ public class AVM2Graph extends Graph {
return checkTry(currentRet, foundGotos, partCodes, partCodePos, visited, aLocalData, part, stopPart, stopPartKind, loops, throwStates, allParts, stack, staticOperation, path, recursionLevel);
}
/**
* Check before decompiling next section.
* Override this method to provide custom behavior.
* @param currentRet Current return
* @param foundGotos Found gotos
* @param partCodes Part codes
* @param partCodePos Part code position
* @param visited Visited
* @param code Code
* @param localData Local data
* @param allParts All parts
* @param stack Stack
* @param parent Parent part
* @param part Part
* @param stopPart Stop part
* @param stopPartKind Stop part kind
* @param loops Loops
* @param throwStates Throw states
* @param output Output
* @param currentLoop Current loop
* @param staticOperation Unused
* @param path Path
* @return List of GraphTargetItems to replace current output and stop further processing. Null to continue.
* @throws InterruptedException
*/
@Override
protected List<GraphTargetItem> check(List<GraphTargetItem> currentRet, List<GotoItem> foundGotos,
Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos,
@@ -1607,6 +1720,13 @@ public class AVM2Graph extends Graph {
return ret;
}
/**
* Get next parts of a part.
* Can be overriden to provide custom behavior.
* @param localData Local data
* @param part Part
* @return List of GraphParts
*/
@Override
protected List<GraphPart> getNextParts(BaseLocalData localData, GraphPart part) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
@@ -1618,6 +1738,17 @@ public class AVM2Graph extends Graph {
return super.getNextParts(localData, part);
}
/**
* Check of Part passing output.
*
* @param output List of GraphTargetItems
* @param stack Translate stack
* @param localData Local data
* @param prev Previous part
* @param part Part
* @param allParts All parts
* @return Return same part to continue processing or return another part to continue to other part. Or return null to stop.
*/
@Override
protected GraphPart checkPartWithOutput(List<GraphTargetItem> output, TranslateStack stack,
BaseLocalData localData, GraphPart prev,
@@ -1693,11 +1824,26 @@ public class AVM2Graph extends Graph {
return part;
}
/**
* Check of part.
* @param stack Translate stack
* @param localData Local data
* @param prev Previous part
* @param part Part
* @param allParts All parts
* @return Return same part to continue processing or return another part to continue to other part. Or return null to stop.
*/
@Override
protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart prev, GraphPart part, Set<GraphPart> allParts) {
return checkPartWithOutput(null, stack, localData, prev, part, allParts);
}
/**
* Checks whether part is empty.
* @param part Part
* @return True if part is empty
*/
@Override
protected boolean isPartEmpty(GraphPart part) {
if (part.nextParts.size() > 1) {
@@ -1715,6 +1861,16 @@ public class AVM2Graph extends Graph {
return true;
}
/**
* Check loop.
* @param output List of GraphTargetItems
* @param loopItem Loop item
* @param localData Local data
* @param loops Loops
* @param throwStates Throw states
* @param stack Translate stack
* @return Return loopItem to replace current loop. Return null to continue.
*/
@Override
protected GraphTargetItem checkLoop(List<GraphTargetItem> output, LoopItem loopItem, BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates, TranslateStack stack) {
if (debugDoNotProcess) {
@@ -2047,6 +2203,11 @@ public class AVM2Graph extends Graph {
return loopItem;
}
/**
* Process various items.
* @param list List of GraphTargetItems
* @param lastLoopId Last loop id
*/
@Override
protected void processOther(List<GraphTargetItem> list, long lastLoopId) {
if (list.isEmpty()) {
@@ -2097,6 +2258,13 @@ public class AVM2Graph extends Graph {
}
}
/**
* Final process after.
* @param list List of GraphTargetItems
* @param level Level
* @param localData Local data
* @param path Path
*/
@Override
protected void finalProcessAfter(List<GraphTargetItem> list, int level, FinalProcessLocalData localData, String path) {
super.finalProcessAfter(list, level, localData, path);
@@ -2127,6 +2295,11 @@ public class AVM2Graph extends Graph {
}
}
/**
* Check if item is integer or pop integer.
* @param item GraphTargetItem
* @return True if item is integer or pop integer
*/
private boolean isIntegerOrPopInteger(GraphTargetItem item) {
if (item instanceof IntegerValueAVM2Item) {
return true;
@@ -2137,6 +2310,14 @@ public class AVM2Graph extends Graph {
return false;
}
/**
* Final process.
* @param list List of GraphTargetItems
* @param level Level
* @param localData Local data
* @param path Path
* @throws InterruptedException
*/
@Override
protected void finalProcess(List<GraphTargetItem> list, int level, FinalProcessLocalData localData, String path) throws InterruptedException {
if (debugDoNotProcess) {
@@ -2489,6 +2670,13 @@ public class AVM2Graph extends Graph {
super.finalProcess(list, level, localData, path);
}
/**
* Gets data for final process.
* @param localData
* @param loops
* @param throwStates
* @return
*/
@Override
protected FinalProcessLocalData getFinalData(BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates) {
FinalProcessLocalData finalProcess = new AVM2FinalProcessLocalData(loops, ((AVM2LocalData) localData).localRegNames, ((AVM2LocalData) localData).setLocalPosToGetLocalPos);
@@ -2496,6 +2684,11 @@ public class AVM2Graph extends Graph {
return finalProcess;
}
/**
* Prepares local data for branch.
* @param localData Local data
* @return Local data for a branch
*/
@Override
public AVM2LocalData prepareBranchLocalData(BaseLocalData localData) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
@@ -2506,6 +2699,13 @@ public class AVM2Graph extends Graph {
return ret;
}
/**
* Checks switch statement.
* @param localData Local data
* @param switchItem Switch item
* @param otherSides Other sides
* @param output Output
*/
@Override
protected void checkSwitch(BaseLocalData localData, SwitchItem switchItem, Collection<? extends GraphTargetItem> otherSides, List<GraphTargetItem> output) {
if (output.isEmpty()) {
@@ -2539,6 +2739,11 @@ public class AVM2Graph extends Graph {
}
}
/**
* Checks if part is a switch.
* @param part Part
* @return True if part is a switch
*/
@Override
protected boolean partIsSwitch(GraphPart part) {
if (part.end < 0) {
@@ -2547,6 +2752,12 @@ public class AVM2Graph extends Graph {
return avm2code.code.get(part.end).definition instanceof LookupSwitchIns;
}
/**
* Gets throw states.
* @param localData Local data
* @param allParts All parts
* @return List of ThrowStates
*/
@Override
protected List<ThrowState> getThrowStates(BaseLocalData localData, Set<GraphPart> allParts) {
@@ -2628,6 +2839,15 @@ public class AVM2Graph extends Graph {
return ret;
}
/**
* Walk catch parts register.
* @param registerId Register id
* @param part Part
* @param startIp Start ip
* @param catchParts Catch parts
* @param path Path
* @param visited Visited
*/
private void walkCatchPartsReg(int registerId, GraphPart part, int startIp, Set<GraphPart> catchParts, List<GraphPart> path, Set<GraphPart> visited) {
if (visited.contains(part)) {
return;
@@ -2661,6 +2881,16 @@ public class AVM2Graph extends Graph {
}
}
/**
* Walk catch parts.
* @param stats Code stats
* @param part Part
* @param startIp Start ip
* @param catchParts Catch parts
* @param scopePos Scope position
* @param allParts All parts
* @param isFinally True if the catch is finally
*/
private void walkCatchParts(CodeStats stats, GraphPart part, int startIp, Set<GraphPart> catchParts, int scopePos, Set<GraphPart> allParts, boolean isFinally) {
if (catchParts.contains(part)) {
return;
@@ -2704,19 +2934,13 @@ public class AVM2Graph extends Graph {
part.nextParts.add(secondPart);
secondPart.refs.add(part);
secondPart.type = GraphPart.TYPE_NONE;
secondPart.discoveredTime = part.discoveredTime;
secondPart.closedTime = part.closedTime;
secondPart.finishedTime = part.finishedTime;
secondPart.iloop_header = part.iloop_header;
secondPart.irreducible = part.irreducible;
secondPart.level = part.level;
secondPart.numBlocks = part.numBlocks;
secondPart.order = part.order;
secondPart.path = part.path;
secondPart.posX = part.posX;
secondPart.posY = part.posY;
secondPart.traversed = true;
allParts.add(secondPart);
}
catchParts.add(part);
@@ -2729,6 +2953,12 @@ public class AVM2Graph extends Graph {
}
}
/**
* Moves all stack items to commands.
* (If it's not a branch stack resistant or other special case)
* @param commands Commands
* @param stack Stack
*/
public void makeAllCommands(List<GraphTargetItem> commands, TranslateStack stack) {
for (int i = 0; i < stack.size(); i++) {
//These are often obfuscated, so ignore them
@@ -2740,6 +2970,12 @@ public class AVM2Graph extends Graph {
super.makeAllCommands(commands, stack);
}
/**
* Prepares second pass data.
* Can return null when no second pass will happen.
* @param list List of GraphTargetItems
* @return Second pass data or null
*/
@Override
protected SecondPassData prepareSecondPass(List<GraphTargetItem> list) {
return new SecondPassData();
@@ -23,49 +23,91 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.ConvertOutput;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
*
* AVM2 graph source.
* @author JPEXS
*/
public class AVM2GraphSource extends GraphSource {
/**
* AVM2 code
*/
private final AVM2Code code;
/**
* Is static
*/
boolean isStatic;
/**
* Class index
*/
int classIndex;
/**
* Script index
*/
int scriptIndex;
/**
* Local registers - map of register index to value item
*/
HashMap<Integer, GraphTargetItem> localRegs;
/**
* ABC
*/
ABC abc;
/**
* Method body
*/
MethodBody body;
/**
* Local register names - map of register index to name
*/
HashMap<Integer, String> localRegNames;
/**
* Fully qualified names
*/
List<DottedChain> fullyQualifiedNames;
/**
* Local register assignment IPs - map of register index to IP
*/
HashMap<Integer, Integer> localRegAssigmentIps;
/**
* Get AVM2 code
* @return AVM2 code
*/
public AVM2Code getCode() {
return code;
}
/**
* Constructs a new AVM2 graph source
* @param code AVM2 code
* @param isStatic Is static
* @param scriptIndex Script index
* @param classIndex Class index
* @param localRegs Local registers
* @param abc ABC
* @param body Method body
* @param localRegNames Local register names
* @param fullyQualifiedNames Fully qualified names
* @param localRegAssigmentIp Local register assignment IPs
*/
public AVM2GraphSource(AVM2Code code, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, ABC abc, MethodBody body, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames, HashMap<Integer, Integer> localRegAssigmentIp) {
this.code = code;
this.isStatic = isStatic;
@@ -80,11 +122,20 @@ public class AVM2GraphSource extends GraphSource {
code.calculateDebugFileLine(abc);
}
/**
* Gets important addresses
* @return Important addresses
*/
@Override
public Set<Long> getImportantAddresses() {
return code.getImportantOffsets(body, false);
}
/**
* Converts instruction at the specified position to string
* @param pos Position of the instruction
* @return Instruction as string
*/
@Override
public String insToString(int pos) {
if (pos < code.code.size()) {
@@ -93,21 +144,47 @@ public class AVM2GraphSource extends GraphSource {
return "";
}
/**
* Gets the size of the graph source
* @return The size of the graph source
*/
@Override
public int size() {
return code.code.size();
}
/**
* Gets the graph source item at the specified position
* @param pos Position of the graph source item
* @return The graph source item at the specified position
*/
@Override
public AVM2Instruction get(int pos) {
return code.code.get(pos);
}
/**
* Checks if the graph source is empty
* @return True if the graph source is empty, false otherwise
*/
@Override
public boolean isEmpty() {
return code.code.isEmpty();
}
/**
* Translates the part of the graph source
* @param graph Graph
* @param part Graph part
* @param localData Local data
* @param stack Translate stack
* @param start Start position
* @param end End position
* @param staticOperation Unused
* @param path Path
* @return List of graph target items
* @throws InterruptedException
*/
@Override
public List<GraphTargetItem> translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException {
List<GraphTargetItem> ret = new ArrayList<>();
@@ -118,16 +195,32 @@ public class AVM2GraphSource extends GraphSource {
return ret;
}
/**
* Converts address to position
* @param adr Address
* @return Position
*/
@Override
public int adr2pos(long adr) {
return code.adr2pos(adr);
}
/**
* Converts address to position
* @param adr Address
* @param nearest Nearest
* @return Position
*/
@Override
public int adr2pos(long adr, boolean nearest) {
return code.adr2pos(adr, true);
}
/**
* Converts position to address
* @param pos Position
* @return Address
*/
@Override
public long pos2adr(int pos) {
return code.pos2adr(pos);
@@ -0,0 +1,4 @@
/**
* AVM2 control flow graph.
*/
package com.jpexs.decompiler.flash.abc.avm2.graph;
@@ -32,43 +32,63 @@ import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.ecma.EcmaScript;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Helper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
*
* AVM2 instruction.
* @author JPEXS
*/
public class AVM2Instruction implements Cloneable, GraphSourceItem {
/**
* Definition
*/
public InstructionDefinition definition;
/**
* Operands
*/
public int[] operands;
/**
* Address
*/
private long address;
/**
* Comment
*/
public String comment;
/**
* Ignored
*/
private boolean ignored = false;
/**
* Line
*/
private int line;
/**
* File
*/
private String file;
/**
* Virtual address - used for deobfuscation
*/
private long virtualAddress = -1;
/**
* Old style names for getlocal and setlocal
*/
private static final Map<String, String> oldStyleNames = new HashMap<>();
static {
@@ -78,11 +98,19 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
}
}
/**
* Gets file offset.
* @return File offset
*/
@Override
public long getFileOffset() {
return -1;
}
/**
* Gets the line offset.
* @return Line offset
*/
@Override
public long getLineOffset() {
if (virtualAddress > -1) {
@@ -91,21 +119,42 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return getAddress();
}
/**
* Sets file and line.
* @param file File
* @param line Line
*/
public void setFileLine(String file, int line) {
this.file = file;
this.line = line;
}
public AVM2Instruction(long offset, int insructionCode, int[] operands) {
this(offset, AVM2Code.instructionSet[insructionCode], operands);
/**
* Constructs a new AVM2 instruction.
* @param offset Offset
* @param instructionCode Instruction code
* @param operands Operands
*/
public AVM2Instruction(long offset, int instructionCode, int[] operands) {
this(offset, AVM2Code.instructionSet[instructionCode], operands);
}
/**
* Constructs a new AVM2 instruction.
* @param address Address
* @param definition Definition
* @param operands Operands
*/
public AVM2Instruction(long address, InstructionDefinition definition, int[] operands) {
this.definition = definition;
this.operands = operands != null && operands.length > 0 ? operands : null;
this.address = address;
}
/**
* Gets the bytes.
* @return Bytes
*/
public byte[] getBytes() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
@@ -142,6 +191,10 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return bos.toByteArray();
}
/**
* Gets the length of the bytes.
* @return Length of the bytes
*/
@Override
public int getBytesLength() {
int cnt = 1;
@@ -174,6 +227,10 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return cnt;
}
/**
* Gets the offsets.
* @return Offsets
*/
public List<Long> getOffsets() {
List<Long> ret = new ArrayList<>();
String s = "";
@@ -196,6 +253,12 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return ret;
}
/**
* Gets the parameter.
* @param constants Constants
* @param idx Index of operand
* @return Parameter
*/
public Object getParam(AVM2ConstantPool constants, int idx) {
//if (idx < 0 || idx >= definition.operands.length) {
// return null;
@@ -223,10 +286,22 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
}
}
/**
* Gets the parameter as a long.
* @param constants Constants
* @param idx Index of operand
* @return Parameter as a long
*/
public Long getParamAsLong(AVM2ConstantPool constants, int idx) {
return (Long) getParam(constants, idx);
}
/**
* Gets all parameters as string.
* @param constants Constants
* @param fullyQualifiedNames Fully qualified names
* @return All parameters as string
*/
public String getParams(AVM2ConstantPool constants, List<DottedChain> fullyQualifiedNames) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < definition.operands.length; i++) {
@@ -398,6 +473,10 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return s.toString();
}
/**
* Gets the comment.
* @return Comment
*/
public String getComment() {
if (isIgnored()) {
return " ;ignored";
@@ -408,11 +487,19 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return " ;" + comment;
}
/**
* Checks whether this item is ignored.
* @return True if this item is ignored, false otherwise
*/
@Override
public boolean isIgnored() {
return ignored;
}
/**
* To string.
* @return String
*/
@Override
public String toString() {
StringBuilder s = new StringBuilder();
@@ -426,12 +513,22 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return s.toString();
}
/**
* To string.
* @param writer Writer
* @param localData Local data
* @return Writer
*/
public GraphTextWriter toString(GraphTextWriter writer, LocalData localData) {
writer.appendNoHilight(Helper.formatAddress(address) + " " + String.format("%-30s", Helper.byteArrToString(getBytes())) + getCustomizedInstructionName());
writer.appendNoHilight(getParams(localData.constantsAvm2, localData.fullyQualifiedNames) + getComment());
return writer;
}
/**
* Gets the customized instruction name.
* @return Customized instruction name
*/
private String getCustomizedInstructionName() {
if (Configuration.useOldStyleGetSetLocalsAs3PCode.get() && oldStyleNames.containsKey(definition.instructionName)) {
return oldStyleNames.get(definition.instructionName);
@@ -439,6 +536,12 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return definition.instructionName;
}
/**
* To string without address.
* @param constants Constants
* @param fullyQualifiedNames Fully qualified names
* @return String without address
*/
public String toStringNoAddress(AVM2ConstantPool constants, List<DottedChain> fullyQualifiedNames) {
String s = getCustomizedInstructionName();
if (Configuration.padAs3PCodeInstructionName.get()) {
@@ -450,6 +553,15 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return s.trim();
}
/**
* Translate the item to target items.
* @param localData Local data
* @param stack Stack
* @param output Output list
* @param staticOperation Unused
* @param path Path
* @throws InterruptedException
*/
@Override
public void translate(BaseLocalData localData, TranslateStack stack, List<GraphTargetItem> output, int staticOperation, String path) throws InterruptedException {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
@@ -460,58 +572,114 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
}*/
}
/**
* Gets the number of stack items that are popped by this item.
* @param localData Local data
* @param stack Stack
* @return Number of stack items that are popped by this item
*/
@Override
public int getStackPopCount(BaseLocalData localData, TranslateStack stack) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
return getStackPopCount(aLocalData);
}
/**
* Gets the number of stack items that are popped by this item.
* @param aLocalData Local data
* @return Number of stack items that are popped by this item
*/
public int getStackPopCount(AVM2LocalData aLocalData) {
return definition.getStackPopCount(this, aLocalData.abc);
}
/**
* Gets the number of stack items that are pushed by this item.
* @param localData Local data
* @param stack Stack
* @return Number of stack items that are pushed by this item
*/
@Override
public int getStackPushCount(BaseLocalData localData, TranslateStack stack) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
return getStackPushCount(aLocalData);
}
/**
* Gets the number of stack items that are pushed by this item.
* @param aLocalData Local data
* @return Number of stack items that are pushed by this item
*/
public int getStackPushCount(AVM2LocalData aLocalData) {
return definition.getStackPushCount(this, aLocalData.abc);
}
/**
* Checks whether this item is a jump.
* @return True if this item is a jump, false otherwise
*/
@Override
public boolean isJump() {
return definition instanceof JumpIns;
}
/**
* Checks whether this item is a branch.
* @return True if this item is a branch, false otherwise
*/
@Override
public boolean isBranch() {
return (definition instanceof IfTypeIns) || (definition instanceof LookupSwitchIns);
}
/**
* Checks whether this item is an exit (throw, return, etc.).
* @return True if this item is an exit, false otherwise
*/
@Override
public boolean isExit() {
return (definition instanceof ReturnValueIns) || (definition instanceof ReturnVoidIns) || (definition instanceof ThrowIns);
}
/**
* Gets the address.
* @return Address
*/
@Override
public long getAddress() {
return address;
}
/**
* Sets the address.
* @param address Address
*/
public void setAddress(long address) {
this.address = address;
}
/**
* Gets the target address of jump.
* @return Target address.
*/
public long getTargetAddress() {
return address + 4 /*getBytesLength()*/ + operands[0];
}
/**
* Sets the target offset of jump.
* @param offset Offset
*/
public void setTargetOffset(int offset) {
operands[0] = offset;
}
/**
* Gets branches
* @param code Code
* @return List of IPs to branch to
*/
@Override
public List<Integer> getBranches(GraphSource code) {
List<Integer> ret = new ArrayList<>();
@@ -531,21 +699,39 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
return ret;
}
/**
* Checks whether the loops are ignored.
* @return True if the loops are ignored, false otherwise
*/
@Override
public boolean ignoredLoops() {
return false;
}
/**
* Sets whether this item is ignored.
* @param ignored True if this item is ignored, false otherwise
* @param pos Sub position
*/
@Override
public void setIgnored(boolean ignored, int pos) {
this.ignored = ignored;
}
/**
* Checks whether this item is a DeobfuscatePop instruction.
* It is a special instruction for deobfuscation.
* @return True if this item is a DeobfuscatePop instruction, false otherwise
*/
@Override
public boolean isDeobfuscatePop() {
return definition instanceof DeobfuscatePopIns;
}
/**
* Clone the instruction.
* @return Cloned instruction
*/
@Override
public AVM2Instruction clone() {
try {
@@ -559,11 +745,19 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
}
}
/**
* Gets the line in the high level source code.
* @return Line
*/
@Override
public int getLine() {
return line;
}
/**
* Gets the high level source code file name.
* @return File name
*/
@Override
public String getFile() {
return file;
@@ -571,12 +765,12 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
/**
* Set operand value the right way - update offsets neccessarily. Because
* some operand types are variable length (like U30)
* some operand types are variable length (like U30).
*
* @param operandIndex
* @param newValue
* @param code
* @param body
* @param operandIndex Index of operand
* @param newValue New value
* @param code Code
* @param body Method body
*/
public void setOperand(int operandIndex, int newValue, AVM2Code code, MethodBody body) {
int oldByteCount = getBytesLength();
@@ -591,11 +785,11 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
/**
* Set operand values the right way - update offsets neccessarily. Because
* some operand types are variable length (like U30)
* some operand types are variable length (like U30).
*
* @param operands
* @param code
* @param body
* @param operands Operands
* @param code Code
* @param body Method body
*/
public void setOperands(int[] operands, AVM2Code code, MethodBody body) {
int oldByteCount = getBytesLength();
@@ -608,11 +802,23 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
body.setModified();
}
/**
* Gets virtual address. A virtual address can be used for storing original
* address before applying deobfuscation.
*
* @return Virtual address
*/
@Override
public long getVirtualAddress() {
return virtualAddress;
}
/**
* Sets virtual address. A virtual address can be used for storing original
* address before applying deobfuscation.
*
* @param virtualAddress Virtual address
*/
@Override
public void setVirtualAddress(long virtualAddress) {
this.virtualAddress = virtualAddress;
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash.abc.avm2.instructions;
/**
*
* Flags for AVM2 instructions.
* @author JPEXS
*/
public enum AVM2InstructionFlag {
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.flash.abc.avm2.instructions;
/**
*
* Set of AVM2 instructions.
* @author JPEXS
*/
public class AVM2Instructions {
@@ -21,31 +21,61 @@ import com.jpexs.decompiler.flash.abc.AVM2LocalData;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import java.util.List;
/**
*
* DeobfuscatePop instruction.
* Special pop pused for deobfuscation purposes.
* @author JPEXS
*/
public class DeobfuscatePopIns extends PopIns {
/**
* Instruction name
*/
public static final String NAME = "ffdec_deobfuscatepop";
/**
* Singleton instance
*/
private static final DeobfuscatePopIns instance = new DeobfuscatePopIns();
/**
* Returns singleton instance
* @return Singleton instance
*/
public static final DeobfuscatePopIns getInstance() {
return instance;
}
/**
* Constructs new instance
*/
private DeobfuscatePopIns() {
instructionName = NAME;
}
/**
* Translates instruction to high level code.
* @param localData Local data area
* @param stack Translate stack
* @param ins Instruction
* @param output Output
* @param path Path
*/
@Override
public void translate(AVM2LocalData localData, TranslateStack stack, AVM2Instruction ins, List<GraphTargetItem> output, String path) {
stack.pop(); //Just ignore the value
}
/**
* Gets number of pops from stack.
* @param ins Instruction
* @param abc ABC
* @return Number of pops from stack
*/
@Override
public int getStackPopCount(AVM2Instruction ins, ABC abc) {
return 1;
@@ -19,13 +19,21 @@ package com.jpexs.decompiler.flash.abc.avm2.instructions;
import com.jpexs.decompiler.flash.abc.AVM2LocalData;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import java.util.HashMap;
/**
*
* IfType instruction interface.
* @author JPEXS
*/
public interface IfTypeIns {
public abstract void translateInverted(AVM2LocalData localData, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, AVM2Instruction ins);
/**
* Translates the instruction as inverted.
* @param localData Local data
* @param localRegs Local registers
* @param stack Stack
* @param ins Instruction
*/
public void translateInverted(AVM2LocalData localData, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, AVM2Instruction ins);
}
@@ -23,35 +23,9 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea;
import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2ExecutionException;
import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2VerifyErrorException;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns;
import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ClassAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ConstructAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ConvertAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.DecrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.GetLexAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.GlobalAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.IncrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.InitPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewActivationAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ThisAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.*;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item;
@@ -61,43 +35,57 @@ import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitWithSlot;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.decompiler.graph.model.DuplicateItem;
import com.jpexs.helpers.Reference;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.*;
/**
*
* AVM2 Instruction definition.
* @author JPEXS
*/
public abstract class InstructionDefinition implements Serializable {
/**
* Serial version UID
*/
public static final long serialVersionUID = 1L;
/**
* Operands
*/
public int[] operands;
/**
* Instruction name
*/
public String instructionName = "";
/**
* Instruction code
*/
public int instructionCode = 0;
/**
* Can throw exception
*/
public boolean canThrow;
/**
* Flags
*/
public AVM2InstructionFlag[] flags;
/**
* Constructs new instance
* @param instructionCode Instruction code
* @param instructionName Instruction name
* @param operands Operands
* @param canThrow Can throw exception
* @param flags Flags
*/
public InstructionDefinition(int instructionCode, String instructionName, int[] operands, boolean canThrow, AVM2InstructionFlag... flags) {
this.instructionCode = instructionCode;
this.instructionName = instructionName;
@@ -106,6 +94,11 @@ public abstract class InstructionDefinition implements Serializable {
this.flags = flags;
}
/**
* Checks if instruction has flag
* @param flag Flag
* @return True if instruction has flag
*/
public boolean hasFlag(AVM2InstructionFlag flag) {
for (AVM2InstructionFlag f : flags) {
if (f == flag) {
@@ -115,6 +108,10 @@ public abstract class InstructionDefinition implements Serializable {
return false;
}
/**
* To string
* @return String representation
*/
@Override
public String toString() {
StringBuilder s = new StringBuilder();
@@ -125,6 +122,13 @@ public abstract class InstructionDefinition implements Serializable {
return s.toString();
}
/**
* Verify instruction
* @param lda Local data area
* @param constants Constant pool
* @param ins Instruction
* @throws AVM2VerifyErrorException
*/
public void verify(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) throws AVM2VerifyErrorException {
for (int i = 0; i < operands.length; i++) {
int operand = operands[i];
@@ -177,22 +181,77 @@ public abstract class InstructionDefinition implements Serializable {
}
}
/**
* Checks if instruction cannot be statically computed.
* @return True = cannot be statically computed, false = can be statically computed
*/
public boolean isNotCompileTimeSupported() {
return false;
}
/**
* Executes instruction.
* @param lda Local data area
* @param constants Constant pool
* @param ins Instruction
* @return True if instruction was executed, false if not
* @throws AVM2ExecutionException
*/
public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) throws AVM2ExecutionException {
//throw new UnsupportedOperationException("Instruction " + instructionName + " not implemented");
return false;
}
/**
* Throws illegal opcode exception.
* @param lda Local data area
* @param ins Instruction
* @throws AVM2VerifyErrorException
*/
protected void illegalOpCode(LocalDataArea lda, AVM2Instruction ins) throws AVM2VerifyErrorException {
throw new AVM2VerifyErrorException(AVM2VerifyErrorException.ILLEGAL_OPCODE, lda.isDebug(), new Object[]{lda.methodName, instructionCode, ins.getAddress()});
}
/**
* Translates instruction to high level code.
* @param localData Local data area
* @param stack Translate stack
* @param ins Instruction
* @param output Output
* @param path Path
* @throws InterruptedException
*/
public void translate(AVM2LocalData localData, TranslateStack stack, AVM2Instruction ins, List<GraphTargetItem> output, String path) throws InterruptedException {
}
/**
* Translates instruction to high level code.
* @param switchParts Switch parts
* @param callStack Call stack
* @param abcIndex ABC indexing
* @param setLocalPosToGetLocalPos Set local position to get local position
* @param lineStartItem Line start item
* @param isStatic Is static
* @param scriptIndex Script index
* @param classIndex Class index
* @param localRegs Local registers
* @param stack Translate stack
* @param scopeStack Scope stack
* @param localScopeStack Local scope stack
* @param ins Instruction
* @param output Output
* @param body Method body
* @param abc ABC
* @param localRegNames Local register names
* @param localRegTypes Local register types
* @param fullyQualifiedNames Fully qualified names
* @param path Path
* @param localRegsAssignmentIps Local registers assignment IPs
* @param ip IP
* @param code AVM2 code
* @param thisHasDefaultToPrimitive This has default to primitive
* @throws InterruptedException
*/
public void translate(Set<GraphPart> switchParts, List<MethodBody> callStack, AbcIndexing abcIndex, Map<Integer, Set<Integer>> setLocalPosToGetLocalPos, Reference<GraphSourceItem> lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List<GraphTargetItem> output, MethodBody body, ABC abc, HashMap<Integer, String> localRegNames, HashMap<Integer, GraphTargetItem> localRegTypes, List<DottedChain> fullyQualifiedNames, String path, HashMap<Integer, Integer> localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive) throws InterruptedException {
AVM2LocalData localData = new AVM2LocalData();
localData.allSwitchParts = switchParts;
@@ -219,14 +278,32 @@ public abstract class InstructionDefinition implements Serializable {
lineStartItem.setVal(localData.lineStartInstruction);
}
/**
* Gets number of pops from stack.
* @param ins Instruction
* @param abc ABC
* @return Number of pops from stack
*/
public int getStackPopCount(AVM2Instruction ins, ABC abc) {
return 0;
}
/**
* Gets number of pushes to stack.
* @param ins Instruction
* @param abc ABC
* @return Number of pushes to stack
*/
public int getStackPushCount(AVM2Instruction ins, ABC abc) {
return 0;
}
/**
* Resolves multiname.
* @param localData Local data area
* @param constants Constant pool
* @param multinameIndex Multiname index
*/
protected void resolveMultiname(LocalDataArea localData, AVM2ConstantPool constants, int multinameIndex) {
if (multinameIndex > 0 && multinameIndex < constants.getMultinameCount()) {
Multiname multiname = constants.getMultiname(multinameIndex);
@@ -239,6 +316,16 @@ public abstract class InstructionDefinition implements Serializable {
}
}
/**
* Resolves multiname.
* @param localData Local data area
* @param property Property
* @param stack Translate stack
* @param constants Constant pool
* @param multinameIndex Multiname index
* @param ins Instruction
* @return Resolved multiname
*/
protected FullMultinameAVM2Item resolveMultiname(AVM2LocalData localData, boolean property, TranslateStack stack, AVM2ConstantPool constants, int multinameIndex, AVM2Instruction ins) {
GraphTargetItem ns = null;
GraphTargetItem name = null;
@@ -255,6 +342,12 @@ public abstract class InstructionDefinition implements Serializable {
return new FullMultinameAVM2Item(property, ins, localData.lineStartInstruction, multinameIndex, localData.abc.constants.getMultiname(multinameIndex).getName(localData.getConstants(), new ArrayList<>(), true, true), name, ns);
}
/**
* Gets required stack size for multiname.
* @param constants Constant pool
* @param multinameIndex Multiname index
* @return Required stack size
*/
protected int getMultinameRequiredStackSize(AVM2ConstantPool constants, int multinameIndex) {
int res = 0;
if (multinameIndex > 0 && multinameIndex < constants.getMultinameCount()) {
@@ -274,78 +367,40 @@ public abstract class InstructionDefinition implements Serializable {
return res;
}
protected int resolvedCount(AVM2ConstantPool constants, int multinameIndex) {
int pos = 0;
if (constants.getMultiname(multinameIndex).needsNs()) {
pos++;
}
if (constants.getMultiname(multinameIndex).needsName()) {
pos++;
}
return pos;
}
protected String resolveMultinameNoPop(int pos, Stack<AVM2Item> stack, AVM2ConstantPool constants, int multinameIndex, AVM2Instruction ins, List<DottedChain> fullyQualifiedNames) {
String ns = "";
String name;
if (constants.getMultiname(multinameIndex).needsNs()) {
ns = "[" + stack.get(pos) + "]";
pos++;
}
if (constants.getMultiname(multinameIndex).needsName()) {
name = stack.get(pos).toString();
} else {
name = GraphTextWriter.hilighOffset(constants.getMultiname(multinameIndex).getName(constants, fullyQualifiedNames, false, true), ins.getAddress());
}
return name + ns;
}
/**
* Gets stack delta. Stack push count - stack pop count.
* @param ins Instruction
* @param abc ABC
* @return Stack delta
*/
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return getStackPushCount(ins, abc) - getStackPopCount(ins, abc);
}
/**
* Gets scope stack delta. Scope stack push count - scope stack pop count.
* @param ins Instruction
* @param abc ABC
* @return Scope stack delta
*/
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
return 0;
}
protected boolean isRegisterCompileTime(int regId, int ip, HashMap<Integer, List<Integer>> refs, AVM2Code code) {
Set<Integer> previous = new HashSet<>();
AVM2Code.getPreviousReachableIps(ip, refs, previous, new HashSet<>());
for (int p : previous) {
if (p < 0) {
continue;
}
if (p >= code.code.size()) {
continue;
}
AVM2Instruction sins = code.code.get(p);
if (code.code.get(p).definition instanceof SetLocalTypeIns) {
SetLocalTypeIns sl = (SetLocalTypeIns) sins.definition;
if (sl.getRegisterId(sins) == regId) {
if (!AVM2Code.isDirectAncestor(ip, p, refs)) {
return false;
}
}
}
if ((code.code.get(p).definition instanceof IncLocalIns)
|| (code.code.get(p).definition instanceof IncLocalIIns)
|| (code.code.get(p).definition instanceof DecLocalIns)
|| (code.code.get(p).definition instanceof DecLocalIIns)) {
if (sins.operands[0] == regId) {
if (!AVM2Code.isDirectAncestor(ip, p, refs)) {
return false;
}
}
}
}
return true;
}
/**
* Checks if instruction is exit instruction. (e.g. return, throw)
* @return True if instruction is exit instruction
*/
public boolean isExitInstruction() {
return false;
}
/**
* Gets item IP.
* @param localData Local data
* @param item Item
* @return Item IP or -1 if not found
*/
public static int getItemIp(AVM2LocalData localData, GraphTargetItem item) {
GraphSourceItem src = item.getSrc();
if (src == null) {
@@ -354,10 +409,27 @@ public abstract class InstructionDefinition implements Serializable {
return localData.code.adr2pos(src.getAddress());
}
/**
* Searches for slot name.
* @param slotIndex Slot index
* @param localData Local data
* @param obj Object
* @param realObj Real object
* @return Slot multiname or null if not found
*/
protected static Multiname searchSlotName(int slotIndex, AVM2LocalData localData, GraphTargetItem obj, Reference<GraphTargetItem> realObj) {
return searchSlotName(slotIndex, localData, obj, -1, realObj);
}
/**
* Searches for slot name.
* @param slotIndex Slot index
* @param localData Local data
* @param obj Object
* @param multiNameIndex Multiname index
* @param realObj Real object
* @return Slot multiname or null if not found
*/
private static Multiname searchSlotName(int slotIndex, AVM2LocalData localData, GraphTargetItem obj, int multiNameIndex, Reference<GraphTargetItem> realObj) {
if ((obj instanceof ExceptionAVM2Item) && (multiNameIndex == -1 || ((ExceptionAVM2Item) obj).exception.name_index == multiNameIndex)) {
return localData.getConstants().getMultiname(((ExceptionAVM2Item) obj).exception.name_index);
@@ -402,6 +474,15 @@ public abstract class InstructionDefinition implements Serializable {
return null;
}
/**
* Handles set property.
* @param init Init
* @param localData Local data
* @param stack Translate stack
* @param ins Instruction
* @param output Output
* @param path Path
*/
public void handleSetProperty(boolean init, AVM2LocalData localData, TranslateStack stack, AVM2Instruction ins, List<GraphTargetItem> output, String path) {
int multinameIndex = ins.operands[0];
GraphTargetItem value = stack.pop();
@@ -649,6 +730,18 @@ public abstract class InstructionDefinition implements Serializable {
SetTypeIns.handleResult(value, stack, output, localData, (GraphTargetItem) result, -1, type.getVal());
}
/**
* Checks if increment or decrement.
* @param standalone Standalone
* @param multinameIndex Multiname index
* @param ins Instruction
* @param localData Local data
* @param item Item
* @param valueLocalReg Value local register
* @param nameLocalReg Name local register
* @param objLocalReg Object local register
* @return Increment or decrement item or null if not found
*/
private GraphTargetItem checkIncDec(boolean standalone, int multinameIndex, AVM2Instruction ins, AVM2LocalData localData, GraphTargetItem item,
LocalRegAVM2Item valueLocalReg, LocalRegAVM2Item nameLocalReg, LocalRegAVM2Item objLocalReg) {
if (item instanceof SetLocalAVM2Item) {
@@ -17,23 +17,26 @@
package com.jpexs.decompiler.flash.abc.avm2.instructions;
import com.jpexs.decompiler.flash.abc.AVM2LocalData;
import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.ConvertAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.*;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.DuplicateItem;
import java.util.List;
/**
*
* SetType instruction interface.
* @author JPEXS
*/
public interface SetTypeIns {
/**
* Handles number to int conversion.
* @param value Value to convert
* @param type Type to convert to
* @return Value
*/
public static GraphTargetItem handleNumberToInt(GraphTargetItem value, GraphTargetItem type) {
if ((value instanceof ConvertAVM2Item) || (value instanceof CoerceAVM2Item)) {
if (type != null && (type.equals(TypeItem.INT) || type.equals(TypeItem.UINT))) {
@@ -45,6 +48,16 @@ public interface SetTypeIns {
return value;
}
/**
* Handles result.
* @param value Value
* @param stack Stack
* @param output Output
* @param localData Local data
* @param result Result
* @param regId Register ID
* @param type Type
*/
public static void handleResult(GraphTargetItem value, TranslateStack stack, List<GraphTargetItem> output, AVM2LocalData localData, GraphTargetItem result, int regId, GraphTargetItem type) {
GraphTargetItem notCoercedValue = value;
if ((value instanceof CoerceAVM2Item) || (value instanceof ConvertAVM2Item)) {
@@ -22,20 +22,39 @@ import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2ExecutionException;
import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2VerifyErrorException;
/**
*
* Unknown instruction definition.
* @author JPEXS
*/
public class UnknownInstruction extends InstructionDefinition {
/**
* Constructs a new UnknownInstruction object.
* @param instructionCode Instruction code
*/
public UnknownInstruction(int instructionCode) {
super(instructionCode, "instruction_" + Integer.toString(instructionCode), new int[0], false);
}
/**
* Verify instruction
* @param lda Local data area
* @param constants Constant pool
* @param ins Instruction
* @throws AVM2VerifyErrorException
*/
@Override
public void verify(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) throws AVM2VerifyErrorException {
illegalOpCode(lda, ins);
}
/**
* Executes instruction.
* @param lda Local data area
* @param constants Constant pool
* @param ins Instruction
* @return True if instruction was executed, false if not
* @throws AVM2ExecutionException
*/
@Override
public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) throws AVM2ExecutionException {
return false;
@@ -0,0 +1,4 @@
/**
* Alchemy (domain memory) AVM2 instructions
*/
package com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy;
@@ -0,0 +1,4 @@
/**
* Arithmetic AVM2 instructions.
*/
package com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic;
@@ -0,0 +1,4 @@
/**
* Bitwise AVM2 instructions.
*/
package com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise;
@@ -0,0 +1,4 @@
/**
* Comparison AVM2 instructions.
*/
package com.jpexs.decompiler.flash.abc.avm2.instructions.comparison;

Some files were not shown because too many files have changed in this diff Show More