From 1a90ab026dda36e3d70e2290e775e889ab96d681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 5 Jul 2015 20:07:08 +0200 Subject: [PATCH] Undefined registers handling (some obfuscators use getlocal_x when there is no selocal before) Issue #941 index out of bounds fix --- .../decompiler/flash/abc/avm2/AVM2Code.java | 11 +++- .../AVM2DeobfuscatorRegisters.java | 52 ++++++++++++------- .../deobfuscation/AVM2DeobfuscatorSimple.java | 39 ++++++++++++-- .../abc/avm2/model/LocalRegAVM2Item.java | 8 +-- 4 files changed, 80 insertions(+), 30 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 e53ba96cf..ed1462254 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 @@ -247,6 +247,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.SetSlotAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.DeclarationAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; @@ -1516,7 +1517,12 @@ public class AVM2Code implements Cloneable { } else if (ins.definition instanceof DupIns) { int nextPos; do { - AVM2Instruction insAfter = code.get(ip + 1); + AVM2Instruction insAfter = ip + 1 < code.size() ? code.get(ip + 1) : null; + if (insAfter == null) { + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + ip++; + break; + } AVM2Instruction insBefore = ins; if (ip - 1 >= start) { insBefore = code.get(ip - 1); @@ -1813,6 +1819,9 @@ public class AVM2Code implements Cloneable { HashMap localRegs = new HashMap<>(); int regCount = getRegisterCount(); + for (int i = 0; i < regCount; i++) { + localRegs.put(0, new UndefinedAVM2Item(null)); + } //try { list = AVM2Graph.translateViaGraph(path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs); 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 5b17d5284..4ebb64a3e 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 @@ -71,8 +71,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { @Override public void deobfuscate(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody abody) throws InterruptedException { - MethodBody body = abody.clone(); - removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + removeUnreachableActions(abody.getCode(), cpool, trait, minfo, abody); Map outFirstAssigned = new HashMap<>(); Map outFirstAssignments = new HashMap<>(); @@ -83,29 +82,46 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { List ignored = new ArrayList<>(); Map registers = new HashMap<>(); - getFirstRegistersUsage(outAssignCount1, outFirstAssigned, outFirstAssignments, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, ignored, registers); - ignored.addAll(outFirstAssignments.values()); - registers.putAll(outFirstAssigned); - replaceSingleUseRegisters(registers, ignored, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); + boolean extended = true; - super.deobfuscate(path, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); - removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + MethodBody body = abody; - //second pass - ignore all first assignments - registers.clear(); - ignored.clear(); - outFirstAssignments.clear(); - getFirstRegistersUsage(outAssignCount2, new HashMap<>(), outFirstAssignments, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, ignored, registers); + if (extended) { + body = abody.clone(); + getFirstRegistersUsage(outAssignCount1, outFirstAssigned, outFirstAssignments, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, ignored, registers); + ignored.addAll(outFirstAssignments.values()); + registers.putAll(outFirstAssigned); - for (int regId : outAssignCount1.keySet()) { - int ac = outAssignCount2.containsKey(regId) ? outAssignCount2.get(regId) : 0; - if (ac == 0) { - singleRegisters.put(regId, outFirstAssigned.get(regId)); + replaceSingleUseRegisters(registers, ignored, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); + + super.deobfuscate(path, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); + removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + + //second pass - ignore all first assignments + registers.clear(); + ignored.clear(); + outFirstAssignments.clear(); + getFirstRegistersUsage(outAssignCount2, new HashMap<>(), outFirstAssignments, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, ignored, registers); + + for (int regId : outAssignCount1.keySet()) { + int ac = outAssignCount2.containsKey(regId) ? outAssignCount2.get(regId) : 0; + if (ac == 0) { + singleRegisters.put(regId, outFirstAssigned.get(regId)); + } + } + body = abody; + } else { + getFirstRegistersUsage(outAssignCount1, outFirstAssigned, outFirstAssignments, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, ignored, registers); + for (int regId : outAssignCount1.keySet()) { + int ac = outAssignCount1.get(regId); + if (ac == 1) { + singleRegisters.put(regId, outFirstAssigned.get(regId)); + } } } - body = abody; + //body.max_regs replaceSingleUseRegisters(singleRegisters, null, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); super.deobfuscate(path, classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); 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 653efad41..812d5d58c 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 @@ -45,7 +45,9 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIn import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessEqualsIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessThanIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.StrictEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; @@ -60,6 +62,8 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewFunctionAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; @@ -233,6 +237,9 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { localData.isStatic = isStatic; localData.classIndex = classIndex; localData.localRegs = new HashMap<>(); + for (int i = 0; i < body.max_regs; i++) { + localData.localRegs.put(i, new UndefinedAVM2Item(null)); + } localData.scopeStack = new ScopeStack(true); localData.constants = cpool; localData.methodInfo = abc.method_info; @@ -270,7 +277,17 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { } AVM2Instruction action = code.code.get(idx); - action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + if (action.definition instanceof NewFunctionIns) { + if (idx + 1 < code.code.size()) { + if (code.code.get(idx + 1).definition instanceof PopIns) { + code.removeInstruction(idx + 1, body); + code.removeInstruction(idx, body); + continue; + } + } + } else { + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + } InstructionDefinition def = action.definition; Class allowedDefs[] = new Class[]{ @@ -307,7 +324,9 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { GreaterThanIns.class, LessThanIns.class, StrictEqualsIns.class, - PopIns.class + PopIns.class, + GetLocalTypeIns.class, + NewFunctionIns.class }; boolean ok = false; @@ -321,6 +340,17 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { break; } + if (def instanceof GetLocalTypeIns) { + int regId = ((GetLocalTypeIns) def).getRegisterId(action); + if (regId > 0 && localData.localRegs.get(regId) instanceof UndefinedAVM2Item) { + //System.err.println(""+); + stack.pop(); + stack.push(new UndefinedAVM2Item(action)); + } else { + break; + } + } + boolean ifed = false; if (def instanceof JumpIns) { //ActionJump jump = (ActionJump) action; @@ -339,9 +369,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { AVM2Instruction tarIns = code.code.get(nidx); if (EcmaScript.toBoolean(res)) { - /*if (nidx == -1) { - throw new TranslateException("If target not found: " + address); - }*/ + //System.err.println("replacing " + action + " on " + idx + " with jump"); AVM2Instruction jumpIns = new AVM2Instruction(0, new JumpIns(), new int[]{0}); //jumpIns.operands[0] = action.operands[0] /*- action.getBytes().length*/ + jumpIns.getBytes().length; code.replaceInstruction(idx, jumpIns, body); @@ -351,6 +379,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { idx = code.adr2pos(jumpIns.offset + jumpIns.getBytesLength() + jumpIns.operands[0]); } else { + //System.err.println("replacing " + action + " on " + idx + " with pop"); code.replaceInstruction(idx, new AVM2Instruction(action.offset, new DeobfuscatePopIns(), new int[]{}), body); //action.definition = new DeobfuscatePopIns(); idx++; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java index 552465a57..9e0e7c534 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java @@ -81,16 +81,12 @@ public class LocalRegAVM2Item extends AVM2Item { @Override public Object getResult() { - if (computedResult == null) { - return new Undefined(); - } - return computedResult; - + return computedValue.getResult(); } @Override public boolean isCompileTime(Set dependencies) { - return false; //isCT; + return computedValue instanceof UndefinedAVM2Item; } @Override