/* * Copyright (C) 2010-2016 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.BaseLocalData; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.AVM2LocalData; 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.InstructionDefinition; import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugIns; 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.localregs.KillIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSource; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateException; import com.jpexs.decompiler.graph.TranslateStack; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; /** * * AVM2 Deobfuscator removing single assigned local registers. * * Example: var a = true; var b = false; ... if(a){ ...ok }else{ not executed } * * @author JPEXS */ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld { private Set<Integer> getRegisters(AVM2Code code) { Set<Integer> regs = new HashSet<>(); for (AVM2Instruction ins : code.code) { InstructionDefinition def = ins.definition; if (def instanceof SetLocalTypeIns) { int regId = ((SetLocalTypeIns) def).getRegisterId(ins); regs.add(regId); } else if (def instanceof GetLocalTypeIns) { int regId = ((GetLocalTypeIns) def).getRegisterId(ins); regs.add(regId); } else { for (int p = 0; p < ins.definition.operands.length; p++) { int op = ins.definition.operands[p]; if (op == AVM2Code.DAT_LOCAL_REG_INDEX) { int regId = ins.operands[p]; regs.add(regId); } } } } return regs; } @Override public void avm2CodeRemoveTraps(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, Trait trait, int methodInfo, MethodBody body) throws InterruptedException { //System.err.println("regdeo:" + path); MethodBody originalBody = body; body.getCode().removeDeadCode(body); Set<Integer> ignoredRegs = new HashSet<>(); int localReservedCount = body.getLocalReservedCount(); for (int i = 0; i < localReservedCount; i++) { ignoredRegs.add(i); } int setReg = 0; List<Integer> listedRegs = new ArrayList<>(); List<MethodBody> listedLastBodies = new ArrayList<>(); Set<Integer> ignoredRegGets = new HashSet<>(); Reference<AVM2Instruction> assignmentRef = new Reference<>(null); while (setReg > -1) { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } MethodBody bodybefore = body; body = bodybefore.clone(); setReg = getFirstRegisterSetter(assignmentRef, classIndex, isStatic, scriptIndex, abc, body, ignoredRegs, ignoredRegGets); //System.err.println("setreg " + setReg + " ass:" + assignmentRef.getVal()); if (setReg < 0) { break; } // if there is second assignment if (listedRegs.contains(setReg)) { //System.err.println("second assignment of loc" + setReg + ", ignoring"); int lindex = listedRegs.indexOf(setReg); body = listedLastBodies.get(lindex); // switch to body before ignoredRegs.add(setReg); // this is not obfuscated for (int i = listedRegs.size() - 1; i >= lindex; i--) { int r = listedRegs.get(i); listedRegs.remove(i); listedLastBodies.remove(i); ignoredRegGets.remove(r); } continue; } AVM2Instruction assignment = assignmentRef.getVal(); InstructionDefinition def = assignment.definition; if ((def instanceof SetLocalTypeIns) || (def instanceof GetLocalTypeIns /*First usage -> value undefined*/)) { super.removeObfuscationIfs(classIndex, isStatic, scriptIndex, abc, body, Arrays.asList(assignment)); } if (def instanceof GetLocalTypeIns) { ignoredRegGets.add(setReg); } listedRegs.add(setReg); listedLastBodies.add(bodybefore); } body.getCode().removeDeadCode(body); originalBody.exceptions = body.exceptions; originalBody.setCode(body.getCode()); } private int getFirstRegisterSetter(Reference<AVM2Instruction> assignment, int classIndex, boolean isStatic, int scriptIndex, ABC abc, MethodBody body, Set<Integer> ignoredRegisters, Set<Integer> ignoredGets) throws InterruptedException { AVM2Code code = body.getCode(); if (code.code.isEmpty()) { return -1; } return visitCode(assignment, new HashSet<>(), new TranslateStack("deo"), classIndex, isStatic, body, scriptIndex, abc, code, 0, code.code.size() - 1, ignoredRegisters, ignoredGets); } private int visitCode(Reference<AVM2Instruction> assignment, Set<Integer> visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, Set<Integer> ignored, Set<Integer> ignoredGets) throws InterruptedException { List<GraphTargetItem> output = new ArrayList<>(); AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex); initLocalRegs(localData, body.getLocalReservedCount(), body.max_regs); localData.localRegs.put(0, new NullAVM2Item(null, null)); // this List<Integer> toVisit = new ArrayList<>(); toVisit.add(idx); List<TranslateStack> toVisitStacks = new ArrayList<>(); toVisitStacks.add(stack); outer: while (!toVisit.isEmpty()) { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } idx = toVisit.remove(0); stack = toVisitStacks.remove(0); while (true) { if (idx > endIdx) { break; } if (visited.contains(idx)) { break; } visited.add(idx); AVM2Instruction ins = code.code.get(idx); InstructionDefinition def = ins.definition; //System.err.println("" + idx + ": " + ins + " stack:" + stack.size()); // do not throw EmptyStackException, much faster int requiredStackSize = ins.getStackPopCount(localData); if (stack.size() < requiredStackSize) { continue outer; } ins.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); if (def instanceof SetLocalTypeIns) { int regId = ((SetLocalTypeIns) def).getRegisterId(ins); if (!ignored.contains(regId)) { assignment.setVal(ins); return regId; } } else if (def instanceof GetLocalTypeIns) { int regId = ((GetLocalTypeIns) def).getRegisterId(ins); if (!ignored.contains(regId) && !ignoredGets.contains(regId)) { assignment.setVal(ins); return regId; } } else if (!(def instanceof KillIns) && !(def instanceof DebugIns)) { //can be inclocal, declocal, hasnext... for (int p = 0; p < ins.definition.operands.length; p++) { int op = ins.definition.operands[p]; if (op == AVM2Code.DAT_LOCAL_REG_INDEX) { int regId = ins.operands[p]; if (!ignored.contains(regId)) { assignment.setVal(ins); return regId; } } } } idx++; if (ins.definition instanceof JumpIns) { long address = ins.getTargetAddress(); idx = code.adr2pos(address);//code.indexOf(code.getByAddress(address)); if (idx == -1) { throw new TranslateException("Jump target not found: " + address); } } if (ins.isBranch()) { List<Integer> branches = ins.getBranches(new GraphSource() { @Override public int size() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public GraphSourceItem get(int pos) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public boolean isEmpty() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<GraphTargetItem> translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public int adr2pos(long adr) { return code.adr2pos(adr); } @Override public long pos2adr(int pos) { return code.pos2adr(pos); } }); idx = branches.get(0); for (int n = 1; n < branches.size(); n++) { //visitCode(visited, (TranslateStack) stack.clone(), classIndex, isStatic, body, scriptIndex, abc, code, branches.get(n), endIdx, result); int nidx = branches.get(n); if (visited.contains(nidx)) { continue; } toVisit.add(nidx); toVisitStacks.add((TranslateStack) stack.clone()); } } /*if (ins.definition instanceof IfTypeIns) { long address = ins.offset + ins.getBytes().length + ins.operands[0]; int newIdx = code.adr2pos(address); if (newIdx == -1) { throw new TranslateException("If target not found: " + address); } visitCode(visited, (TranslateStack) stack.clone(), classIndex, isStatic, body, scriptIndex, abc, code, newIdx, endIdx, result); }*/ if (ins.definition instanceof ReturnValueIns) { break; } if (ins.definition instanceof ThrowIns) { break; } if (ins.definition instanceof ReturnVoidIns) { break; } } } return -1; } }