From df36ce3548f8c680a4a4f10d9bab69a6736561d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 6 Jul 2015 17:54:57 +0200 Subject: [PATCH] AS: P-code reading fixes, better deobfuscation --- .../decompiler/flash/abc/avm2/AVM2Code.java | 129 ++++++++++++------ .../deobfuscation/AVM2DeobfuscatorJumps.java | 1 - .../AVM2DeobfuscatorRegisters.java | 8 +- .../deobfuscation/AVM2DeobfuscatorSimple.java | 12 +- .../flash/abc/avm2/graph/AVM2Graph.java | 10 +- .../flash/abc/types/MethodBody.java | 9 +- 6 files changed, 107 insertions(+), 62 deletions(-) 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 5a88af4f0..e565ca721 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 @@ -777,7 +777,7 @@ public class AVM2Code implements Cloneable { } public AVM2Code(ABCInputStream ais, MethodBody body) throws IOException { - Map codeMap = new TreeMap<>(); + Map codeMap = new HashMap<>(); DumpInfo diParent = ais.dumpInfo; List addresses = new ArrayList<>(); //Do not add new jumps when processing these addresses (unreachable code,etc.) @@ -814,19 +814,20 @@ public class AVM2Code implements Cloneable { address = unAdresses.remove(0); handleJumps = false; } - if (codeMap.containsKey(address) && !(codeMap.get(address).definition instanceof NopIns)) { - continue; - } if (address < startPos) // no jump outside block { continue; } - boolean afterExit = false; try { ais.seek(address); while (ais.available() > 0) { DumpInfo di = ais.newDumpLevel("instruction", "instruction"); long startOffset = ais.getPosition(); + + if (codeMap.containsKey(startOffset) && !(codeMap.get(startOffset).definition instanceof NopIns)) { + continue loopaddr; + } + int instructionCode = ais.read("instructionCode"); InstructionDefinition instr = instructionSet[instructionCode]; if (instr instanceof LookupSwitchIns) { @@ -842,7 +843,6 @@ public class AVM2Code implements Cloneable { } if (instr != null) { int[] actualOperands = null; - long beforeSwitchPos = ais.getPosition(); if (instructionCode == 0x1b) { // switch int firstOperand = ais.readS24("default_offset"); @@ -851,7 +851,7 @@ public class AVM2Code implements Cloneable { boolean invalidSwitch = false; //If there are already some instructions in the lookupswitch bytes, the lookupswitch is invalid (obfuscation) - for (long a = beforeSwitchPos; a < afterCasePos; a++) { + for (long a = startOffset; a < afterCasePos; a++) { if (codeMap.containsKey(a) && (!(codeMap.get(a).definition instanceof NopIns))) { invalidSwitch = true; break; @@ -899,40 +899,67 @@ public class AVM2Code implements Cloneable { AVM2Instruction ai = new AVM2Instruction(startOffset, instr, actualOperands); long endOffset = ais.getPosition(); + boolean hasRoom = true; + for (long p = startOffset; p < endOffset; p++) { + if (codeMap.containsKey(p) && !(codeMap.get(p).definition instanceof NopIns)) { + hasRoom = false; + } + } + + //There is no room for this instruction (it is invalid?) + if (!hasRoom) { + continue loopaddr; + } for (long p = startOffset; p < endOffset; p++) { codeMap.put(p, ai); } ais.endDumpLevel(instr.instructionCode); - if (handleJumps) { - if ((instr instanceof IfTypeIns)) { + if ((instr instanceof IfTypeIns)) { + if (handleJumps) { long target = ais.getPosition() + actualOperands[0]; addresses.add(target); - //addresses.add(ais.getPosition()); + } else { + actualOperands[0] = 0; } + } - if (instr instanceof JumpIns) { + if (instr instanceof JumpIns) { + if (handleJumps) { long target = ais.getPosition() + actualOperands[0]; addresses.add(target); unAdresses.add(ais.getPosition()); continue loopaddr; + } else { + actualOperands[0] = 0; } + } - if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction - unAdresses.add(ais.getPosition()); - continue loopaddr; - } - if (instr instanceof LookupSwitchIns) { - addresses.add(beforeSwitchPos + actualOperands[0]); - - for (int c = 2; c < actualOperands.length; c++) { - addresses.add(beforeSwitchPos + actualOperands[c]); - } + if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction + if (handleJumps) { unAdresses.add(ais.getPosition()); continue loopaddr; } } + if (instr instanceof LookupSwitchIns) { + if (handleJumps) { + addresses.add(startOffset + actualOperands[0]); + + for (int c = 2; c < actualOperands.length; c++) { + addresses.add(startOffset + actualOperands[c]); + } + unAdresses.add(ais.getPosition()); + continue loopaddr; + } else { + int swlen = (int) (endOffset - startOffset); + actualOperands[0] = swlen; + for (int c = 2; c < actualOperands.length; c++) { + actualOperands[c] = swlen; + } + } + } + } else { ais.endDumpLevel(); break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions) @@ -945,7 +972,8 @@ public class AVM2Code implements Cloneable { } } AVM2Instruction prev = null; - for (AVM2Instruction ins : codeMap.values()) { + for (int i = 0; i < availableBytes; i++) { + AVM2Instruction ins = codeMap.get((long) i); if (prev != ins) { code.add(ins); } @@ -1284,11 +1312,22 @@ public class AVM2Code implements Cloneable { } public int adr2pos(long address) throws ConvertException { + return adr2pos(address, false); + } + + public int adr2pos(long address, boolean nearest) throws ConvertException { if (!cacheActual) { buildCache(); } int ret = posCache.indexOf(address); if (ret == -1) { + if (nearest) { + for (long a : posCache) { + if (a > address) { + return posCache.indexOf(a); + } + } + } throw new ConvertException("Invalid jump to ofs" + Helper.formatAddress(address), -1); } return ret; @@ -1417,7 +1456,7 @@ public class AVM2Code implements Cloneable { } public int fixAddrAfterDebugLine(int addr) throws ConvertException { - return pos2adr(fixIPAfterDebugLine(adr2pos(addr))); + return pos2adr(fixIPAfterDebugLine(adr2pos(addr, true))); } public ConvertOutput toSourceOutput(String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ABC abc, AVM2ConstantPool constants, List method_info, MethodBody body, int start, int end, HashMap localRegNames, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, HashMap> refs) throws ConvertException, InterruptedException { @@ -1940,7 +1979,11 @@ public class AVM2Code implements Cloneable { //Faster, but not so universal if ((ins.definition instanceof JumpIns) || (ins.definition instanceof IfTypeIns)) { long target = ins.offset + ins.getBytesLength() + ins.operands[0]; - ins.operands[0] = updater.updateOperandOffset(ins.offset, target, ins.operands[0]); + try { + ins.operands[0] = updater.updateOperandOffset(ins.offset, target, ins.operands[0]); + } catch (ConvertException cex) { + throw new ConvertException("Invalid offset (" + ins + ")", i); + } } } ins.offset = updater.updateInstructionOffset(ins.offset); @@ -1991,6 +2034,7 @@ public class AVM2Code implements Cloneable { } public void checkValidOffsets(MethodBody body) { + invalidateCache(); updateOffsets(new OffsetUpdater() { @Override @@ -2001,6 +2045,9 @@ public class AVM2Code implements Cloneable { @Override public int updateOperandOffset(long insAddr, long targetAddress, int offset) { + if (insAddr == -1) { //do not check exceptions + return offset; + } adr2pos(targetAddress); return offset; } @@ -2013,18 +2060,9 @@ public class AVM2Code implements Cloneable { throw new IndexOutOfBoundsException(); } - //checkValidOffsets(body); AVM2Instruction ins = code.get(pos); final long remOffset = ins.offset; - int bc; - if (pos < code.size() - 1) { - // Checking the instuction length is not enough because in some - // obfuscated files the length of the lookupswitch is not the same - // as the difference of the offsets - bc = (int) (code.get(pos + 1).offset - remOffset); - } else { - bc = ins.getBytesLength(); - } + int bc = ins.getBytesLength(); final int byteCount = bc; updateOffsets(new OffsetUpdater() { @@ -2230,7 +2268,7 @@ public class AVM2Code implements Cloneable { new AVM2DeobfuscatorSimple().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, info, body); new AVM2DeobfuscatorRegisters().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, info, body); new AVM2DeobfuscatorJumps().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, info, body); - body.getCode().checkValidOffsets(body); // todo: only for debugging. checkValidOffsets can be made private later + //body.getCode().checkValidOffsets(body); // todo: only for debugging. checkValidOffsets can be made private later return 1; } } @@ -2470,12 +2508,12 @@ public class AVM2Code implements Cloneable { for (ABCException e : body.exceptions) { pos++; try { - visitCode(adr2pos(e.start), adr2pos(e.start) - 1, refs); - visitCode(adr2pos(e.start), -1, refs); - visitCode(adr2pos(e.target), adr2pos(e.end), refs); - visitCode(adr2pos(e.end), -pos, refs); + visitCode(adr2pos(e.start, true), adr2pos(e.start, true) - 1, refs); + visitCode(adr2pos(e.start, true), -1, refs); + visitCode(adr2pos(e.target), adr2pos(e.end, true), refs); + visitCode(adr2pos(e.end, true), -pos, refs); } catch (ConvertException ex) { - logger.log(Level.FINE, null, ex); + logger.log(Level.SEVERE, "Visitcode error", ex); } } return refs; @@ -2708,9 +2746,9 @@ public class AVM2Code implements Cloneable { restoreControlFlow(0, refs, visited2, appended); for (ABCException e : body.exceptions) { try { - restoreControlFlow(adr2pos(e.start), refs, visited2, appended); + restoreControlFlow(adr2pos(e.start, true), refs, visited2, appended); restoreControlFlow(adr2pos(e.target), refs, visited2, appended); - restoreControlFlow(adr2pos(e.end), refs, visited2, appended); + restoreControlFlow(adr2pos(e.end, true), refs, visited2, appended); } catch (ConvertException ex) { logger.log(Level.FINE, null, ex); } @@ -2782,17 +2820,19 @@ public class AVM2Code implements Cloneable { }*/ public void removeIgnored(AVM2ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { + //System.err.println("removing ignored..."); for (int i = 0; i < code.size(); i++) { if (code.get(i).ignored) { removeInstruction(i, body); i--; } } + //System.err.println("/ignored removed"); } public int removeDeadCode(AVM2ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { + invalidateCache(); HashMap> refs = visitCode(body); - int cnt = 0; for (int i = code.size() - 1; i >= 0; i--) { if (refs.get(i).isEmpty()) { @@ -2808,8 +2848,7 @@ public class AVM2Code implements Cloneable { AVM2Instruction ins = code.get(i); if (ins.definition instanceof JumpIns) { if (ins.operands[0] == 0) { - code.get(i).ignored = true; - //removeInstruction(i, body); + ins.ignored = true; cnt++; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java index aecf84b8f..913c71caf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorJumps.java @@ -83,6 +83,5 @@ public class AVM2DeobfuscatorJumps extends AVM2DeobfuscatorSimple { } removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); } while (found); - } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java index c41906a9e..17aecbc56 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java @@ -153,7 +153,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { } } - private int getFirstRegisterSetter(Reference assignment, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body, Set ignoredRegisters, Set ignoredGets) { + private int getFirstRegisterSetter(Reference assignment, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body, Set ignoredRegisters, Set ignoredGets) throws InterruptedException { AVM2Code code = body.getCode(); Map ret = new HashMap<>(); @@ -165,7 +165,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { return visitCode(assignment, new HashSet<>(), new TranslateStack("deo"), classIndex, isStatic, body, scriptIndex, abc, code, 0, code.code.size() - 1, res, ignoredRegisters, ignoredGets); } - private int visitCode(Reference assignment, Set visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, ExecutionResult result, Set ignored, Set ignoredGets) { + private int visitCode(Reference assignment, Set visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, ExecutionResult result, Set ignored, Set ignoredGets) throws InterruptedException { List output = new ArrayList<>(); AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex); localData.localRegs.put(0, new NullAVM2Item(null));//this @@ -295,7 +295,9 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { break; } } - } catch (Exception ex) { + } catch (InterruptedException ex) { + throw ex; + } catch (Throwable ex) { //ignore } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java index 4202d4107..a418fa806 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java @@ -185,10 +185,12 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { boolean result = false; for (int i = 0; i < actions.code.size(); i++) { AVM2Instruction action = actions.code.get(i); - if (action.definition instanceof JumpIns && action.operands[0] == 0) { - actions.removeInstruction(i, body); - i--; - result = true; + if (action.definition instanceof JumpIns) { + if (action.operands[0] == 0) { + actions.removeInstruction(i, body); + i--; + result = true; + } } } return result; @@ -418,7 +420,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { public void deobfuscate(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException { AVM2Code code = body.getCode(); - code.fixJumps(body); + //code.fixJumps(body); removeUnreachableActions(code, cpool, trait, minfo, body); removeObfuscationIfs(classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, new ArrayList<>()); removeZeroJumps(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 0646e50fc..ee207dbcb 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 @@ -145,18 +145,18 @@ public class AVM2Graph extends Graph { @Override protected void checkGraph(List allBlocks) { for (ABCException ex : body.exceptions) { - int startIp = avm2code.adr2pos(ex.start); - int endIp = avm2code.adr2pos(ex.end); - int targetIp = avm2code.adr2pos(ex.target); + /*int startAddr = avm2code.adr2pos(ex.start); + int endAddr = avm2code.adr2pos(ex.end); + int targetIp = avm2code.adr2pos(ex.target);*/ GraphPart target = null; for (GraphPart p : allBlocks) { - if (p.start == targetIp) { + if (avm2code.pos2adr(p.start) == ex.target) { target = p; break; } } for (GraphPart p : allBlocks) { - if (p.start >= startIp && p.end <= endIp) { + if (avm2code.pos2adr(p.start) >= ex.start && avm2code.pos2adr(p.end) <= ex.end) { p.throwParts.add(target); target.refs.add(p); } 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 0913802f3..2c008a8fd 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 @@ -141,8 +141,8 @@ public final class MethodBody implements Cloneable { public List getExceptionEntries() { List ret = new ArrayList<>(); for (ABCException e : exceptions) { - ret.add(getCode().adr2pos(e.start)); - ret.add(getCode().adr2pos(e.end)); + //ret.add(getCode().adr2pos(e.start)); + //ret.add(getCode().adr2pos(e.end)); ret.add(getCode().adr2pos(e.target)); } return ret; @@ -344,8 +344,11 @@ public final class MethodBody implements Cloneable { if (Configuration.autoDeobfuscate.get()) { try { body.getCode().removeTraps(constants, trait, method_info.get(this.method_info), body, abc, scriptIndex, classIndex, isStatic, path); + } catch (InterruptedException ex) { + throw ex; } catch (Throwable ex) { - logger.log(Level.SEVERE, "Error during deobfuscation: " + path, ex); + //ignore + return this; } }