diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscator.java new file mode 100644 index 000000000..e057fb9d4 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscator.java @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2010-2015 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; + +import com.jpexs.decompiler.flash.action.deobfuscation.*; +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.AVM2LocalData; +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.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.ModuloIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitAndIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitOrIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitXorIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIns; +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.localregs.SetLocalIns; +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.stack.DupIns; +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.PushNullIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +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.PushUndefinedIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionList; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.action.ActionLocalData; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.ReturnActionItem; +import com.jpexs.decompiler.flash.action.special.ActionEnd; +import com.jpexs.decompiler.flash.action.swf4.ActionAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf4.ActionMultiply; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; +import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; +import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd; +import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitOr; +import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; +import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDecrement; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.action.swf5.ActionModulo; +import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; +import com.jpexs.decompiler.flash.action.swf5.ActionReturn; +import com.jpexs.decompiler.flash.action.swf6.ActionGreater; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateException; +import com.jpexs.decompiler.graph.TranslateStack; +import java.io.IOException; +import java.util.ArrayList; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * AVM2 Deobfuscator - FIXME!!! Not ready yet! + * + * @author JPEXS + */ +public class AVM2Deobfuscator extends AVM2DeobfuscatorSimple { + + private final int executionLimit = 30000; + + @Override + public void actionListParsed(ActionList actions, SWF swf) { + + } + + @Override + public void deobfuscate(int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException { + removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + removeObfuscationIfs(classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); + removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + removeZeroJumps(body.getCode(), body); + } + + private boolean removeObfuscationIfs(int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) { + AVM2Code code = body.getCode(); + if (code.code.size() == 0) { + return false; + } + System.err.println("============================================="); + for (int i = 0; i < code.code.size(); i++) { + ExecutionResult result = new ExecutionResult(); + System.err.println("Execute from " + i); + executeActions(classIndex, isStatic, body, scriptIndex, abc, code, i, code.code.size() - 1, result); + + if (result.idx != -1) { + int newIstructionCount = 1; // jump + if (!result.stack.isEmpty()) { + newIstructionCount++; + } + newIstructionCount += 2 * result.variables.size(); + + if (newIstructionCount * 2 < result.instructionsProcessed) { + AVM2Instruction target = code.code.get(result.idx); + AVM2Instruction prevAction = code.code.get(i); + + for (int variableName : result.variables.keySet()) { + Object value = result.variables.get(variableName); + /*ActionPush push = new ActionPush(variableName); + push.values.add(value);*/ + AVM2Instruction push = makePush(value, cpool); + + code.insertInstruction(i++, push); + push.offset = prevAction.offset; + + code.insertInstruction(i++, push); + prevAction = push; + + /*if (result.defines.contains(variableName)) { + //ActionDefineLocal defineLocal = new ActionDefineLocal(); + AVM2Instruction defineLocal = new AVM2Instruction(prevAction.offset, new SetLocalIns(), new int[]{}); + defineLocal.setAddress(prevAction.getAddress()); + code.addAction(i++, defineLocal); + prevAction = defineLocal; + } else { + ActionSetVariable setVariable = new ActionSetVariable(); + setVariable.setAddress(prevAction.getAddress()); + code.addAction(i++, setVariable); + prevAction = setVariable; + }*/ + AVM2Instruction setVariable = new AVM2Instruction(prevAction.offset, new SetLocalIns(), new int[]{}); + code.insertInstruction(i++, setVariable); + prevAction = setVariable; + } + + if (!result.stack.isEmpty()) { + //ActionPush push = new ActionPush(0); + //push.values.clear(); + long ofs = prevAction.offset; + for (GraphTargetItem graphTargetItem : result.stack) { + //DirectValueActionItem dv = (DirectValueActionItem) graphTargetItem; + //push.values.add(dv.value); + AVM2Instruction push = makePush(cpool, graphTargetItem); + push.offset = ofs; + code.insertInstruction(i++, push); + ofs += push.getBytes().length; + prevAction = push; + } + } + + //ctionJump jump = new ActionJump(0); + AVM2Instruction jump = new AVM2Instruction(prevAction.offset, new JumpIns(), new int[]{0}); + //jump.setAddress(prevAction.getAddress()); + jump.operands[0] = (int) (target.offset - jump.offset - jump.getBytes().length); + code.insertInstruction(i++, jump); + return true; + } + } + } + + return false; + } + + private AVM2LocalData newLocalData(int scriptIndex, ABC abc, AVM2ConstantPool cpool, MethodBody body, boolean isStatic, int classIndex) { + AVM2LocalData localData = new AVM2LocalData(); + localData.isStatic = isStatic; + localData.classIndex = classIndex; + localData.localRegs = new HashMap<>(); + localData.scopeStack = new ScopeStack(); + localData.constants = cpool; + localData.methodInfo = abc.method_info; + localData.methodBody = body; + localData.abc = abc; + localData.localRegNames = new HashMap<>(); + localData.fullyQualifiedNames = new ArrayList<>(); + localData.parsedExceptions = new ArrayList<>(); + localData.finallyJumps = new HashMap<>(); + localData.ignoredSwitches = new HashMap<>(); + localData.ignoredSwitches2 = new ArrayList<>(); + localData.scriptIndex = scriptIndex; + localData.localRegAssignmentIps = new HashMap<>(); + localData.ip = 0; + localData.refs = new HashMap<>(); + localData.code = body.getCode(); + return localData; + } + + private void executeActions(int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, ExecutionResult result) { + List output = new ArrayList<>(); + AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex); + localData.localRegs.put(0, new NullAVM2Item(null));//this + FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack(""); + int instructionsProcessed = 0; + + try { + while (true) { + if (idx > endIdx) { + break; + } + + AVM2Instruction action = code.code.get(idx); + instructionsProcessed++; + + if (instructionsProcessed > executionLimit) { + break; + } + + /*if (action instanceof ActionDefineLocal) { + GraphTargetItem top = stack.pop(); + String variableName = stack.peek().getResult().toString(); + result.defines.add(variableName); + stack.push(top); + }*/ + if (action.definition instanceof GetLocalTypeIns) { + int regId = ((GetLocalTypeIns) action.definition).getRegisterId(action);//stack.peek().getResult().toString(); + if (!localData.localRegs.containsKey(regId)) { + break; + } + } + + /*if (action instanceof ActionCallFunction) { + String functionName = stack.pop().getResult().toString(); + long numArgs = EcmaScript.toUint32(stack.pop().getResult()); + if (numArgs == 0) { + if (fakeFunctions != null && fakeFunctions.containsKey(functionName)) { + stack.push(new DirectValueActionItem(fakeFunctions.get(functionName))); + } else { + break; + } + } else { + break; + } + } else { + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + }*/ + System.err.println("Translating " + action); + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + Class allowedDefs[] = new Class[]{ + PushByteIns.class, + PushShortIns.class, + PushIntIns.class, + PushDoubleIns.class, + PushStringIns.class, + PushNullIns.class, + PushUndefinedIns.class, + PushFalseIns.class, + PushTrueIns.class, + DupIns.class, + SwapIns.class, + AddIns.class, + AddIIns.class, + SubtractIns.class, + SubtractIIns.class, + ModuloIns.class, + MultiplyIns.class, + BitAndIns.class, + BitXorIns.class, + BitOrIns.class, + LShiftIns.class, + RShiftIns.class, + URShiftIns.class, + EqualsIns.class, + NotIns.class, + IfTypeIns.class, + JumpIns.class, + IncrementIns.class, + IncrementIIns.class, + DecrementIns.class, + DecrementIIns.class, + SetLocalTypeIns.class, + GetLocalTypeIns.class, + GreaterEqualsIns.class, + GreaterThanIns.class, + LessThanIns.class, + LessEqualsIns.class, + StrictEqualsIns.class, + IfTypeIns.class, + ReturnVoidIns.class, + ReturnValueIns.class, + NewFunctionIns.class, + PopIns.class, + PushScopeIns.class + }; + + InstructionDefinition def = action.definition; + + boolean ok = false; + for (Class s : allowedDefs) { + if (s.isAssignableFrom(def.getClass())) { + ok = true; + break; + } + } + if (!ok) { + System.err.println("Broken"); + break; + } + + + /*for (String variable : localData.variables.keySet()) { + System.out.println(Helper.byteArrToString(variable.getBytes())); + }*/ + idx++; + + if (action.definition instanceof JumpIns) { + + long address = action.offset + action.getBytes().length + action.operands[0]; + idx = code.adr2pos(address);//code.indexOf(code.getByAddress(address)); + if (idx == -1) { + throw new TranslateException("Jump target not found: " + address); + } + } + + if (action.definition instanceof IfTypeIns) { + if (EcmaScript.toBoolean(stack.pop().getResult())) { + long address = action.offset + action.getBytes().length + action.operands[0]; + idx = code.adr2pos(address); + if (idx == -1) { + throw new TranslateException("If target not found: " + address); + } + } + } + + if (/*localData.variables.size() == 1 && */stack.allItemsFixed()) { + result.idx = idx == code.code.size() ? idx - 1 : idx; + result.instructionsProcessed = instructionsProcessed; + result.variables.clear(); + for (int variableName : localData.localRegs.keySet()) { + Object value = localData.localRegs.get(variableName).getResult(); + result.variables.put(variableName, value); + } + result.stack.clear(); + result.stack.addAll(stack); + } + + if (action.definition instanceof ReturnValueIns) { + if (output.size() > 0) { + ReturnValueAVM2Item ret = (ReturnValueAVM2Item) output.get(output.size() - 1); + result.resultValue = ret.value.getResult(); + } + break; + } + + if (action.definition instanceof ReturnVoidIns) { + break; + } + } + } catch (EmptyStackException | TranslateException | InterruptedException ex) { + //ex.printStackTrace(); + } + } + + /*private Map getFakeFunctionResults(ActionList actions) { + + Map results = new HashMap<>(); + + for (int i = 0; i < actions.size(); i++) { + Action action = actions.get(i); + if (action instanceof ActionDefineFunction) { + ActionDefineFunction def = (ActionDefineFunction) action; + if (def.paramNames.isEmpty()) { + ExecutionResult result = new ExecutionResult(); + List lastActions = actions.getContainerLastActions(action); + int lastActionIdx = actions.indexOf(lastActions.get(0)); + executeActions(actions, i + 1, lastActionIdx, null, result, null); + if (result.resultValue != null) { + results.put(def.functionName, result.resultValue); + for (int j = i; j <= lastActionIdx; j++) { + actions.removeAction(i); + } + } + } + } + } + + return results; + } + + @Override + public byte[] proxyFileCatched(byte[] data) { + return null; + } + + @Override + public void swfParsed(SWF swf) { + } + + @Override + public void abcParsed(ABC abc, SWF swf) { + } + + @Override + public void methodBodyParsed(MethodBody body, SWF swf) { + }*/ + class ExecutionResult { + + public int idx = -1; + + public int instructionsProcessed = -1; + + //public ActionConstantPool constantPool; + public Map variables = new HashMap<>(); + + //public Set defines = new HashSet<>(); + public TranslateStack stack = new TranslateStack("?"); + + public ScopeStack scopeStack = new ScopeStack(); + + public Object resultValue; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorSimple.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorSimple.java new file mode 100644 index 000000000..1e21be20b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2DeobfuscatorSimple.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2010-2015 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; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.AVM2LocalData; +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.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.ModuloIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitAndIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitOrIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitXorIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +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.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.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.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.action.ActionList; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerListener; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateException; +import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.decompiler.graph.model.FalseItem; +import com.jpexs.decompiler.graph.model.TrueItem; +import java.util.ArrayList; +import java.util.EmptyStackException; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { + + private final int executionLimit = 30000; + + @Override + public void actionListParsed(ActionList actions, SWF swf) { + + } + + protected AVM2Instruction makePush(Object ovalue, AVM2ConstantPool cpool) { + if (ovalue instanceof Long) { + long value = (Long) ovalue; + if (value >= -128 && value <= 127) { + return new AVM2Instruction(0, new PushByteIns(), new int[]{(int) (long) value}); + } else if (value >= -32768 && value <= 32767) { + return new AVM2Instruction(0, new PushShortIns(), new int[]{((int) (long) value) & 0xffff}); + } else { + return new AVM2Instruction(0, new PushIntIns(), new int[]{cpool.getIntId(value, true)}); + } + } + if (ovalue instanceof Double) { + return new AVM2Instruction(0, new PushDoubleIns(), new int[]{cpool.getDoubleId((Double) ovalue, true)}); + } + if (ovalue instanceof String) { + return new AVM2Instruction(0, new PushStringIns(), new int[]{cpool.getStringId((String) ovalue, true)}); + } + if (ovalue instanceof Boolean) { + if ((Boolean) ovalue) { + return new AVM2Instruction(0, new PushTrueIns(), new int[]{}); + } + return new AVM2Instruction(0, new PushFalseIns(), new int[]{}); + } + if (ovalue instanceof Null) { + return new AVM2Instruction(0, new PushNullIns(), new int[]{}); + } + if (ovalue instanceof Undefined) { + return new AVM2Instruction(0, new PushUndefinedIns(), new int[]{}); + } + return null; + } + + protected AVM2Instruction makePush(AVM2ConstantPool cpool, GraphTargetItem graphTargetItem) { + AVM2Instruction ins = null; + if (graphTargetItem instanceof IntegerValueAVM2Item) { + IntegerValueAVM2Item iv = (IntegerValueAVM2Item) graphTargetItem; + return makePush(iv.value, cpool); + } else if (graphTargetItem instanceof FloatValueAVM2Item) { + FloatValueAVM2Item fv = (FloatValueAVM2Item) graphTargetItem; + return makePush(fv.value, cpool); + } else if (graphTargetItem instanceof StringAVM2Item) { + StringAVM2Item fv = (StringAVM2Item) graphTargetItem; + return makePush(fv.value, cpool); + } else if (graphTargetItem instanceof TrueItem) { + return makePush(Boolean.TRUE, cpool); + } else if (graphTargetItem instanceof FalseItem) { + return makePush(Boolean.FALSE, cpool); + } else if (graphTargetItem instanceof NullAVM2Item) { + return makePush(new Null(), cpool); + } else if (graphTargetItem instanceof UndefinedAVM2Item) { + return makePush(new Undefined(), cpool); + } else { + return null; + } + } + + private boolean removeObfuscationIfs(int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException { + AVM2Code code = body.getCode(); + if (code.code.size() == 0) { + return false; + } + System.err.println("====================================================="); + + for (int i = 0; i < code.code.size(); i++) { + ExecutionResult result = new ExecutionResult(); + System.err.println("Execute from " + i); + executeActions(code, i, code.code.size() - 1, result); + + if (result.idx != -1) { + int newIstructionCount = 1; // jump + if (!result.stack.isEmpty()) { + newIstructionCount += result.stack.size(); + } + + if (newIstructionCount < result.instructionsProcessed) { + AVM2Instruction target = code.code.get(result.idx); + AVM2Instruction prevAction = code.code.get(i); + + if (result.stack.isEmpty() && prevAction.definition instanceof JumpIns) { + prevAction.operands[0] = ((int) (target.getOffset() - prevAction.getOffset() - prevAction.getBytes().length)); + } else { + if (!result.stack.isEmpty()) { + for (GraphTargetItem graphTargetItem : result.stack) { + AVM2Instruction ins = makePush(cpool, graphTargetItem); + if (ins != null) { + code.insertInstruction(i++, ins); + } + prevAction = ins; + //DirectValueActionItem dv = (DirectValueActionItem) graphTargetItem; + //push.values.add(dv.value); + } + //push.setAddress(prevAction.getAddress()); + + } + + AVM2Instruction jump = new AVM2Instruction(0, new JumpIns(), new int[]{0}); + jump.offset = prevAction.offset; + jump.operands[0] = ((int) (target.offset - jump.offset - jump.getBytes().length)); + code.insertInstruction(i++, jump); + } + + AVM2Instruction nextAction = code.code.size() > i ? code.code.get(i) : null; + + removeUnreachableActions(code, cpool, trait, minfo, body); + removeZeroJumps(code, body); + + if (nextAction != null) { + int nextIdx = code.code.indexOf(nextAction); + if (nextIdx < i) { + i = nextIdx; + } + } + } + } + } + + return false; + } + + protected void removeUnreachableActions(AVM2Code code, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException { + code.removeDeadCode(cpool, trait, minfo, body); + } + + protected boolean removeZeroJumps(AVM2Code actions, MethodBody body) { + 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; + } + } + return result; + } + + private void executeActions(AVM2Code code, int idx, int endIdx, ExecutionResult result) { + List output = new ArrayList<>(); + AVM2LocalData localData = new AVM2LocalData(); + + FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack(""); + int instructionsProcessed = 0; + + try { + while (true) { + if (idx > endIdx) { + break; + } + + if (instructionsProcessed > executionLimit) { + break; + } + + AVM2Instruction action = code.code.get(idx); + + /*System.out.print(action.getASMSource(actions, new ArrayList(), ScriptExportMode.PCODE)); + for (int j = 0; j < stack.size(); j++) { + System.out.print(" '" + stack.get(j).getResult() + "'"); + } + System.out.println();*/ + InstructionDefinition def = action.definition; + + Class allowedDefs[] = new Class[]{ + PushByteIns.class, + PushShortIns.class, + PushIntIns.class, + PushDoubleIns.class, + PushStringIns.class, + PushNullIns.class, + PushUndefinedIns.class, + PushFalseIns.class, + PushTrueIns.class, + DupIns.class, + SwapIns.class, + AddIns.class, + AddIIns.class, + SubtractIns.class, + SubtractIIns.class, + ModuloIns.class, + MultiplyIns.class, + BitAndIns.class, + BitXorIns.class, + BitOrIns.class, + LShiftIns.class, + RShiftIns.class, + URShiftIns.class, + EqualsIns.class, + NotIns.class, + IfTypeIns.class, + JumpIns.class + }; + + boolean ok = false; + for (Class s : allowedDefs) { + if (s.isAssignableFrom(def.getClass())) { + ok = true; + break; + } + } + if (!ok) { + break; + } + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + + idx++; + + if (def instanceof JumpIns) { + //ActionJump jump = (ActionJump) action; + long address = action.getOffset() + action.getBytes().length + action.operands[0]; + idx = code.adr2pos(address); + if (idx == -1) { + throw new TranslateException("Jump target not found: " + address); + } + } + + if (def instanceof IfTypeIns) { + //ActionIf aif = (ActionIf) action; + if (EcmaScript.toBoolean(stack.pop().getResult())) { + long address = action.offset + action.getBytes().length + action.operands[0]; + idx = code.adr2pos(address);//code.indexOf(code.getByAddress(address)); + if (idx == -1) { + throw new TranslateException("If target not found: " + address); + } + } + } + + instructionsProcessed++; + + if (stack.allItemsFixed()) { + result.idx = idx == code.code.size() ? idx - 1 : idx; + result.instructionsProcessed = instructionsProcessed; + result.stack.clear(); + result.stack.addAll(stack); + } + } + } catch (EmptyStackException | TranslateException | InterruptedException ex) { + } + } + + @Override + public byte[] proxyFileCatched(byte[] data) { + return null; + } + + @Override + public void swfParsed(SWF swf) { + } + + @Override + public void abcParsed(ABC abc, SWF swf) { + } + + @Override + public void methodBodyParsed(MethodBody body, SWF swf) { + + } + + public void deobfuscate(int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException { + removeUnreachableActions(body.getCode(), cpool, trait, minfo, body); + removeObfuscationIfs(classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body); + removeZeroJumps(body.getCode(), body); + } + + class ExecutionResult { + + public int idx = -1; + + public int instructionsProcessed = -1; + + public TranslateStack stack = new TranslateStack("?"); + + public Object resultValue; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/FixItemCounterTranslateStack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/FixItemCounterTranslateStack.java new file mode 100644 index 000000000..05f02f399 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/FixItemCounterTranslateStack.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010-2015 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; + +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateStack; + +/** + * + * @author JPEXS + */ +public class FixItemCounterTranslateStack extends TranslateStack { + + private int fixItemCount = Integer.MAX_VALUE; + + public FixItemCounterTranslateStack(String path) { + super(null); //null path => do not add PushItems + } + + @Override + public GraphTargetItem pop() { + GraphTargetItem result = super.pop(); + int itemCount = size(); + if (itemCount < fixItemCount) { + fixItemCount = itemCount; + } + return result; + } + + @Override + public synchronized GraphTargetItem remove(int index) { + if (index < fixItemCount) { + fixItemCount = index; + } + return super.remove(index); + } + + public boolean allItemsFixed() { + return size() <= fixItemCount; + } + + public int getFixItemCount() { + return fixItemCount; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java index 85895b8e5..57158d6a3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java @@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.NotCompileTimeItem; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.decompiler.graph.TranslateStack; import java.util.HashMap; @@ -116,7 +117,7 @@ public abstract class SetLocalTypeIns extends InstructionDefinition implements S } //if(val.startsWith("catchscope ")) return; - //if(val.startsWith("newactivation()")) return; + //if(val.startsWith("newactivation()")) return; output.add(new SetLocalAVM2Item(ins, regId, value)); } 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 47817f2b5..303150337 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 @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ABCInputStream; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscator; import com.jpexs.decompiler.flash.abc.avm2.CodeStats; import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; import com.jpexs.decompiler.flash.abc.types.traits.Trait; @@ -279,13 +280,9 @@ public final class MethodBody implements Cloneable { deobfuscated.markMappedOffsets(); //deobfuscated.inlineJumpExit(); if (Configuration.autoDeobfuscate.get()) { - try { - deobfuscated.removeTraps(constants, trait, method_info.get(this.method_info), b, abc, scriptIndex, classIndex, isStatic, path); - } catch (StackOverflowError ex) { - logger.log(Level.SEVERE, "Error during remove traps in " + path, ex); - } + AVM2Deobfuscator deo = new AVM2Deobfuscator(); + deo.deobfuscate(classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b); } - //deobfuscated.restoreControlFlow(constants, b); return b; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/ScopeStack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/ScopeStack.java index af1152907..bb5478932 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/ScopeStack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/ScopeStack.java @@ -22,5 +22,9 @@ import java.util.Stack; * * @author JPEXS */ -public class ScopeStack extends Stack { +public class ScopeStack extends TranslateStack { + + public ScopeStack() { + super("scope"); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java index cc68f15ea..72ca11740 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java @@ -39,6 +39,17 @@ public class TranslateStack extends Stack { return path; } + @Override + public synchronized GraphTargetItem get(int index) { + if (path != null) { + if (index >= this.size() || index < 0) { + Logger.getLogger(TranslateStack.class.getName()).log(Level.FINE, "{0}: Attemp to Get item outside of bounds of stack", path); + return pop; + } + } + return super.get(index); + } + @Override public synchronized GraphTargetItem peek() { if (path != null) {