diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8a53873..20a936c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +# [Unreleased] +### Fixed +- AS1/2 handle declaration of registers in certain cases + ## [14.5.1] - 2021-11-20 ### Fixed - AS 1/2 - do not use eval function on obfuscated increment/decrement diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java index 4385d145d..b6ed337b2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java @@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.action.model.SetTarget2ActionItem; import com.jpexs.decompiler.flash.action.model.SetTargetActionItem; import com.jpexs.decompiler.flash.action.model.SetTypeActionItem; import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; +import com.jpexs.decompiler.flash.action.model.TemporaryRegister; import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem; import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem; import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem; @@ -42,12 +43,15 @@ import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor; +import com.jpexs.decompiler.graph.Block; 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.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.Loop; import com.jpexs.decompiler.graph.StopPartKind; import com.jpexs.decompiler.graph.ThrowState; @@ -63,7 +67,9 @@ import com.jpexs.decompiler.graph.model.WhileItem; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Reference; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -309,6 +315,69 @@ public class ActionGraph extends Graph { } + @Override + protected void finalProcessAfter(List list, int level, FinalProcessLocalData localData, String path) { + super.finalProcessAfter(list, level, localData, path); + makeDefineRegistersUp(list); + } + + + /* + This makes declarations of registers on one level up when inside some + structure. + Example : + if((var loc4 = random()) > 5) { + trace("x"); + } + => + var loc4 = null; + if ((loc4 = random()) > 5) + { + trace("x"); + } + + + */ + private void makeDefineRegistersUp(List list) { + for (int i = 0; i < list.size(); i++) { + final int fi = i; + GraphTargetItem ti = list.get(i); + + if (ti instanceof TemporaryRegister) { + continue; + } + + Set visitedItems = new HashSet<>(); + ti.visitNoBlock(new AbstractGraphTargetVisitor() { + @Override + public void visit(GraphTargetItem item) { + if (item != null && !visitedItems.contains(item)) { + visitedItems.add(item); + + if (item instanceof TemporaryRegister) { + return; + } + if (item instanceof StoreRegisterActionItem) { + StoreRegisterActionItem sr = (StoreRegisterActionItem) item; + if (sr.define) { + list.add(fi, new StoreRegisterActionItem(null, null, sr.register, new DirectValueActionItem(Null.INSTANCE), true)); + sr.define = false; + } + } + + item.visitNoBlock(this); + } + } + }); + if (ti instanceof Block) { + Block b = (Block) ti; + for (List items : b.getSubs()) { + makeDefineRegistersUp(items); + } + } + } + } + @Override protected List check(List currentRet, List foundGotos, Map> partCodes, Map partCodePos, Set visited, GraphSource code, BaseLocalData localData, Set allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List stopPartKind, List loops, List throwStates, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { if (!output.isEmpty()) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java index 6d7e40bc1..c1e11832d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java @@ -87,6 +87,12 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista visitor.visitAll(actions); } + @Override + public void visitNoBlock(GraphTargetVisitorInterface visitor) { + + } + + public FunctionActionItem() { super(null, null, PRECEDENCE_PRIMARY); } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java index 1549136a0..30f6f7dff 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.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; import com.jpexs.decompiler.flash.action.Action; @@ -62,6 +63,28 @@ public class ActionScript2AssemblerTest extends ActionScript2TestBase { return null; } + private String decompilePcode(String pcode) { + try { + List actions = ASMParser.parse(0, true, pcode, swf.version, false); + + DoActionTag doa = getFirstActionTag(); + doa.setActionBytes(Action.actionsToBytes(actions, true, swf.version)); + HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false); + + try { + Action.actionsToSource(doa, doa.getActions(), "", writer); + } catch (InterruptedException ex) { + fail(); + } + + return writer.toString(); + } catch (IOException | ActionParseException ex) { + fail(); + } + + return null; + } + @Test public void testModifiedConstantPools() { String actionsString = "ConstantPool \"ok\"\n" @@ -97,4 +120,31 @@ public class ActionScript2AssemblerTest extends ActionScript2TestBase { String decompiled = recompilePcode(actionsString); assertTrue(decompiled.contains("Push -0.25")); } + + @Test + public void testDeclaredRegister() { + String res = decompilePcode("ConstantPool\n" + + "DefineFunction2 \"test\" 1 3 false true true false true false true false false 2 \"p\" {\n" + + "Push register2 \"type\"\n" + + "GetMember\n" + + "StoreRegister 0\n" + + "Push 1\n" + + "StrictEquals\n" + + "If loc003a\n" + + "Jump loc004a\n" + + "loc003a:Push \"Hello\"\n" + + "Trace\n" + + "Jump loc004a\n" + + "}\n" + + "loc004a:"); + res = cleanPCode(res); + assertEquals(res, "function test(p)\n" + + "{\n" + + "var _loc0_ = null;\n" + + "if((_loc0_ = p.type) === 1)\n" + + "{\n" + + "trace(\"Hello\");\n" + + "}\n" + + "}"); + } }