From 6cfd01dc3d9d9806bb29cf8bfb9463234b3fe0c7 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 7 Sep 2014 12:29:27 +0200 Subject: [PATCH] AVM2 instruction reading fix, strongly typed Cache key --- .../src/com/jpexs/decompiler/flash/SWF.java | 2 +- .../decompiler/flash/abc/ABCInputStream.java | 13 +- .../decompiler/flash/abc/avm2/AVM2Code.java | 114 ++++++++++-------- .../flash/abc/avm2/AVM2ConstantPool.java | 58 +++++++-- .../avm2/instructions/AVM2Instruction.java | 8 +- .../flash/action/Deobfuscation.java | 7 +- .../flash/tags/DefineButton2Tag.java | 2 +- .../flash/tags/DefineButtonTag.java | 2 +- .../flash/tags/DefineSpriteTag.java | 5 +- .../src/com/jpexs/helpers/Cache.java | 23 ++-- 10 files changed, 153 insertions(+), 81 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 24d0b7fe3..1df9da85f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -2203,7 +2203,7 @@ public final class SWF implements TreeItem, Timelined { mat.translateX, mat.translateY); } - private static Cache frameCache = Cache.getInstance(false); + private static Cache frameCache = Cache.getInstance(false); public static SerializableImage getFromCache(String key) { if (frameCache.contains(key)) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java index 58ec233f2..571efc230 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java @@ -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 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 2e5a6f6ed..0b6922696 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -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 codeMap = new TreeMap<>(); + DumpInfo diParent = ais.dumpInfo; + List 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++; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java index 5bc68fd8d..04b6043d3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java @@ -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() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java index 8514cb705..d67eb86b2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java @@ -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]); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Deobfuscation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Deobfuscation.java index b5763db90..225ab8848 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Deobfuscation.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Deobfuscation.java @@ -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 nameCache = Cache.getInstance(false); + private static final Map 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()) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java index b8865918b..39fad0d93 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java @@ -195,7 +195,7 @@ public class DefineButton2Tag extends ButtonTag implements Container { return modified; } - private static final Cache rectCache = Cache.getInstance(true); + private static final Cache rectCache = Cache.getInstance(true); @Override public RECT getRect(Set added) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index bc19d135e..7f8c76051 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -234,7 +234,7 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { return modified; } - private static final Cache rectCache = Cache.getInstance(true); + private static final Cache rectCache = Cache.getInstance(true); @Override public RECT getRect(Set added) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index 38c79703b..4d3e0e8b1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -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 rectCache = Cache.getInstance(true); + private static final Cache rectCache = Cache.getInstance(true); @Override public RECT getRect(Set added) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java index e2d603082..142bcf6fe 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java @@ -34,18 +34,19 @@ import java.util.logging.Logger; /** * * @author JPEXS - * @param + * @param + * @param */ -public class Cache { +public class Cache { - private final Map cacheFiles; - private final Map cacheMemory; + private final Map cacheFiles; + private final Map cacheMemory; private static final List instances = new ArrayList<>(); public static final int STORAGE_FILES = 1; public static final int STORAGE_MEMORY = 2; - public static Cache getInstance(boolean weak) { - Cache instance = new Cache<>(weak); + public static Cache getInstance(boolean weak) { + Cache instance = new Cache<>(weak); instances.add(instance); return instance; } @@ -89,7 +90,7 @@ public class Cache { } } - 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 { 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 { } - 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 { 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 { return null; } - public void put(Object key, E value) { + public void put(K key, V value) { if (storageType == STORAGE_FILES) { File temp = null; try {