Fixed: #1295, #116 AS3 dynamic construction via MultinameL

This commit is contained in:
Jindra Petřík
2025-07-24 23:47:25 +02:00
parent efa21d7ec5
commit ea56b5680f
12 changed files with 226 additions and 9 deletions

View File

@@ -46,6 +46,7 @@ All notable changes to this project will be documented in this file.
- [#2493] Incorrect placement of §§push instructions
- AS3 direct editation/highlighter §§pop in class traits
- AS direct editation/highlighter §§pop and §§dup can have getmember or call
- [#1295], [#116] AS3 dynamic construction via MultinameL
### Changed
- Icon of "Deobfuscation options" menu from pile of pills to medkit
@@ -3937,6 +3938,8 @@ Major version of SWF to XML export changed to 2.
[#2483]: https://www.free-decompiler.com/flash/issues/2483
[#2486]: https://www.free-decompiler.com/flash/issues/2486
[#2493]: https://www.free-decompiler.com/flash/issues/2493
[#1295]: https://www.free-decompiler.com/flash/issues/1295
[#116]: https://www.free-decompiler.com/flash/issues/116
[#2476]: https://www.free-decompiler.com/flash/issues/2476
[#2404]: https://www.free-decompiler.com/flash/issues/2404
[#1418]: https://www.free-decompiler.com/flash/issues/1418
@@ -4178,7 +4181,6 @@ Major version of SWF to XML export changed to 2.
[#2099]: https://www.free-decompiler.com/flash/issues/2099
[#2090]: https://www.free-decompiler.com/flash/issues/2090
[#2079]: https://www.free-decompiler.com/flash/issues/2079
[#116]: https://www.free-decompiler.com/flash/issues/116
[#2097]: https://www.free-decompiler.com/flash/issues/2097
[#2098]: https://www.free-decompiler.com/flash/issues/2098
[#2093]: https://www.free-decompiler.com/flash/issues/2093

View File

@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item;
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.types.Multiname;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.helpers.Reference;
@@ -108,7 +109,7 @@ public class ConstructPropIns extends InstructionDefinition {
if (obj instanceof FindPropertyAVM2Item) {
multiname.property = false; //can be type
}
}
stack.push(new ConstructPropAVM2Item(ins, localData.lineStartInstruction, obj, multiname, args, type.getVal(), isStatic.getVal()));
}

View File

@@ -16,11 +16,14 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Helper;
import java.util.List;
import java.util.Objects;
@@ -75,12 +78,45 @@ public class ConstructAVM2Item extends AVM2Item {
|| (object instanceof CallStaticAVM2Item)
|| (object instanceof CallSuperAVM2Item);
if (object.getPrecedence() > getPrecedence() || objectIsCall) {
writer.append("(");
boolean isGetDefinition = false;
if (object instanceof GetPropertyAVM2Item) {
GetPropertyAVM2Item getProperty = (GetPropertyAVM2Item) object;
if (getProperty.propertyName instanceof FullMultinameAVM2Item) {
FullMultinameAVM2Item fm = (FullMultinameAVM2Item) getProperty.propertyName;
if (fm.multinameIndex > 0) {
Multiname m = localData.abc.constants.getMultiname(fm.multinameIndex);
if (m.kind == Multiname.MULTINAMEL) {
if (m.getNamespaceSet(localData.abc.constants).namespaces.length == 1) {
isGetDefinition = true;
writer.append("(");
if (localData.fullyQualifiedNames.contains(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"))) {
writer.append("flash.utils.getDefinitionByName");
} else {
writer.append("getDefinitionByName");
}
writer.append("(");
String nname = m.getSingleNamespace(localData.abc.constants).getName(localData.abc.constants).toRawString();
if (!nname.isEmpty()) {
writer.append("\"");
writer.append(Helper.escapeActionScriptString(nname));
writer.append("\"+\".\"+");
}
fm.name.appendTo(writer, localData);
writer.append("))");
}
}
}
}
}
object.toString(writer, localData);
if (object.getPrecedence() > getPrecedence() || objectIsCall) {
writer.append(")");
if (!isGetDefinition) {
if (object.getPrecedence() > getPrecedence() || objectIsCall) {
writer.append("(");
}
object.toString(writer, localData);
if (object.getPrecedence() > getPrecedence() || objectIsCall) {
writer.append(")");
}
}
writer.spaceBeforeCallParenthesis(args.size());
if (object instanceof InitVectorAVM2Item) {

View File

@@ -16,11 +16,14 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Helper;
import java.util.List;
import java.util.Objects;
@@ -86,7 +89,36 @@ public class ConstructPropAVM2Item extends AVM2Item {
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
writer.append("new ");
formatProperty(writer, object, propertyName, localData, isStatic, false);
boolean isGetDefinition = false;
if (propertyName instanceof FullMultinameAVM2Item) {
FullMultinameAVM2Item fm = (FullMultinameAVM2Item) propertyName;
if (fm.multinameIndex > 0) {
Multiname m = localData.abc.constants.getMultiname(fm.multinameIndex);
if (m.kind == Multiname.MULTINAMEL) {
if (m.getNamespaceSet(localData.abc.constants).namespaces.length == 1) {
isGetDefinition = true;
writer.append("(");
if (localData.fullyQualifiedNames.contains(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"))) {
writer.append("flash.utils.getDefinitionByName");
} else {
writer.append("getDefinitionByName");
}
writer.append("(");
String nname = m.getSingleNamespace(localData.abc.constants).getName(localData.abc.constants).toRawString();
if (!nname.isEmpty()) {
writer.append("\"");
writer.append(Helper.escapeActionScriptString(nname));
writer.append("\"+\".\"+");
}
fm.name.appendTo(writer, localData);
writer.append("))");
}
}
}
}
if (!isGetDefinition) {
formatProperty(writer, object, propertyName, localData, isStatic, false);
}
writer.spaceBeforeCallParenthesis(args.size());
writer.append("(");
for (int a = 0; a < args.size(); a++) {

View File

@@ -989,7 +989,7 @@ public class Multiname {
* @return True if this MULTINAME kind with only one namespace
*/
public boolean isMULTINAMEwithOneNs(AVM2ConstantPool pool) {
return kind == MULTINAME && pool.getNamespaceSet(namespace_set_index).namespaces.length == 1;
return (kind == MULTINAME || kind == MULTINAMEL || kind == MULTINAMELA) && pool.getNamespaceSet(namespace_set_index).namespaces.length == 1;
}
/**

View File

@@ -21,10 +21,13 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.AlchemyTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetOuterScopeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns;
import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
@@ -196,6 +199,7 @@ public class DependencyParser {
}
}
boolean wasNewClass = false;
AVM2Instruction prevIns = null;
for (int i = 0; i < body.getCode().code.size(); i++) {
AVM2Instruction ins = body.getCode().code.get(i);
@@ -245,6 +249,26 @@ public class DependencyParser {
}
}
}
if (ins.definition instanceof ConstructPropIns) {
Multiname m = abc.constants.getMultiname(ins.operands[0]);
if (m != null) {
if (m.kind == Multiname.MULTINAMEL) {
Dependency dep = new Dependency(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"), DependencyType.NAMESPACE);
dependencies.add(dep);
}
}
}
//TODO: what if there's jump between getproperty and construct?
if (ins.definition instanceof ConstructIns && prevIns != null && prevIns.definition instanceof GetPropertyIns) {
Multiname m = abc.constants.getMultiname(prevIns.operands[0]);
if (m != null) {
if (m.kind == Multiname.MULTINAMEL) {
Dependency dep = new Dependency(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"), DependencyType.NAMESPACE);
dependencies.add(dep);
}
}
}
if (classIndex > -1 && ins.definition instanceof GetOuterScopeIns) {
if (ins.operands[0] > 0) { //first is global
DottedChain type = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, true);
@@ -282,6 +306,7 @@ public class DependencyParser {
numberContextRef.setVal(ins.operands[k]);
}
}
prevIns = ins;
}
}
}

View File

@@ -748,4 +748,25 @@ public class ActionScript3ClassTest extends ActionScript3DecompileTestBase {
+ "}\n"
);
}
@Test
public void testConstructDynamically() {
decompileScriptPack("assembled", "tests.TestConstructDynamically", "package tests\n"
+ "{\n"
+ "import flash.display.*;\n"
+ "import flash.utils.getDefinitionByName;\n"
+ "public class TestConstructDynamically\n"
+ "{\n"
+ "public function TestConstructDynamically()\n"
+ "{\n"
+ "super();\n"
+ "}\n"
+ "public function test() : void\n"
+ "{\n"
+ "var _loc1_:* = new (getDefinitionByName(\"flash.display\"+\".\"+\"Sprite\"))();\n"
+ "_loc1_ = new (getDefinitionByName(\"Object\"))();\n"
+ "}\n"
+ "}\n"
+ "}");
}
}

View File

@@ -40,5 +40,6 @@ program
#include "tests/TestAlwaysBreak.script.asasm"
#include "tests/TestAlwaysBreak2.script.asasm"
#include "tests/TestPushPlacement.script.asasm"
#include "tests/TestConstructDynamically.script.asasm"
; place to add next
end ; program

View File

@@ -0,0 +1,70 @@
class
refid "tests:TestConstructDynamically"
instance QName(PackageNamespace("tests"), "TestConstructDynamically")
extends QName(PackageNamespace(""), "Object")
flag SEALED
flag PROTECTEDNS
protectedns ProtectedNamespace("tests:TestConstructDynamically")
iinit
refid "tests:TestConstructDynamically/instance/init"
body
maxstack 1
localcount 1
initscopedepth 4
maxscopedepth 5
code
getlocal0
pushscope
getlocal0
constructsuper 0
returnvoid
end ; code
end ; body
end ; method
trait method QName(PackageNamespace(""), "test")
method
refid "tests:TestConstructDynamically/instance/test"
returns QName(PackageNamespace(""), "void")
body
maxstack 2
localcount 4
initscopedepth 4
maxscopedepth 5
code
getlocal0
pushscope
pushstring "Sprite"
findpropstrict MultinameL([PackageNamespace("flash.display")])
pushstring "Sprite"
constructprop MultinameL([PackageNamespace("flash.display")]), 0
setlocal1
pushstring "Object"
findpropstrict MultinameL([PackageNamespace("")])
pushstring "Object"
getproperty MultinameL([PackageNamespace("")])
construct 0
setlocal1
returnvoid
end ; code
end ; body
end ; method
end ; trait
end ; instance
cinit
refid "tests:TestConstructDynamically/class/init"
body
maxstack 1
localcount 1
initscopedepth 3
maxscopedepth 4
code
getlocal0
pushscope
returnvoid
end ; code
end ; body
end ; method
end ; class

View File

@@ -0,0 +1,29 @@
script
sinit
refid "tests:TestConstructDynamically/init"
body
maxstack 2
localcount 1
initscopedepth 1
maxscopedepth 3
code
getlocal0
pushscope
findpropstrict Multiname("TestConstructDynamically", [PackageNamespace("tests")])
getlex QName(PackageNamespace(""), "Object")
pushscope
getlex Multiname("Object", [PrivateNamespace(null, "tests:TestConstructDynamically"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")])
newclass "tests:TestConstructDynamically"
popscope
initproperty QName(PackageNamespace("tests"), "TestConstructDynamically")
returnvoid
end ; code
end ; body
end ; method
trait class QName(PackageNamespace("tests"), "TestConstructDynamically")
#include "TestConstructDynamically.class.asasm"
end ; trait
end ; script