mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-07 15:18:10 +00:00
AS3 DeobfuscatorJumps fix
New DeobfuscatorGroupParts (unused yet)
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.IfTypeIns;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Stub for deobfuscator merging jump parts
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class AVM2DeobfuscatorGroupParts extends SWFDecompilerAdapter {
|
||||
|
||||
/*
|
||||
blk_1
|
||||
jump A
|
||||
B:
|
||||
blk_2
|
||||
jump C
|
||||
A: blk_3
|
||||
jump B
|
||||
C: blk_4
|
||||
|
||||
=>
|
||||
jump A
|
||||
A: jump B
|
||||
B: jump C
|
||||
blk_1
|
||||
blk_3
|
||||
blk_2
|
||||
blk_4
|
||||
|
||||
----------------------
|
||||
|
||||
blk_1
|
||||
jump A
|
||||
B:
|
||||
blk_2
|
||||
jump C
|
||||
A: blk_3
|
||||
if B
|
||||
blk_5
|
||||
C: blk_4
|
||||
|
||||
=>
|
||||
jump A
|
||||
B:
|
||||
blk_2
|
||||
jump C
|
||||
A: blk_1
|
||||
blk_3
|
||||
if B
|
||||
blk_5
|
||||
C:
|
||||
blk_4
|
||||
|
||||
----------------------
|
||||
blk_1
|
||||
jump A
|
||||
B:
|
||||
blk_2
|
||||
if C
|
||||
A: blk_3
|
||||
jump B
|
||||
C: blk_4
|
||||
|
||||
=>
|
||||
|
||||
blk_1
|
||||
jump A
|
||||
B:
|
||||
blk_3
|
||||
blk_2
|
||||
if C
|
||||
A: jump B
|
||||
C: blk_4
|
||||
|
||||
*/
|
||||
@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();
|
||||
code.removeDeadCode(body);
|
||||
|
||||
Map<Integer, List<Integer>> refs = body.getCode().visitCode(body);
|
||||
for (int i = 0; i < code.code.size(); i++) {
|
||||
AVM2Instruction ins = code.code.get(i);
|
||||
if (ins.definition instanceof JumpIns) {
|
||||
long targetAddr = ins.getTargetAddress();
|
||||
int targetIp = code.adr2pos(targetAddr);
|
||||
|
||||
if (realRefs(refs, targetIp) == 1) {
|
||||
int startIp = 0;
|
||||
//find preceeding ip which is start of the "part"
|
||||
for (int j = i; j >= 0; j--) {
|
||||
AVM2Instruction startIns = code.code.get(j);
|
||||
if (j < i && ((startIns.definition instanceof IfTypeIns) || (startIns.definition instanceof LookupSwitchIns))) {
|
||||
startIp = j + 1;
|
||||
break;
|
||||
} else {
|
||||
long srcAddr = code.pos2adr(j);
|
||||
boolean exceptionMismatch = false;
|
||||
for (int e = 0; e < body.exceptions.length; e++) {
|
||||
boolean sourceMatch = srcAddr >= body.exceptions[e].start && srcAddr < body.exceptions[e].end;
|
||||
boolean targetMatch = targetAddr >= body.exceptions[e].start && targetAddr < body.exceptions[e].end;
|
||||
if (sourceMatch != targetMatch) {
|
||||
exceptionMismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exceptionMismatch) {
|
||||
startIp = j + 1;
|
||||
break;
|
||||
} else if (realRefs(refs, j) > 1) {
|
||||
startIp = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (startIp < i) { //only when there is something to move
|
||||
List<AVM2Instruction> movedInstructions = new ArrayList<>();
|
||||
for (int k = startIp; k < i; k++) {
|
||||
movedInstructions.add(code.code.get(startIp));
|
||||
code.removeInstruction(startIp, body);
|
||||
}
|
||||
|
||||
int newTargetIp = targetIp;
|
||||
if (targetIp > i) { // forward jump
|
||||
newTargetIp -= movedInstructions.size();
|
||||
}
|
||||
for (int m = 0; m < movedInstructions.size(); m++) {
|
||||
code.insertInstruction(newTargetIp + m, movedInstructions.get(m), body);
|
||||
}
|
||||
i = -1;
|
||||
refs = body.getCode().visitCode(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new AVM2DeobfuscatorJumps().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
}
|
||||
|
||||
private int realRefs(Map<Integer, List<Integer>> refs, int ip) {
|
||||
int refCount = 0;
|
||||
for (int r : refs.get(ip)) {
|
||||
if (r >= 0) {
|
||||
refCount++;
|
||||
}
|
||||
}
|
||||
return refCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class AVM2DeobfuscatorJumps extends SWFDecompilerAdapter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exceptionMismatch) {
|
||||
if (exceptionMismatch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -80,8 +80,22 @@ public class AVM2DeobfuscatorJumps extends SWFDecompilerAdapter {
|
||||
for (int r : refs.get(i)) {
|
||||
if (r >= 0) { //Not Exception start/end
|
||||
AVM2Instruction srcIns = code.code.get(r);
|
||||
srcAddr = srcIns.getAddress();
|
||||
|
||||
if ((srcIns.definition instanceof JumpIns) || ((srcIns.definition instanceof IfTypeIns) && (r != i - 1))) {
|
||||
|
||||
exceptionMismatch = false;
|
||||
for (int e = 0; e < body.exceptions.length; e++) {
|
||||
boolean sourceMatch = srcAddr >= body.exceptions[e].start && srcAddr < body.exceptions[e].end;
|
||||
boolean targetMatch = targetAddr >= body.exceptions[e].start && targetAddr < body.exceptions[e].end;
|
||||
if (sourceMatch != targetMatch) {
|
||||
exceptionMismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exceptionMismatch) {
|
||||
continue;
|
||||
}
|
||||
int oldop = srcIns.operands[0];
|
||||
srcIns.operands[0] = (int) (targetAddr - (srcIns.getAddress() + srcIns.getBytesLength()));
|
||||
if (srcIns.operands[0] != oldop) {
|
||||
|
||||
@@ -18,6 +18,7 @@ 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.AVM2DeobfuscatorGroupParts;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AVM2DeobfuscatorJumps;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser;
|
||||
@@ -31,6 +32,7 @@ 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.helpers.SWFDecompilerAdapter;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.decompiler.graph.CompilationException;
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -64,7 +66,7 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
|
||||
swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false);
|
||||
}
|
||||
|
||||
private String recompilePCode(String str) throws IOException, AVM2ParseException, InterruptedException {
|
||||
private String recompilePCode(String str, SWFDecompilerAdapter deobfuscator) throws IOException, AVM2ParseException, InterruptedException {
|
||||
str = "code\r\n"
|
||||
+ "getlocal0\r\n"
|
||||
+ "pushscope\r\n"
|
||||
@@ -89,7 +91,7 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
|
||||
MethodBody b = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]);
|
||||
AVM2Code code = ASM3Parser.parse(abc, new StringReader(str), null, b, new MethodInfo());
|
||||
b.setCode(code);
|
||||
new AVM2DeobfuscatorJumps().avm2CodeRemoveTraps("test", 0, true, 0, abc, null, 0, b);
|
||||
deobfuscator.avm2CodeRemoveTraps("test", 0, true, 0, abc, null, 0, b);
|
||||
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
|
||||
code.toASMSource(abc, abc.constants, new MethodInfo(), new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]), ScriptExportMode.PCODE, writer);
|
||||
String ret = writer.toString();
|
||||
@@ -239,7 +241,7 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
|
||||
+ "jump b\r\n" //should not change
|
||||
+ "a:jump c\r\n"
|
||||
+ "c:pushbyte 4\r\n"
|
||||
+ "b:pushbyte 3\r\n");
|
||||
+ "b:pushbyte 3\r\n", new AVM2DeobfuscatorJumps());
|
||||
Assert.assertEquals(res, "getlocal0\r\n"
|
||||
+ "pushscope\r\n"
|
||||
+ "pushbyte 3\r\n"
|
||||
@@ -253,6 +255,52 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
|
||||
+ "returnvoid\r\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupParts() throws Exception {
|
||||
String res = recompilePCode(
|
||||
"pushbyte 1\r\npop\r\n"
|
||||
+ "jump A\r\n"
|
||||
+ "B:pushbyte 3\r\npop\r\n"
|
||||
+ "jump C\r\n"
|
||||
+ "A:pushbyte 2\r\npop\r\n"
|
||||
+ "jump B\r\n"
|
||||
+ "C:pushbyte 4\r\npop\r\n", new AVM2DeobfuscatorGroupParts());
|
||||
Assert.assertEquals(res, "getlocal0\r\n"
|
||||
+ "pushscope\r\n"
|
||||
+ "pushbyte 1\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "pushbyte 2\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "pushbyte 3\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "pushbyte 4\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "returnvoid\r\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupParts2() throws Exception {
|
||||
String res = recompilePCode(
|
||||
"pushbyte 1\r\npop\r\n"
|
||||
+ "jump A\r\n"
|
||||
+ "B:pushbyte 3\r\npop\r\n"
|
||||
+ "jump C\r\n"
|
||||
+ "A:pushbyte 2\r\npop\r\n"
|
||||
+ "jump B\r\n"
|
||||
+ "C:pushbyte 4\r\npop\r\n", new AVM2DeobfuscatorGroupParts());
|
||||
Assert.assertEquals(res, "getlocal0\r\n"
|
||||
+ "pushscope\r\n"
|
||||
+ "pushbyte 1\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "pushbyte 2\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "pushbyte 3\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "pushbyte 4\r\n"
|
||||
+ "pop\r\n"
|
||||
+ "returnvoid\r\n");
|
||||
}
|
||||
|
||||
// TODO: JPEXS @Test
|
||||
public void testNotRemoveParams() throws Exception {
|
||||
String res = recompile("function tst(p1,p2){"
|
||||
|
||||
Reference in New Issue
Block a user