AVM2 instruction reading fix, strongly typed Cache key

This commit is contained in:
honfika@gmail.com
2014-09-07 12:29:27 +02:00
parent 946018749e
commit 6cfd01dc3d
10 changed files with 153 additions and 81 deletions

View File

@@ -2203,7 +2203,7 @@ public final class SWF implements TreeItem, Timelined {
mat.translateX, mat.translateY);
}
private static Cache<SerializableImage> frameCache = Cache.getInstance(false);
private static Cache<String, SerializableImage> frameCache = Cache.getInstance(false);
public static SerializableImage getFromCache(String key) {
if (frameCache.contains(key)) {

View File

@@ -42,7 +42,6 @@ public class ABCInputStream implements AutoCloseable {
private static final int CLASS_PROTECTED_NS = 8;
private static final int ATTR_METADATA = 4;
private final MemoryInputStream is;
private long bytesRead = 0;
private ByteArrayOutputStream bufferOs = null;
public static final boolean DEBUG_READ = false;
public DumpInfo dumpInfo;
@@ -102,8 +101,15 @@ public class ABCInputStream implements AutoCloseable {
}
}
public void endDumpLevelUntil(DumpInfo di) {
if (di != null) {
while (dumpInfo != null && dumpInfo != di) {
endDumpLevel();
}
}
}
private int readInternal() throws IOException {
bytesRead++;
int i = is.read();
if (i == -1) {
throw new EndOfStreamException();
@@ -128,7 +134,6 @@ public class ABCInputStream implements AutoCloseable {
private int read(byte[] b) throws IOException {
int currBytesRead = is.read(b);
bytesRead += currBytesRead;
if (DEBUG_READ) {
StringBuilder sb = new StringBuilder("Read[");
sb.append(currBytesRead);
@@ -478,7 +483,7 @@ public class ABCInputStream implements AutoCloseable {
bytesRead=0;
}*/
public long getPosition() {
return bytesRead;
return is.getPos();
}
@Override

View File

@@ -243,6 +243,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -784,59 +785,77 @@ public class AVM2Code implements Cloneable {
}
public AVM2Code(ABCInputStream ais) throws IOException {
try {
while (ais.available() > 0) {
DumpInfo di = ais.newDumpLevel("instruction", "instruction");
long startOffset = ais.getPosition();
int instructionCode = ais.read("instructionCode");
InstructionDefinition instr = instructionSetByCode[instructionCode];
if (di != null) {
di.name = instr.instructionName;
}
if (instr != null) {
int[] actualOperands = null;
if (instructionCode == 0x1b) { //switch
int firstOperand = ais.readS24("default_offset");
int case_count = ais.readU30("case_count");
actualOperands = new int[case_count + 3];
actualOperands[0] = firstOperand;
actualOperands[1] = case_count;
for (int c = 0; c < case_count + 1; c++) {
actualOperands[2 + c] = ais.readS24("actualOperand");
}
} else {
if (instr.operands.length > 0) {
actualOperands = new int[instr.operands.length];
for (int op = 0; op < instr.operands.length; op++) {
switch (instr.operands[op] & 0xff00) {
case OPT_U30:
actualOperands[op] = ais.readU30("operand");
break;
case OPT_U8:
actualOperands[op] = ais.read("operand");
break;
case OPT_BYTE:
actualOperands[op] = (byte) ais.read("operand");
break;
case OPT_S24:
actualOperands[op] = ais.readS24("operand");
break;
Map<Long, AVM2Instruction> codeMap = new TreeMap<>();
DumpInfo diParent = ais.dumpInfo;
List<Long> addresses = new ArrayList<>();
addresses.add(ais.getPosition());
while (!addresses.isEmpty()) {
long address = addresses.remove(0);
if (codeMap.containsKey(address)) {
continue;
}
try {
ais.seek(address);
while (ais.available() > 0) {
DumpInfo di = ais.newDumpLevel("instruction", "instruction");
long startOffset = ais.getPosition();
int instructionCode = ais.read("instructionCode");
InstructionDefinition instr = instructionSetByCode[instructionCode];
if (di != null) {
di.name = instr.instructionName;
}
if (instr != null) {
int[] actualOperands = null;
if (instructionCode == 0x1b) { //switch
int firstOperand = ais.readS24("default_offset");
int case_count = ais.readU30("case_count");
actualOperands = new int[case_count + 3];
actualOperands[0] = firstOperand;
actualOperands[1] = case_count;
for (int c = 0; c < case_count + 1; c++) {
actualOperands[2 + c] = ais.readS24("actualOperand");
}
} else {
if (instr.operands.length > 0) {
actualOperands = new int[instr.operands.length];
for (int op = 0; op < instr.operands.length; op++) {
switch (instr.operands[op] & 0xff00) {
case OPT_U30:
actualOperands[op] = ais.readU30("operand");
break;
case OPT_U8:
actualOperands[op] = ais.read("operand");
break;
case OPT_BYTE:
actualOperands[op] = (byte) ais.read("operand");
break;
case OPT_S24:
actualOperands[op] = ais.readS24("operand");
break;
}
}
}
}
}
code.add(new AVM2Instruction(startOffset, instr, actualOperands));
ais.endDumpLevel(instr.instructionCode);
} else {
ais.endDumpLevel();
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
//throw new UnknownInstructionCode(instructionCode);
if (instr instanceof IfTypeIns) {
long target = ais.getPosition() + actualOperands[0];
addresses.add(target);
}
codeMap.put(startOffset, new AVM2Instruction(startOffset, instr, actualOperands));
ais.endDumpLevel(instr.instructionCode);
} else {
ais.endDumpLevel();
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
//throw new UnknownInstructionCode(instructionCode);
}
}
} catch (EndOfStreamException ex) {
// lookupswitch obfuscation, ignore
ais.endDumpLevelUntil(diParent);
}
} catch (EndOfStreamException ex) {
// lookupswitch obfuscation, ignore
}
code.addAll(codeMap.values());
}
public void compact() {
@@ -1065,7 +1084,6 @@ public class AVM2Code implements Cloneable {
}
}
}
long ofs = 0;
int ip = 0;
int largeLimit = 20000;
boolean markOffsets = code.size() <= largeLimit;
@@ -1074,6 +1092,7 @@ public class AVM2Code implements Cloneable {
Helper.byteArrayToHexWithHeader(writer, getBytes());
} else {
for (AVM2Instruction ins : code) {
long ofs = ins.offset;
if (exportMode == ScriptExportMode.PCODE_HEX) {
writer.appendNoHilight("; ");
writer.appendNoHilight(Helper.bytesToHexString(ins.getBytes()));
@@ -1144,7 +1163,6 @@ public class AVM2Code implements Cloneable {
outputMap.add(ip);
}
}
ofs += ins.getBytes().length;
ip++;
}
}

View File

@@ -23,6 +23,8 @@ import com.jpexs.decompiler.flash.abc.types.NamespaceSet;
import com.jpexs.helpers.utf8.Utf8PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AVM2ConstantPool {
@@ -117,35 +119,75 @@ public class AVM2ConstantPool {
}
public long getInt(int index) {
return constant_int.get(index);
try {
return constant_int.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return 0;
}
public Namespace getNamespace(int index) {
return constant_namespace.get(index);
try {
return constant_namespace.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return null;
}
public NamespaceSet getNamespaceSet(int index) {
return constant_namespace_set.get(index);
try {
return constant_namespace_set.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return null;
}
public Multiname getMultiname(int index) {
return constant_multiname.get(index);
try {
return constant_multiname.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return null;
}
public long getUInt(int index) {
return constant_uint.get(index);
try {
return constant_uint.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return 0;
}
public double getDouble(int index) {
return constant_double.get(index);
try {
return constant_double.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return 0;
}
public Decimal getDecimal(int index) {
return constant_decimal.get(index);
try {
return constant_decimal.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return null;
}
public String getString(int index) {
return constant_string.get(index);
try {
return constant_string.get(index);
} catch (IndexOutOfBoundsException ex) {
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
}
return null;
}
public int getIntCount() {

View File

@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
@@ -173,7 +174,12 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
s.append(" null");
} else {
s.append(" ");
s.append(constants.getMultiname(operands[i]).toString(constants, fullyQualifiedNames));
Multiname multiname = constants.getMultiname(operands[i]);
if (multiname != null) {
s.append(multiname.toString(constants, fullyQualifiedNames));
} else {
s.append("Multiname not found.");
}
}
/*s.append(" m[");
s.append(operands[i]);

View File

@@ -20,7 +20,6 @@ import com.jpexs.decompiler.flash.abc.RenameType;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.helpers.Cache;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -254,7 +253,7 @@ public class Deobfuscation {
return "\u00A7" + escapeOIdentifier(s) + "\u00A7";
}
private static final Cache<String> nameCache = Cache.getInstance(false);
private static final Map<String, String> nameCache = new HashMap<>();
/**
* Ensures identifier is valid and if not, uses paragraph syntax
@@ -268,7 +267,7 @@ public class Deobfuscation {
if (s.startsWith("\u00A7") && s.endsWith("\u00A7")) { //Assuming already printed - TODO:detect better
return s;
}
if (nameCache.contains(s)) {
if (nameCache.containsKey(s)) {
return nameCache.get(s);
}
if (isValidName(s, validExceptions)) {
@@ -281,7 +280,7 @@ public class Deobfuscation {
}
public static String printNamespace(String pkg, String... validNameExceptions) {
if (nameCache.contains(pkg)) {
if (nameCache.containsKey(pkg)) {
return nameCache.get(pkg);
}
if (pkg.isEmpty()) {

View File

@@ -195,7 +195,7 @@ public class DefineButton2Tag extends ButtonTag implements Container {
return modified;
}
private static final Cache<RECT> rectCache = Cache.getInstance(true);
private static final Cache<DefineButton2Tag, RECT> rectCache = Cache.getInstance(true);
@Override
public RECT getRect(Set<BoundedTag> added) {

View File

@@ -234,7 +234,7 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
return modified;
}
private static final Cache<RECT> rectCache = Cache.getInstance(true);
private static final Cache<DefineButtonTag, RECT> rectCache = Cache.getInstance(true);
@Override
public RECT getRect(Set<BoundedTag> added) {

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.SWF;
@@ -125,7 +126,7 @@ public class DefineSpriteTag extends CharacterTag implements Container, Drawable
return ret;
}
private static final Cache<DefineSpriteTag, RECT> rectCache = Cache.getInstance(true);
@Override
public RECT getRect(Set<BoundedTag> added) {

View File

@@ -34,18 +34,19 @@ import java.util.logging.Logger;
/**
*
* @author JPEXS
* @param <E>
* @param <K>
* @param <V>
*/
public class Cache<E> {
public class Cache<K, V> {
private final Map<Object, File> cacheFiles;
private final Map<Object, E> cacheMemory;
private final Map<K, File> cacheFiles;
private final Map<K, V> cacheMemory;
private static final List<Cache> instances = new ArrayList<>();
public static final int STORAGE_FILES = 1;
public static final int STORAGE_MEMORY = 2;
public static <E> Cache<E> getInstance(boolean weak) {
Cache<E> instance = new Cache<>(weak);
public static <K, V> Cache<K, V> getInstance(boolean weak) {
Cache<K, V> instance = new Cache<>(weak);
instances.add(instance);
return instance;
}
@@ -89,7 +90,7 @@ public class Cache<E> {
}
}
public boolean contains(Object key) {
public boolean contains(K key) {
if (storageType == STORAGE_FILES) {
return cacheFiles.containsKey(key);
} else if (storageType == STORAGE_MEMORY) {
@@ -106,7 +107,7 @@ public class Cache<E> {
cacheFiles.clear();
}
public void remove(Object key) {
public void remove(K key) {
if (storageType == STORAGE_FILES) {
if (cacheFiles.containsKey(key)) {
File f = cacheFiles.get(key);
@@ -121,7 +122,7 @@ public class Cache<E> {
}
public E get(Object key) {
public V get(K key) {
if (storageType == STORAGE_FILES) {
if (!cacheFiles.containsKey(key)) {
return null;
@@ -130,7 +131,7 @@ public class Cache<E> {
try (FileInputStream fis = new FileInputStream(f)) {
ObjectInputStream ois = new ObjectInputStream(fis);
@SuppressWarnings("unchecked")
E item = (E) ois.readObject();
V item = (V) ois.readObject();
return item;
} catch (IOException | ClassNotFoundException ex) {
Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
@@ -145,7 +146,7 @@ public class Cache<E> {
return null;
}
public void put(Object key, E value) {
public void put(K key, V value) {
if (storageType == STORAGE_FILES) {
File temp = null;
try {