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 911626f6f..88aad0683 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 @@ -274,8 +274,6 @@ import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.Multiname; 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.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.configuration.Configuration; @@ -746,7 +744,9 @@ public class AVM2Code implements Cloneable { lda.runtimeInfo = runtimeInfo; for (AVM2Instruction ins : code) { - ins.definition.verify(lda, constants, ins); + if (!(ins.definition instanceof CallSuperVoidIns)) { + ins.definition.verify(lda, constants, ins); + } } while (pos < code.size()) { @@ -2129,7 +2129,6 @@ public class AVM2Code implements Cloneable { public void updateInstructionByteCountByAddr(long instructionAddress, int byteDelta, MethodBody body) { if (byteDelta != 0) { updateOffsets(new OffsetUpdater() { - @Override public long updateInstructionOffset(long address) { if (address > instructionAddress) { @@ -2200,7 +2199,6 @@ public class AVM2Code implements Cloneable { final List insAddrToRemove = new ArrayList<>(); final long endOffset = getEndOffset(); updateOffsets(new OffsetUpdater() { - @Override public long updateInstructionOffset(long address) { return address; @@ -2213,7 +2211,6 @@ public class AVM2Code implements Cloneable { } return offset; } - }, body); boolean someIgnored = false; @@ -2232,7 +2229,6 @@ public class AVM2Code implements Cloneable { public void checkValidOffsets(MethodBody body) { updateOffsets(new OffsetUpdater() { - @Override public long updateInstructionOffset(long address) { adr2pos(address); @@ -2247,7 +2243,6 @@ public class AVM2Code implements Cloneable { adr2pos(targetAddress); return offset; } - }, body); } @@ -2329,7 +2324,6 @@ public class AVM2Code implements Cloneable { if (byteDelta != 0) { updateOffsets(new OffsetUpdater() { - @Override public long updateInstructionOffset(long address) { if (address > instruction.getAddress()) { @@ -2380,7 +2374,6 @@ public class AVM2Code implements Cloneable { } final long x = instruction.getAddress(); updateOffsets(new OffsetUpdater() { - @Override public long updateInstructionOffset(long offset) { if (offset >= x) { @@ -2612,6 +2605,27 @@ public class AVM2Code implements Cloneable { return stats; } + // simplified varions of getStats. This method calculates only the maxlocal value + public CodeStats getMaxLocal() { + CodeStats stats = new CodeStats(); + for (AVM2Instruction ins : code) { + if (ins.definition instanceof SetLocalTypeIns) { + handleRegister(stats, ((SetLocalTypeIns) ins.definition).getRegisterId(ins)); + } else if (ins.definition instanceof GetLocalTypeIns) { + handleRegister(stats, ((GetLocalTypeIns) ins.definition).getRegisterId(ins)); + } else { + for (int i = 0; i < ins.definition.operands.length; i++) { + int op = ins.definition.operands[i]; + if (op == DAT_LOCAL_REG_INDEX) { + handleRegister(stats, ins.operands[i]); + } + } + } + } + + return stats; + } + private void visitCode(int ip, int lastIp, HashMap> refs) throws InterruptedException { List toVisit = new ArrayList<>(); List toVisitLast = new ArrayList<>(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/CodeStats.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/CodeStats.java index e1816668f..c51fb3f67 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/CodeStats.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/CodeStats.java @@ -57,6 +57,9 @@ public class CodeStats { return writer; } + public CodeStats() { + } + public CodeStats(AVM2Code code) { instructionStats = new InstructionStats[code.code.size()]; for (int i = 0; i < code.code.size(); i++) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java index 5d83071e0..93b64c618 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java @@ -34,9 +34,11 @@ import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item; import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.XMLAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.AddAVM2Item; +import com.jpexs.decompiler.flash.ecma.ObjectType; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** @@ -49,18 +51,28 @@ public class ConstructIns extends InstructionDefinition { super(0x42, "construct", new int[]{AVM2Code.DAT_ARG_COUNT}, true); } + @Override + public boolean isNotCompileTimeSupported() { + return true; + } + @Override public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) { - /*int argCount = ins.getParamAsLong(constants, 0).intValue(); - List passArguments = new ArrayList(); - for (int i = argCount - 1; i >= 0; i--) { - passArguments.set(i, lda.operandStack.pop()); - } - Object obj = lda.operandStack.pop();*/ + int argCount = ins.getParamAsLong(constants, 0).intValue(); + List passArguments = new ArrayList(); + for (int i = argCount - 1; i >= 0; i--) { + passArguments.set(i, lda.operandStack.pop()); + } + + Object obj = lda.operandStack.pop(); //lda.executionException = "Cannot call constructor"; - return false; - //call construct property of obj + + ObjectType result = new ObjectType(new HashMap<>()); + //todo: call construct property of obj + //push new instance + lda.operandStack.push(result); + return true; } public static boolean walkXML(GraphTargetItem item, List list) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructSuperIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructSuperIns.java index 231168226..cf8042ca1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructSuperIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructSuperIns.java @@ -39,18 +39,25 @@ public class ConstructSuperIns extends InstructionDefinition { super(0x49, "constructsuper", new int[]{AVM2Code.DAT_ARG_COUNT}, true); } + @Override + public boolean isNotCompileTimeSupported() { + return true; + } + @Override public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) { - /*int argCount = ins.getParamAsLong(constants, 0).intValue(); - List passArguments = new ArrayList(); - for (int i = argCount - 1; i >= 0; i--) { - passArguments.set(i, lda.operandStack.pop()); - } - Object obj = lda.operandStack.pop();*/ + int argCount = ins.getParamAsLong(constants, 0).intValue(); + List passArguments = new ArrayList(); + for (int i = argCount - 1; i >= 0; i--) { + passArguments.set(i, lda.operandStack.pop()); + } + + Object obj = lda.operandStack.pop(); //lda.executionException = "Cannot call super constructor"; - return false; - //call construct property of obj + + //todo: call construct property of obj //do not push anything + return true; } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeIns.java index 4d2ebec49..87b49b155 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeIns.java @@ -41,6 +41,11 @@ public class AsTypeIns extends InstructionDefinition { super(0x86, "astype", new int[]{AVM2Code.DAT_MULTINAME_INDEX}, true); } + @Override + public boolean isNotCompileTimeSupported() { + return true; + } + @Override public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) { //Long typeIndex = ins.getParamAsLong(constants, 0); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeLateIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeLateIns.java index 40e40a40e..8532f8ef7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeLateIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/AsTypeLateIns.java @@ -38,6 +38,11 @@ public class AsTypeLateIns extends InstructionDefinition { super(0x87, "astypelate", new int[]{}, true); } + @Override + public boolean isNotCompileTimeSupported() { + return true; + } + @Override public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) { //Object objClass = lda.operandStack.pop(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index c78ecea02..5cf36f9d0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -449,7 +449,6 @@ public class AVM2SourceGenerator implements SourceGenerator { )); GraphTargetItem assigned = new GraphTargetItem() { - @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { return null; @@ -2125,11 +2124,17 @@ public class AVM2SourceGenerator implements SourceGenerator { continue; } if (item instanceof InterfaceAVM2Item) { - generateClass(((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex), abcIndex.getSelectedAbc().class_info.get(((TraitClass) traits[k]).class_info), abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item) item).pkg.name, localData, (InterfaceAVM2Item) item, class_index); + ABC abc = abcIndex.getSelectedAbc(); + TraitClass trait = (TraitClass) traits[k]; + InterfaceAVM2Item iitem = (InterfaceAVM2Item) item; + generateClass(iitem.pkg.getCpoolIndex(abcIndex), abc.class_info.get(trait.class_info), abc.instance_info.get(trait.class_info), initScopes.get(trait), iitem.pkg.name, localData, iitem, class_index); } if (item instanceof ClassAVM2Item) { - generateClass(((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex), abcIndex.getSelectedAbc().class_info.get(((TraitClass) traits[k]).class_info), abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item) item).pkg.name, localData, (ClassAVM2Item) item, class_index); + ABC abc = abcIndex.getSelectedAbc(); + TraitClass trait = (TraitClass) traits[k]; + ClassAVM2Item citem = (ClassAVM2Item) item; + generateClass(citem.pkg.getCpoolIndex(abcIndex), abc.class_info.get(trait.class_info), abc.instance_info.get(trait.class_info), initScopes.get(trait), citem.pkg.name, localData, citem, class_index); } if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { MethodAVM2Item mai = (MethodAVM2Item) item; @@ -2713,5 +2718,4 @@ public class AVM2SourceGenerator implements SourceGenerator { List ret = new ArrayList<>(); return ret; } - } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index 6f0b5c436..943b02480 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -475,4 +475,19 @@ public final class MethodBody implements Cloneable { } return true; } + + public boolean autoFillMaxRegs(ABC abc) { + CodeStats stats = getCode().getMaxLocal(); + if (stats == null) { + return false; + } + max_regs = stats.maxlocal; + MethodInfo mi = abc.method_info.get(method_info); + int min_regs = mi.param_types.length + 1 + (mi.flagNeed_rest() ? 1 : 0); + if (max_regs < min_regs) { + max_regs = min_regs; + } + + return true; + } } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java index dc219973c..b6d9e0a0e 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3DeobfuscatorTest.java @@ -49,7 +49,9 @@ import org.testng.annotations.Test; * * @author JPEXS */ -public class ActionScript3DeobfuscatorTest extends ActionScript2TestBase { +public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase { + + protected SWF swf; @BeforeClass public void init() throws IOException, InterruptedException { @@ -67,7 +69,6 @@ public class ActionScript3DeobfuscatorTest extends ActionScript2TestBase { + str + "returnvoid\r\n"; final ABC abc = new ABC(new ABCContainerTag() { - @Override public ABC getABC() { return null; @@ -96,7 +97,6 @@ public class ActionScript3DeobfuscatorTest extends ActionScript2TestBase { private String recompile(String str) throws AVM2ParseException, IOException, CompilationException, InterruptedException { str = "package { public class Test { public static function trace(s){ } public static function test(){ " + str + " } } }"; final ABC abc = new ABC(new ABCContainerTag() { - @Override public ABC getABC() { return null; diff --git a/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java b/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java index 91d659809..bbd77072d 100644 --- a/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java +++ b/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java @@ -85,7 +85,6 @@ public class AdobeFlashExecutor { flash.setAllowScriptAccess("always"); flash.setAllowNetworking("all"); flash.addFSCommandListener(new ActiveXEventListener() { - @Override public void onEvent(ActiveXEvent axe) { resultRef.setVal((String) axe.args.get("args")); @@ -363,6 +362,7 @@ public class AdobeFlashExecutor { methodBody.max_scope_depth = 10; methodBody.setCode(code); + methodBody.autoFillMaxRegs(abc); return methodTrait.name_index; } diff --git a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java index 9fdebc123..83d23c4d3 100644 --- a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java +++ b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2Runtime; @@ -25,6 +26,7 @@ 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.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionLocalData; import com.jpexs.decompiler.flash.action.LocalDataArea; @@ -73,10 +75,15 @@ import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.ecma.Null; import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.helpers.Helper; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -94,7 +101,83 @@ public class FlashPlayerTest { private final Random random = new Random(); - private final AVM2RuntimeInfo adobeRuntime = new AVM2RuntimeInfo(AVM2Runtime.ADOBE_FLASH, 19, false); + private final AVM2RuntimeInfo adobeRuntime = new AVM2RuntimeInfo(AVM2Runtime.ADOBE_FLASH, 19, true); + + //@Test + public void testAs3Files() throws IOException, InterruptedException { + List files = new ArrayList<>(); + File dir = new File("..\\swf"); + if (dir.exists()) { + File[] fs = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".swf") && !name.toLowerCase().endsWith(".recompiled.swf"); + } + }); + for (File f : fs) { + files.add(dir.getAbsolutePath() + File.separator + f.getName()); + } + } + + for (String file : files) { + SWF swf = new SWF(new BufferedInputStream(new FileInputStream(file)), true); + List abcs = swf.getAbcList(); + for (ABCContainerTag abcContainer : abcs) { + ABC abc = abcContainer.getABC(); + + for (MethodBody body : abc.bodies) { + if (body.method_info != 55) { + continue; + } + + AVM2Code code = body.getCode(); + + AdobeFlashExecutor adobeExecutor = new AdobeFlashExecutor(); + + adobeExecutor.loadTestSwf(); + ABC testAbc = adobeExecutor.as3TestSwfAbcTag.getABC(); + testAbc.constants.ensureStringCapacity(1000000); + testAbc.constants.ensureMultinameCapacity(1000000); + + List tasks = new ArrayList<>(); + { + AS3ExecuteTask task = new AS3ExecuteTask(); + task.description = swf.getFileTitle() + " " + body.method_info; + while (code.code.size() > 9) { + code.code.remove(code.code.size() - 1); + } + task.code = code; + tasks.add(task); + } + + adobeExecutor.executeAvm2(tasks); + + for (AS3ExecuteTask task : tasks) { + + String ffdecExecuteResult; + try { + Object res = task.code.execute(new HashMap<>(), testAbc.constants, adobeRuntime); + ffdecExecuteResult = "Result:" + EcmaScript.toString(res) + " Type:" + EcmaScript.typeString(res); + } catch (AVM2ExecutionException ex) { + ffdecExecuteResult = "Error:" + ex.getMessage(); + } + + task.ffdecResult = ffdecExecuteResult; + } + + for (AS3ExecuteTask task : tasks) { + System.out.println("Flash result (" + task.description + "): " + task.flashResult); + System.out.println("FFDec execte result: " + task.ffdecResult); + if (!task.ffdecResult.equals(task.flashResult)) { + System.out.println(code.toASMSource(testAbc.constants)); + } + + assertEquals(task.ffdecResult, task.flashResult); + } + } + } + } + } //@Test public void testAs3Pushes() throws IOException, InterruptedException { @@ -119,20 +202,23 @@ public class FlashPlayerTest { AS3ExecuteTask task = new AS3ExecuteTask(); task.description = p1 + ", " + pushes[p1].definition.instructionName; task.code = ccode; + tasks.add(task); + } + adobeExecutor.executeAvm2(tasks); + + for (AS3ExecuteTask task : tasks) { String ffdecExecuteResult; try { - Object res = ccode.execute(new HashMap<>(), abc.constants, adobeRuntime); + Object res = task.code.execute(new HashMap<>(), abc.constants, adobeRuntime); ffdecExecuteResult = "Result:" + EcmaScript.toString(res) + " Type:" + EcmaScript.typeString(res); } catch (AVM2ExecutionException ex) { ffdecExecuteResult = "Error:" + ex.getMessage(); } task.ffdecResult = ffdecExecuteResult; - tasks.add(task); } - adobeExecutor.executeAvm2(tasks); for (AS3ExecuteTask task : tasks) { System.out.println("Flash result (" + task.description + "): " + task.flashResult); System.out.println("FFDec execte result: " + task.ffdecResult);