diff --git a/CHANGELOG.md b/CHANGELOG.md index 614b6b452..8c041f727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file. - Incorrectly colliding class names detection on script private classes - AS3 deobfuscator of registers parsing of exception targets - AS3 code with exception start/end not matching instruction boundary +- AS3 deobfuscator in some cases ### Changed - AS3 test methods separated to classes 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 ff76d9a1d..f0cdba7b9 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 @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorGetSet; import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorJumps; import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorRegistersOld; import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorSimpleOld; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorZeroJumpsNullPushes; import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2ExecutionException; import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2VerifyErrorException; import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph; @@ -2333,6 +2334,9 @@ public class AVM2Code implements Cloneable { try (Statistics s = new Statistics("AVM2DeobfuscatorJumps")) { new AVM2DeobfuscatorJumps().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body); } + try (Statistics s = new Statistics("AVM2DeobfuscatorZeroJumpsNullPushes")) { + new AVM2DeobfuscatorZeroJumpsNullPushes().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body); + } return 1; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java index 3bc0980be..ab7e106be 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.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.abc.avm2.deobfuscation; import com.jpexs.decompiler.flash.abc.ABC; @@ -109,7 +110,7 @@ import java.util.Set; * * @author JPEXS */ -public class AVM2DeobfuscatorSimpleOld extends SWFDecompilerAdapter { +public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPushes { private static final UndefinedAVM2Item UNDEFINED_ITEM = new UndefinedAVM2Item(null, null); @@ -186,60 +187,6 @@ public class AVM2DeobfuscatorSimpleOld extends SWFDecompilerAdapter { return false; } - protected boolean removeZeroJumps(AVM2Code code, MethodBody body) throws InterruptedException { - boolean result = false; - for (int i = 0; i < code.code.size(); i++) { - AVM2Instruction ins = code.code.get(i); - if (ins.definition instanceof JumpIns) { - if (ins.operands[0] == 0) { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - - code.removeInstruction(i, body); - i--; - result = true; - } - } - } - return result; - } - - protected boolean removeNullPushes(AVM2Code code, MethodBody body) throws InterruptedException { - boolean result = false; - Set offsets = code.getImportantOffsets(body, true); - - // Deliberately skip over instruction zero - for (int i = 1; i < code.code.size(); i++) { - AVM2Instruction ins1 = code.code.get(i - 1); - AVM2Instruction ins2 = code.code.get(i); - if (ins2.definition instanceof PopIns - && !offsets.contains(ins2.getAddress()) - && (ins1.definition instanceof PushByteIns - || ins1.definition instanceof PushDoubleIns - || ins1.definition instanceof PushFalseIns - || ins1.definition instanceof PushIntIns - || ins1.definition instanceof PushNanIns - || ins1.definition instanceof PushNullIns - || ins1.definition instanceof PushShortIns - || ins1.definition instanceof PushStringIns - || ins1.definition instanceof PushTrueIns - || ins1.definition instanceof PushUIntIns - || ins1.definition instanceof PushUndefinedIns)) { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - - code.removeInstruction(i - 1, body); - i--; - code.removeInstruction(i, body); - i--; - offsets = code.getImportantOffsets(body, true); //update offsets, they changed because of removing instruction - result = true; - } - } - return result; - } protected AVM2LocalData newLocalData(int scriptIndex, ABC abc, AVM2ConstantPool cpool, MethodBody body, boolean isStatic, int classIndex) { AVM2LocalData localData = new AVM2LocalData(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorZeroJumpsNullPushes.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorZeroJumpsNullPushes.java new file mode 100644 index 000000000..083dcd4d0 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorZeroJumpsNullPushes.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010-2021 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.abc.avm2.deobfuscation; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushDoubleIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushFalseIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNanIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushTrueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUIntIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter; +import java.util.ArrayList; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter { + protected boolean removeZeroJumps(AVM2Code code, MethodBody body) throws InterruptedException { + boolean result = false; + for (int i = 0; i < code.code.size(); i++) { + AVM2Instruction ins = code.code.get(i); + if (ins.definition instanceof JumpIns) { + if (ins.operands[0] == 0) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + + code.removeInstruction(i, body); + i--; + result = true; + } + } + } + return result; + } + + protected boolean removeNullPushes(AVM2Code code, MethodBody body) throws InterruptedException { + boolean result = false; + Set offsets = code.getImportantOffsets(body, true); + + // Deliberately skip over instruction zero + for (int i = 1; i < code.code.size(); i++) { + AVM2Instruction ins1 = code.code.get(i - 1); + AVM2Instruction ins2 = code.code.get(i); + if (ins2.definition instanceof PopIns + && !offsets.contains(ins2.getAddress()) + && (ins1.definition instanceof PushByteIns + || ins1.definition instanceof PushDoubleIns + || ins1.definition instanceof PushFalseIns + || ins1.definition instanceof PushIntIns + || ins1.definition instanceof PushNanIns + || ins1.definition instanceof PushNullIns + || ins1.definition instanceof PushShortIns + || ins1.definition instanceof PushStringIns + || ins1.definition instanceof PushTrueIns + || ins1.definition instanceof PushUIntIns + || ins1.definition instanceof PushUndefinedIns)) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + + code.removeInstruction(i - 1, body); + i--; + code.removeInstruction(i, body); + i--; + offsets = code.getImportantOffsets(body, true); //update offsets, they changed because of removing instruction + result = true; + } + } + return result; + } + + @Override + public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException { + AVM2Code code = body.getCode(); + removeZeroJumps(code, body); + removeNullPushes(code, body); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java index ae70317b7..171082c73 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java @@ -864,7 +864,9 @@ public class AVM2Graph extends Graph { GraphPart exAfterPart = endIpPart; - stack.clear(); //If the original code (before check()) had "if" in it, there would be something on stack + if (part.nextParts.size() > 1 && !stack.isEmpty()) { //If the original code (before check()) had "if" in it, there would be something on stack + stack.pop(); + } if (finallyException == null) { List stopPart2 = new ArrayList<>(stopPart); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index cdacf1b0e..78f99bdd0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -45,6 +45,7 @@ import com.jpexs.helpers.Helper; import com.jpexs.helpers.MemoryInputStream; import com.jpexs.helpers.stat.Statistics; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List;