/* * Copyright (C) 2010-2015 JPEXS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ 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; 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.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.ActionList; import com.jpexs.decompiler.flash.action.ActionLocalData; import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.swf4.ActionAdd; import com.jpexs.decompiler.flash.action.swf4.ActionAnd; import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii; import com.jpexs.decompiler.flash.action.swf4.ActionDivide; import com.jpexs.decompiler.flash.action.swf4.ActionEquals; import com.jpexs.decompiler.flash.action.swf4.ActionLess; import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar; import com.jpexs.decompiler.flash.action.swf4.ActionMBCharToAscii; import com.jpexs.decompiler.flash.action.swf4.ActionMBStringExtract; import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength; import com.jpexs.decompiler.flash.action.swf4.ActionMultiply; import com.jpexs.decompiler.flash.action.swf4.ActionNot; import com.jpexs.decompiler.flash.action.swf4.ActionOr; import com.jpexs.decompiler.flash.action.swf4.ActionPush; import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd; import com.jpexs.decompiler.flash.action.swf4.ActionStringExtract; import com.jpexs.decompiler.flash.action.swf4.ActionStringLength; import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; import com.jpexs.decompiler.flash.action.swf4.ActionToInteger; import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd; import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift; import com.jpexs.decompiler.flash.action.swf5.ActionBitOr; import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift; import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; import com.jpexs.decompiler.flash.action.swf5.ActionDecrement; import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; import com.jpexs.decompiler.flash.action.swf5.ActionLess2; import com.jpexs.decompiler.flash.action.swf5.ActionModulo; import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; import com.jpexs.decompiler.flash.action.swf5.ActionStackSwap; import com.jpexs.decompiler.flash.action.swf5.ActionToNumber; import com.jpexs.decompiler.flash.action.swf5.ActionToString; import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf; import com.jpexs.decompiler.flash.action.swf6.ActionGreater; import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; 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.DoABC2Tag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.javactivex.ActiveX; import com.jpexs.javactivex.ActiveXEvent; import com.jpexs.javactivex.ActiveXEventListener; import com.jpexs.javactivex.Reference; import com.jpexs.javactivex.example.controls.flash.ShockwaveFlash; import java.awt.Panel; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.testng.Assert; import static org.testng.Assert.fail; /** * * @author JPEXS */ public class FlashPlayerTest { private final Object lockObj = new Object(); private final Random random = new Random(); //@Test public void test1() throws IOException, InterruptedException { final Reference resultRef = new Reference<>(null); ShockwaveFlash flash = ActiveX.createObject(ShockwaveFlash.class, new Panel()); flash.setAllowScriptAccess("always"); flash.setAllowNetworking("all"); flash.addFSCommandListener(new ActiveXEventListener() { @Override public void onEvent(ActiveXEvent axe) { resultRef.setVal((String) axe.args.get("args")); synchronized (lockObj) { lockObj.notify(); } } }); File f = new File("libsrc/ffdec_lib/testdata/run_as3/run.swf"); SWF swf = new SWF(new BufferedInputStream(new FileInputStream(f)), false); swf.version = SWF.MAX_VERSION; DoABC2Tag abcTag = null; for (Tag t : swf.tags) { if (t instanceof DoABC2Tag) { abcTag = ((DoABC2Tag) t); break; } } ABC abc = abcTag.getABC(); abc.constants.addUInt(0); abc.constants.addUInt(100); abc.constants.addDouble(0.0); abc.constants.addDouble(111.11); MethodBody body = abc.findBodyByClassAndName("Run", "run"); body.max_stack = 20; body.max_regs = 10; AVM2Instruction[] pushes = new AVM2Instruction[]{ new AVM2Instruction(0, AVM2Instructions.PushUndefined, null), new AVM2Instruction(0, AVM2Instructions.PushNull, null), new AVM2Instruction(0, AVM2Instructions.PushTrue, null), new AVM2Instruction(0, AVM2Instructions.PushFalse, null), new AVM2Instruction(0, AVM2Instructions.PushNan, null), new AVM2Instruction(0, AVM2Instructions.PushString, new int[]{1}), new AVM2Instruction(0, AVM2Instructions.PushDouble, new int[]{1}), new AVM2Instruction(0, AVM2Instructions.PushShort, new int[]{1}), new AVM2Instruction(0, AVM2Instructions.PushInt, new int[]{1}), new AVM2Instruction(0, AVM2Instructions.PushUInt, new int[]{1}), new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{-2}), new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{-1}), new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{0}), new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{1}), new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{2}),}; for (int i = 58; i < 256; i++) { for (int p1 = 0; p1 < pushes.length; p1++) { for (int p2 = 0; p2 < pushes.length; p2++) { System.out.println("p1: " + p1 + " p2: " + p2); // todo: the following instructions are not implemented if (i == AVM2Instructions.GetSuper || i == AVM2Instructions.SetSuper || i == AVM2Instructions.DXNS || i == AVM2Instructions.DXNSLate || i == AVM2Instructions.Kill || i == AVM2Instructions.LookupSwitch || i == AVM2Instructions.PushWith || i == AVM2Instructions.NextName || i == AVM2Instructions.HasNext || i == AVM2Instructions.NextValue || i == AVM2Instructions.PushScope || i == AVM2Instructions.PushNamespace || i == AVM2Instructions.HasNext2 || i == AVM2Instructions.NewFunction || i == AVM2Instructions.Call || i == AVM2Instructions.Construct || i == AVM2Instructions.CallMethod || i == AVM2Instructions.CallStatic || i == AVM2Instructions.CallSuper || i == AVM2Instructions.CallProperty || i == AVM2Instructions.ConstructSuper || i == AVM2Instructions.ConstructProp || i == AVM2Instructions.CallPropLex || i == AVM2Instructions.CallSuperVoid || i == AVM2Instructions.CallPropVoid || i == AVM2Instructions.ApplyType || i == AVM2Instructions.NewObject || i == AVM2Instructions.NewArray || i == AVM2Instructions.NewActivation || i == AVM2Instructions.NewClass || i == AVM2Instructions.GetDescendants || i == AVM2Instructions.NewCatch || i == AVM2Instructions.FindPropGlobal || i == AVM2Instructions.FindPropertyStrict || i == AVM2Instructions.FindProperty || i == AVM2Instructions.FindDef || i == AVM2Instructions.GetLex || i == AVM2Instructions.SetProperty || i == AVM2Instructions.GetGlobalScope || i == AVM2Instructions.GetScopeObject || i == AVM2Instructions.GetProperty || i == AVM2Instructions.GetOuterScope || i == AVM2Instructions.InitProperty || i == AVM2Instructions.DeleteProperty || i == AVM2Instructions.GetSlot || i == AVM2Instructions.SetSlot || i == AVM2Instructions.GetGlobalSlot || i == AVM2Instructions.SetGlobalSlot || i == AVM2Instructions.CheckFilter || i == AVM2Instructions.AsType // todo: fix || i == AVM2Instructions.AsTypeLate || i == AVM2Instructions.InstanceOf || i == AVM2Instructions.IsType || i == AVM2Instructions.IsTypeLate || i == AVM2Instructions.In) { continue; } System.out.println("Instruction code: " + Integer.toHexString(i) + " (" + i + ") " + AVM2Code.instructionSet[i].instructionName); int j = 1; File f2 = new File("run_test_" + new Date().getTime() + "_" + i + "_" + j + ".swf"); f2.deleteOnExit(); AVM2Code ccode = new AVM2Code(); ccode.code = new ArrayList<>(); List code = ccode.code; code.add(new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); code.add(new AVM2Instruction(0, AVM2Instructions.PushScope, null)); code.add(new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{10})); code.add(new AVM2Instruction(0, AVM2Instructions.SetLocal0, null)); code.add(new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{11})); code.add(new AVM2Instruction(0, AVM2Instructions.SetLocal1, null)); code.add(new AVM2Instruction(0, AVM2Instructions.PushByte, new int[]{0})); code.add(pushes[p1].clone()); code.add(pushes[p2].clone()); InstructionDefinition ins = AVM2Code.instructionSet[i]; int[] params = null; boolean ifType = false; if (ins.operands.length > 0) { params = new int[ins.operands.length]; if (!(ins instanceof IfTypeIns)) { for (int param = 0; param < params.length; param++) { params[param] = 1; } } ifType = ins instanceof IfTypeIns; if (ifType) { params[0] = 3; } } code.add(new AVM2Instruction(0, ins, params)); if (ifType) { code.add(new AVM2Instruction(0, AVM2Instructions.PushString, new int[]{1})); code.add(new AVM2Instruction(0, AVM2Instructions.ReturnValue, null)); } code.add(new AVM2Instruction(0, AVM2Instructions.Dup, null)); code.add(new AVM2Instruction(0, AVM2Instructions.TypeOf, null)); code.add(new AVM2Instruction(0, AVM2Instructions.Add, null)); code.add(new AVM2Instruction(0, AVM2Instructions.ReturnValue, null)); body.setCode(ccode); body.markOffsets(); //String pcode = ccode.toASMSource(); //String as = body.toSource(); abcTag.setModified(true); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f2))) { swf.saveTo(fos); } flash.setMovie(f2.getAbsolutePath()); synchronized (lockObj) { lockObj.wait(); } f2.delete(); String flashResult = resultRef.getVal(); System.out.println("Flash result: " + flashResult); String ffdecExecuteResult; try { Object res = ccode.execute(new HashMap<>(), abc.constants, AVM2Runtime.ADOBE_FLASH, 19); ffdecExecuteResult = "Result:" + EcmaScript.toString(res) + " Type:" + EcmaScript.typeString(res); } catch (AVM2ExecutionException ex) { ffdecExecuteResult = "Error:" + ex.getMessage(); } System.out.println("FFDec execte result: " + ffdecExecuteResult); Assert.assertEquals(ffdecExecuteResult, flashResult); } } } /*int cnt = 0; while (flash.getReadyState() != 4) { Thread.sleep(50); if (cnt > 100) { Assert.fail("Flash init timeout"); } cnt++; }*/ /*try { String res = flash.CallFunction("something"); //String str = flash.GetVariable("_root.myText.text"); throw new Error(res + " " + body.getCode().toString() + ""); } catch (Exception ex) { int a = 1; }*/ } //@Test public void testAs2() throws IOException, InterruptedException { final Reference resultRef = new Reference<>(null); ShockwaveFlash flash = ActiveX.createObject(ShockwaveFlash.class, new Panel()); flash.setAllowScriptAccess("always"); flash.setAllowNetworking("all"); flash.addFSCommandListener(new ActiveXEventListener() { @Override public void onEvent(ActiveXEvent axe) { resultRef.setVal((String) axe.args.get("args")); synchronized (lockObj) { lockObj.notify(); } } }); File f = new File("libsrc/ffdec_lib/testdata/run_as2/run_as2.swf"); for (int i = 0; i < 15; i++) { for (int j = 0; j < 13 + 23 + 2; j++) { File f2 = new File("run_test_" + new Date().getTime() + "_" + i + "_" + j + ".swf"); f2.deleteOnExit(); SWF swf = new SWF(new BufferedInputStream(new FileInputStream(f)), false); Map asms = swf.getASMs(true); ASMSource asm = asms.get("\\frame_1\\DoAction"); ActionList actions = asm.getActions(); actions.removeAction(2, 4); List newActions = new ArrayList<>(); int r1 = random.nextInt(500) - 255; int r2 = random.nextInt(100); Action opAction = getOpAction(j); if (j >= 13 + 23) { newActions.add(new ActionPush("mystring_árvíztűrő_tükörfúrógép")); } if (j >= 13) { newActions.add(new ActionPush(r1)); } Object r2Obj; if (i == 0) { r2Obj = Undefined.INSTANCE; } else if (i == 1) { r2Obj = Null.INSTANCE; } else if (i == 2) { r2Obj = false; } else if (i == 3) { r2Obj = true; } else if (i == 4) { r2Obj = ""; } else if (i == 5) { r2Obj = "test"; } else if (i == 6) { r2Obj = "0"; } else if (i == 7) { r2Obj = "0.0"; } else if (i == 8) { r2Obj = "1.0"; } else if (i == 9) { r2Obj = "-1.0"; } else if (i == 10) { r2Obj = 0; } else if (i == 11) { r2Obj = -100; } else if (i == 12) { r2Obj = 100; } else { r2Obj = r2; } newActions.add(new ActionPush(r2Obj)); System.out.println(i + " " + j + " " + opAction.toString() + " r1:" + r1 + " r2:" + r2Obj + " r3:" + "mystring"); newActions.add(opAction); newActions.add(new ActionPushDuplicate()); newActions.add(new ActionTypeOf()); newActions.add(new ActionStackSwap()); newActions.add(new ActionStringAdd()); actions.addActions(2, newActions); List output = new ArrayList<>(); ActionLocalData localData = new ActionLocalData(); TranslateStack stack = new TranslateStack(""); for (Action a : newActions) { a.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); } Object ffdecResult = stack.pop().getResult(); System.out.println("FFDec result: " + ffdecResult); LocalDataArea lda = new LocalDataArea(); for (Action a : newActions) { if (!a.execute(lda)) { fail(); } } Object ffdecExecuteResult = lda.stack.pop(); System.out.println("FFDec execte result: " + ffdecExecuteResult); asm.setActions(actions); asm.setModified(); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f2))) { swf.saveTo(fos); } flash.setMovie(f2.getAbsolutePath()); synchronized (lockObj) { lockObj.wait(); } String str = flash.GetVariable("myText.text"); String flashResult = resultRef.getVal(); boolean checkOnlyStart = false; /*if (flashResult.length() > 10) { boolean onlyNumber = true; for (int k = 0; k < 10; k++) { char ch = flashResult.charAt(flashResult.length() - k - 1); if (ch < '0' || ch > '9') { onlyNumber = false; break; } } if (onlyNumber) { flashResult = flashResult.substring(0, flashResult.length() - 1); checkOnlyStart = true; } }*/ if (checkOnlyStart) { Assert.assertTrue(((String) ffdecResult).startsWith(flashResult)); Assert.assertTrue(((String) ffdecExecuteResult).startsWith(flashResult)); } else { Assert.assertEquals(ffdecResult, flashResult); Assert.assertEquals(ffdecExecuteResult, flashResult); } f2.delete(); } } } private Action getOpAction(int idx) { Action result; if (idx < 13) { result = getUnaryOpAction(idx); Assert.assertEquals(1, result.getStackPopCount(null, null)); Assert.assertEquals(1, result.getStackPushCount(null, null)); } else if (idx < 13 + 23) { result = getBinaryOpAction(idx - 13); Assert.assertEquals(2, result.getStackPopCount(null, null)); Assert.assertEquals(1, result.getStackPushCount(null, null)); } else { result = getTernaryOpAction(idx - 13 - 23); Assert.assertEquals(3, result.getStackPopCount(null, null)); Assert.assertEquals(1, result.getStackPushCount(null, null)); } return result; } private Action getUnaryOpAction(int idx) { switch (idx) { case 0: return new ActionAsciiToChar(); case 1: return new ActionCharToAscii(); case 2: return new ActionDecrement(); case 3: return new ActionIncrement(); case 4: return new ActionNot(); case 5: return new ActionToInteger(); case 6: return new ActionToNumber(); case 7: return new ActionToString(); case 8: return new ActionTypeOf(); case 9: return new ActionStringLength(); case 10: return new ActionMBAsciiToChar(); case 11: return new ActionMBCharToAscii(); case 12: return new ActionMBStringLength(); } throw new Error("Invalid index"); } private Action getBinaryOpAction(int idx) { switch (idx) { case 0: return new ActionAnd(); case 1: return new ActionAdd(); case 2: return new ActionAdd2(); case 3: return new ActionBitAnd(); case 4: return new ActionBitLShift(); case 5: return new ActionBitOr(); case 6: return new ActionBitRShift(); case 7: return new ActionBitURShift(); case 8: return new ActionBitXor(); case 9: return new ActionDivide(); case 10: return new ActionEquals(); case 11: return new ActionEquals2(); case 12: return new ActionGreater(); case 13: return new ActionLess(); case 14: return new ActionLess2(); case 15: return new ActionModulo(); case 16: return new ActionMultiply(); case 17: return new ActionOr(); case 18: return new ActionStringAdd(); case 19: return new ActionStrictEquals(); case 20: return new ActionStringGreater(); case 21: return new ActionStringLess(); case 22: return new ActionSubtract(); } throw new Error("Invalid index"); } private Action getTernaryOpAction(int idx) { switch (idx) { case 0: return new ActionStringExtract(); case 1: return new ActionMBStringExtract(); } throw new Error("Invalid index"); } }