mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-12 21:01:57 +00:00
AVM2 execution fixes
This commit is contained in:
@@ -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<Long> 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<Integer, List<Integer>> refs) throws InterruptedException {
|
||||
List<Integer> toVisit = new ArrayList<>();
|
||||
List<Integer> toVisitLast = new ArrayList<>();
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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<Object> passArguments = new ArrayList<Object>();
|
||||
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<Object> passArguments = new ArrayList<Object>();
|
||||
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<GraphTargetItem> list) {
|
||||
|
||||
@@ -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<Object> passArguments = new ArrayList<Object>();
|
||||
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<Object> passArguments = new ArrayList<Object>();
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<GraphSourceItem> ret = new ArrayList<>();
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<String> 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<ABCContainerTag> 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<AS3ExecuteTask> 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);
|
||||
|
||||
Reference in New Issue
Block a user