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 4c259920b..aadf42769 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 @@ -1771,13 +1771,14 @@ public class AVM2Code implements Cloneable { } public void updateOffsets(OffsetUpdater updater, MethodBody body) { - for (AVM2Instruction ins : code) { + for (int i = 0; i < code.size(); i++) { + AVM2Instruction ins = code.get(i); if (ins.definition instanceof LookupSwitchIns) { long target = ins.offset + ins.operands[0]; - ins.operands[0] = updater.updateOperandOffset(target, ins.operands[0]); + ins.operands[0] = updater.updateOperandOffset(ins.offset, target, ins.operands[0]); for (int k = 2; k < ins.operands.length; k++) { target = ins.offset + ins.operands[k]; - ins.operands[k] = updater.updateOperandOffset(target, ins.operands[k]); + ins.operands[k] = updater.updateOperandOffset(ins.offset, target, ins.operands[k]); } } else { /*for (int j = 0; j < ins.definition.operands.length; j++) { @@ -1789,18 +1790,16 @@ public class AVM2Code implements Cloneable { //Faster, but not so universal if ((ins.definition instanceof JumpIns) || (ins.definition instanceof IfTypeIns)) { long target = ins.offset + ins.getBytes().length + ins.operands[0]; - ins.operands[0] = updater.updateOperandOffset(target, ins.operands[0]); + ins.operands[0] = updater.updateOperandOffset(ins.offset, target, ins.operands[0]); } } + ins.offset = updater.updateInstructionOffset(ins.offset); } for (ABCException ex : body.exceptions) { - ex.start = updater.updateOperandOffset(ex.start, ex.start); - ex.end = updater.updateOperandOffset(ex.end, ex.end); - ex.target = updater.updateOperandOffset(ex.target, ex.target); - } - for (AVM2Instruction ins : code) { - ins.offset = updater.updateInstructionOffset(ins.offset); + ex.start = updater.updateOperandOffset(-1, ex.start, ex.start); + ex.end = updater.updateOperandOffset(-1, ex.end, ex.end); + ex.target = updater.updateOperandOffset(-1, ex.target, ex.target); } } @@ -1814,7 +1813,7 @@ public class AVM2Code implements Cloneable { } @Override - public int updateOperandOffset(long targetAddress, int offset) { + public int updateOperandOffset(long insAddr, long targetAddress, int offset) { adr2pos(targetAddress); return offset; } @@ -1826,7 +1825,7 @@ public class AVM2Code implements Cloneable { if ((pos < 0) || (pos >= code.size())) { throw new IndexOutOfBoundsException(); } - //checkValidOffsets(body); + checkValidOffsets(body); final long remOffset = code.get(pos).offset; final int byteCount = code.get(pos).getBytes().length; updateOffsets(new OffsetUpdater() { @@ -1839,8 +1838,11 @@ public class AVM2Code implements Cloneable { } @Override - public int updateOperandOffset(long targetAddress, int offset) { + public int updateOperandOffset(long insAddr, long targetAddress, int offset) { if (targetAddress > remOffset) { + if (insAddr > remOffset) { + return offset; + } return offset - byteCount; } return offset; @@ -1848,7 +1850,7 @@ public class AVM2Code implements Cloneable { }, body); code.remove(pos); invalidateCache(); - //checkValidOffsets(body); + checkValidOffsets(body); //System.exit(0); } @@ -1891,23 +1893,28 @@ public class AVM2Code implements Cloneable { updateOffsets(new OffsetUpdater() { @Override - public long updateInstructionOffset(long offset) { - if (offset > instruction.offset) { - return offset + byteDelta; + public long updateInstructionOffset(long addr) { + if (addr > instruction.offset) { + return addr + byteDelta; } - return offset; + return addr; } @Override - public int updateOperandOffset(long targetAddress, int offset) { - if (targetAddress > instruction.offset) { + public int updateOperandOffset(long insAddr, long targetAddress, int offset) { + if (targetAddress > instruction.offset && insAddr <= instruction.offset) { return offset + byteDelta; } + if (targetAddress <= instruction.offset && insAddr > instruction.offset) { + return offset - byteDelta; + } return offset; } }, body); } code.set(pos, instruction); + invalidateCache(); + checkValidOffsets(body); } /** @@ -1922,6 +1929,7 @@ public class AVM2Code implements Cloneable { * @param body Method body (used for try handling) */ public void insertInstruction(int pos, AVM2Instruction instruction, boolean mapOffsetsAfterIns, MethodBody body) { + checkValidOffsets(body); if (pos < 0) { pos = 0; } @@ -1945,8 +1953,12 @@ public class AVM2Code implements Cloneable { } @Override - public int updateOperandOffset(long targetAddress, int offset) { + public int updateOperandOffset(long insAddr, long targetAddress, int offset) { + //System.err.println("instruction.offset=" + instruction.offset); if ((targetAddress > instruction.offset) || (mapOffsetsAfterIns && (targetAddress == instruction.offset))) { + if (insAddr >= instruction.offset) { + return offset; + } return offset + byteCount; } return offset; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java index 66d4ecbe9..33ea22753 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java @@ -22,7 +22,7 @@ package com.jpexs.decompiler.flash.abc.avm2; */ public interface OffsetUpdater { - public long updateInstructionOffset(long offset); + public long updateInstructionOffset(long addr); - public int updateOperandOffset(long targetAddress, int offset); + public int updateOperandOffset(long jumpAddr, long targetAddress, int offset); } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3AssemblerTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3AssemblerTest.java new file mode 100644 index 000000000..903fc4da9 --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3AssemblerTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorJumps; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; +import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DoABCDefineTag; +import com.jpexs.decompiler.flash.tags.Tag; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.StringReader; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author JPEXS + */ +public class ActionScript3AssemblerTest extends ActionScriptTestBase { + + private SWF swf; + + @BeforeClass + public void init() throws IOException, InterruptedException { + //Main.initLogging(false); + Configuration.autoDeobfuscate.set(true); + Configuration.deobfuscationMode.set(1); + swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); + } + + private int getBaseAddr() { + return 2; //getlocal_0 + pushscope + } + + private ABC getABC() { + return new ABC(new ABCContainerTag() { + + @Override + public ABC getABC() { + return null; + } + + @Override + public SWF getSwf() { + return swf; + } + + @Override + public int compareTo(ABCContainerTag o) { + return 0; + } + }); + } + + private MethodBody compilePCode(String str) throws IOException, AVM2ParseException, InterruptedException { + str = "code\r\n" + + "getlocal_0\r\n" + + "pushscope\r\n" + + str + + "returnvoid\r\n"; + + MethodBody b = new MethodBody(); + AVM2Code code = ASM3Parser.parse(new StringReader(str), getABC().constants, null, b, new MethodInfo()); + b.setCode(code); + return b; + } + + /*private String codeToStr(AVM2Code code) { + HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false); + code.toASMSource(getABC().constants, null, new MethodInfo(), new MethodBody(), ScriptExportMode.PCODE, writer); + String ret = writer.toString(); + return ret.substring(ret.lastIndexOf("code\r\n") + 6); + }*/ + @Test + public void removeInstruction() throws Exception { + MethodBody b = compilePCode("pushbyte 1\r\n" + + "setlocal_1\r\n" //remove this + + "jump label1\r\n" + + "pushtrue\r\n" + + "pop\r\n" + + "label1:pushfalse\r\n"); + b.getCode().removeInstruction(getBaseAddr() + 1, b); + } + + @Test + public void removeInstruction2() throws Exception { + MethodBody b = compilePCode("pushbyte 1\r\n" + + "setlocal_1\r\n" + + "jump label1\r\n" + + "pushtrue\r\n" + + "pop\r\n" //remove this + + "label1:pushfalse\r\n"); + b.getCode().removeInstruction(getBaseAddr() + 4, b); + } + + @Test + public void replaceIstruction() throws Exception { + MethodBody b = compilePCode("pushbyte 1\r\n" + + "setlocal_1\r\n" + + "jump label1\r\n" //remove this + + "jump label1\r\n" + + "pushtrue\r\n" + + "pop\r\n" + + "label1:pushfalse\r\n"); + b.getCode().replaceInstruction(getBaseAddr() + 2, new AVM2Instruction(0, new DeobfuscatePopIns(), new int[]{}), b); + } + + @Test + public void replaceIstruction2() throws Exception { + MethodBody b = compilePCode("pushbyte 1\r\n" + + "setlocal_1\r\n" + + "jump label1\r\n" + + "pushtrue\r\n" + + "jump label1\r\n" //remove this + + "pop\r\n" + + "label1:pushfalse\r\n"); + b.getCode().replaceInstruction(getBaseAddr() + 4, new AVM2Instruction(0, new DeobfuscatePopIns(), new int[]{}), b); + } +}