New AS3 Deobfuscation method similar to that for AS1/2

This commit is contained in:
Jindra Petřík
2015-06-20 20:27:39 +02:00
parent 176f8b6de7
commit 84398eaccf
22 changed files with 1890 additions and 1705 deletions
@@ -1150,7 +1150,7 @@ public class AVM2Code implements Cloneable {
}
int ret = posCache.indexOf(address);
if (ret == -1) {
throw new ConvertException("Bad jump try conver ofs" + Helper.formatAddress(address) + " ", -1);
throw new ConvertException("Invalid jump to ofs" + Helper.formatAddress(address), -1);
}
return ret;
}
@@ -1843,7 +1843,20 @@ public class AVM2Code implements Cloneable {
invalidateCache();
}
/**
* @param pos
* @param instruction
*/
public void insertInstruction(int pos, AVM2Instruction instruction) {
insertInstruction(pos, instruction, true, false);
}
public void replaceInstruction(int idx, AVM2Instruction ins, MethodBody body) {
insertInstruction(idx, ins, true, true);
removeInstruction(idx + 1, body);
}
public void insertInstruction(int pos, AVM2Instruction instruction, boolean preRefsToThis, boolean postRefsToThis) {
if (pos < 0) {
pos = 0;
}
@@ -1857,31 +1870,43 @@ public class AVM2Code implements Cloneable {
instruction.offset = code.get(pos).offset;
}
for (int i = 0; i < pos; i++) {
for (int j = 0; j < code.get(i).definition.operands.length; j++) {
if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) {
long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j];
if (target >= instruction.offset) {
code.get(i).operands[j] += byteCount;
{
for (int i = 0; i < pos; i++) {
for (int j = 0; j < code.get(i).definition.operands.length; j++) {
if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) {
long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j];
if (target > instruction.offset) {
code.get(i).operands[j] += byteCount;
}
if (target == instruction.offset && !preRefsToThis) {
code.get(i).operands[j] += byteCount;
}
}
}
}
}
for (int i = pos; i < code.size(); i++) {
for (int j = 0; j < code.get(i).definition.operands.length; j++) {
if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) {
long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j];
if (target < instruction.offset) {
code.get(i).operands[j] -= byteCount;
{
for (int i = pos; i < code.size(); i++) {
for (int j = 0; j < code.get(i).definition.operands.length; j++) {
if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) {
long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j];
if (target < instruction.offset) {
code.get(i).operands[j] -= byteCount;
}
if (target == instruction.offset && postRefsToThis) {
code.get(i).operands[j] -= byteCount;
}
}
}
}
}
for (int i = pos + 1; i < code.size(); i++) {
for (int i = pos; i < code.size(); i++) {
code.get(i).offset += byteCount;
}
code.add(pos, instruction);
invalidateCache();
}
@SuppressWarnings("unchecked")
@@ -1,435 +0,0 @@
/*
* 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.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.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.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.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* 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<GraphTargetItem> 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<String, Object> getFakeFunctionResults(ActionList actions) {
Map<String, Object> 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<Action> 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<Integer, Object> variables = new HashMap<>();
//public Set<String> defines = new HashSet<>();
public TranslateStack stack = new TranslateStack("?");
public ScopeStack scopeStack = new ScopeStack();
public Object resultValue;
}
}
@@ -0,0 +1,250 @@
/*
* 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.BaseLocalData;
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.graph.AVM2GraphSource;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns;
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.jumps.JumpIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns;
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.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.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.EmptyStackException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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 AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
//private final int executionLimit = 30000;
@Override
public void actionListParsed(ActionList actions, SWF swf) {
}
@Override
public void deobfuscate(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException {
body.getCode().markMappedOffsets();
removeUnreachableActions(body.getCode(), cpool, trait, minfo, body);
Map<Integer, GraphTargetItem> singleRegisters = getSingleUseRegisters(classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body);
replaceSingleUseRegisters(singleRegisters, 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);
}
private void replaceSingleUseRegisters(Map<Integer, GraphTargetItem> singleRegisters, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) {
AVM2Code code = body.getCode();
for (int i = 0; i < code.code.size(); i++) {
AVM2Instruction ins = code.code.get(i);
if (ins.definition instanceof SetLocalTypeIns) {
SetLocalTypeIns slt = (SetLocalTypeIns) ins.definition;
int regId = slt.getRegisterId(ins);
if (singleRegisters.containsKey(regId)) {
code.replaceInstruction(i, new AVM2Instruction(ins.offset, new DeobfuscatePopIns(), new int[]{}), body);
}
}
if (ins.definition instanceof GetLocalTypeIns) {
GetLocalTypeIns glt = (GetLocalTypeIns) ins.definition;
int regId = glt.getRegisterId(ins);
if (singleRegisters.containsKey(regId)) {
code.replaceInstruction(i, makePush(singleRegisters.get(regId).getResult(), cpool), body);
}
}
}
}
private Map<Integer, GraphTargetItem> getSingleUseRegisters(int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) {
AVM2Code code = body.getCode();
Map<Integer, GraphTargetItem> ret = new HashMap<>();
if (code.code.isEmpty()) {
return ret;
}
ExecutionResult res = new ExecutionResult();
visitCode(new HashSet<>(), new TranslateStack("deo"), classIndex, isStatic, body, scriptIndex, abc, code, 0, code.code.size() - 1, res);
for (int reg : res.assignCount.keySet()) {
if (res.assignCount.get(reg) == 1) {
ret.put(reg, res.lastAssigned.get(reg));
}
}
return ret;
}
private void visitCode(Set<Integer> visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, ExecutionResult result) {
List<GraphTargetItem> output = new ArrayList<>();
AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex);
localData.localRegs.put(0, new NullAVM2Item(null));//this
int instructionsProcessed = 0;
try {
while (true) {
if (idx > endIdx) {
break;
}
if (visited.contains(idx)) {
break;
}
visited.add(idx);
AVM2Instruction action = code.code.get(idx);
instructionsProcessed++;
/*if (action.definition instanceof GetLocalTypeIns) {
int regId = ((GetLocalTypeIns) action.definition).getRegisterId(action);//stack.peek().getResult().toString();
if (!localData.localRegs.containsKey(regId)) {
break;
}
}*/
action.translate(localData, stack, output, Graph.SOP_USE_STATIC, "");
InstructionDefinition def = action.definition;
if (def instanceof SetLocalTypeIns) {
SetLocalTypeIns slt = (SetLocalTypeIns) def;
int regId = slt.getRegisterId(action);
if (!result.assignCount.containsKey(regId)) {
result.assignCount.put(regId, 0);
}
result.assignCount.put(regId, result.assignCount.get(regId) + 1);
GraphTargetItem regVal = localData.localRegs.get(regId);
if (regVal == null || !regVal.getNotCoerced().isCompileTime()) {
result.assignCount.put(regId, Integer.MAX_VALUE);
} else {
result.lastAssigned.put(regId, regVal.getNotCoerced());
}
//assignCount
}
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.isBranch()) {
List<Integer> branches = action.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);
}
}
/*if (action.definition instanceof IfTypeIns) {
long address = action.offset + action.getBytes().length + action.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 (action.definition instanceof ReturnValueIns) {
break;
}
if (action.definition instanceof ThrowIns) {
break;
}
if (action.definition instanceof ReturnVoidIns) {
break;
}
}
} catch (EmptyStackException | TranslateException | InterruptedException ex) {
ex.printStackTrace();
}
}
class ExecutionResult {
public Map<Integer, Integer> assignCount = new HashMap<>();
public Map<Integer, GraphTargetItem> lastAssigned = new HashMap<>();
}
}
@@ -20,6 +20,7 @@ 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.DeobfuscatePopIns;
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;
@@ -38,6 +39,7 @@ 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.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;
@@ -48,6 +50,7 @@ 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.instructions.types.CoerceOrConvertTypeIns;
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;
@@ -63,12 +66,15 @@ 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.ScopeStack;
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.PopItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
/**
@@ -145,12 +151,10 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
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);
executeActions(classIndex, isStatic, body, scriptIndex, abc, code, i, code.code.size() - 1, result);
if (result.idx != -1) {
int newIstructionCount = 1; // jump
@@ -158,44 +162,59 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
newIstructionCount += result.stack.size();
}
if (newIstructionCount < result.instructionsProcessed) {
if (newIstructionCount < result.instructionsProcessed) //if (result.isIf)
{
AVM2Instruction target = code.code.get(result.idx);
AVM2Instruction prevAction = code.code.get(i);
int idelta = 0;
if (result.stack.isEmpty() && prevAction.definition instanceof JumpIns) {
prevAction.operands[0] = ((int) (target.getOffset() - prevAction.getOffset() - prevAction.getBytes().length));
prevAction.operands[0] = ((int) (target.offset - prevAction.offset - 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);
if (graphTargetItem instanceof PopItem) {
continue;
}
AVM2Instruction ins = makePush(graphTargetItem.getResult(), cpool);
if (ins != null) {
code.insertInstruction(i + (idelta++), ins);
//prevAction = ins;
} else {
throw new TranslateException("Cannot push: " + graphTargetItem);
}
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);
}
code.insertInstruction(i + (idelta++), jump);
AVM2Instruction nextAction = code.code.size() > i ? code.code.get(i) : null;
jump.operands[0] = ((int) (target.offset - jump.offset - jump.getBytes().length));
}
removeUnreachableActions(code, cpool, trait, minfo, body);
removeZeroJumps(code, body);
if (nextAction != null) {
int nextIdx = code.code.indexOf(nextAction);
if (nextIdx < i) {
i = nextIdx;
}
}
i = -1;
/*if (nextAction != null) {
long mapped = nextAction.mappedOffset;
int nextIdx = -1;
for (int p = 0; p < code.code.size(); p++) {
if (code.code.get(p).mappedOffset == mapped) {
nextIdx = p;
break;
}
}
if (nextIdx == -1) {
//?
break;
} else {
i = nextIdx - 1;
}
}*/
}
}
}
@@ -220,9 +239,33 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
return result;
}
private void executeActions(AVM2Code code, int idx, int endIdx, ExecutionResult result) {
List<GraphTargetItem> output = new ArrayList<>();
protected 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(true);
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<GraphTargetItem> output = new ArrayList<>();
AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex);
FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack("");
int instructionsProcessed = 0;
@@ -238,12 +281,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
}
AVM2Instruction action = code.code.get(idx);
/*System.out.print(action.getASMSource(actions, new ArrayList<Long>(), ScriptExportMode.PCODE));
for (int j = 0; j < stack.size(); j++) {
System.out.print(" '" + stack.get(j).getResult() + "'");
}
System.out.println();*/
action.translate(localData, stack, output, Graph.SOP_USE_STATIC, "");
InstructionDefinition def = action.definition;
Class allowedDefs[] = new Class[]{
@@ -286,28 +324,34 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
if (!ok) {
break;
}
action.translate(localData, stack, output, Graph.SOP_USE_STATIC, "");
idx++;
//boolean ifed = false;
if (def instanceof JumpIns) {
//ActionJump jump = (ActionJump) action;
long address = action.getOffset() + action.getBytes().length + action.operands[0];
long address = action.offset + 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) {
} else if (def instanceof IfTypeIns) {
//ActionIf aif = (ActionIf) action;
if (EcmaScript.toBoolean(stack.pop().getResult())) {
GraphTargetItem top = stack.pop();
Object res = top.getResult();
if (EcmaScript.toBoolean(res)) {
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);
}
//ifed = true;
} else {
//action.definition = new DeobfuscatePopIns();
code.replaceInstruction(idx, new AVM2Instruction(action.offset, new DeobfuscatePopIns(), new int[]{}), body);
idx++;
}
} else {
idx++;
}
instructionsProcessed++;
@@ -318,8 +362,12 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
result.stack.clear();
result.stack.addAll(stack);
}
}
} catch (EmptyStackException | TranslateException | InterruptedException ex) {
//result.idx = -1;
//result.isIf = false;
//ignore
}
}
@@ -341,7 +389,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
}
public void deobfuscate(int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException {
public void deobfuscate(String path, 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);
@@ -355,6 +403,5 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
public TranslateStack stack = new TranslateStack("?");
public Object resultValue;
}
}
@@ -39,6 +39,7 @@ 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 com.jpexs.decompiler.graph.model.PopItem;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
@@ -58,7 +59,9 @@ public abstract class SetLocalTypeIns extends InstructionDefinition implements S
} else {
localRegs.put(regId, value);
}*/
localRegs.put(regId, value);
if (!(value instanceof PopItem)) {
localRegs.put(regId, value);
}
if (!regAssignCount.containsKey(regId)) {
regAssignCount.put(regId, 0);
}
@@ -90,7 +90,7 @@ public class LocalRegAVM2Item extends AVM2Item {
@Override
public boolean isCompileTime(Set<GraphTargetItem> dependencies) {
return isCT;
return false; //isCT;
}
@Override
@@ -27,7 +27,7 @@ import java.util.Stack;
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.6.0
* from the specification file <tt>C:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/actionscript3_script.flex</tt>
* from the specification file <tt>D:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/actionscript3_script.flex</tt>
*/
public final class ActionScriptLexer {
@@ -20,7 +20,8 @@ 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.AVM2DeobfuscatorRegisters;
import com.jpexs.decompiler.flash.abc.avm2.AVM2DeobfuscatorSimple;
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;
@@ -202,6 +203,7 @@ public final class MethodBody implements Cloneable {
if (exportMode != ScriptExportMode.AS) {
getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer);
} else {
//if (!path.contains("@")) {
if (!Configuration.decompile.get()) {
writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine();
return;
@@ -245,6 +247,7 @@ public final class MethodBody implements Cloneable {
if (exportMode != ScriptExportMode.AS) {
getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer);
} else {
//if (!path.contains("@")) {
if (!Configuration.decompile.get()) {
//writer.startMethod(this.method_info);
writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine();
@@ -276,12 +279,19 @@ public final class MethodBody implements Cloneable {
public MethodBody convertMethodBody(String path, boolean isStatic, int scriptIndex, int classIndex, ABC abc, Trait trait, AVM2ConstantPool constants, List<MethodInfo> method_info, ScopeStack scopeStack, boolean isStaticInitializer, List<String> fullyQualifiedNames, Traits initTraits) throws InterruptedException {
MethodBody b = clone();
AVM2Code deobfuscated = b.getCode();
deobfuscated.markMappedOffsets();
//deobfuscated.inlineJumpExit();
b.getCode().markMappedOffsets();
if (Configuration.autoDeobfuscate.get()) {
AVM2Deobfuscator deo = new AVM2Deobfuscator();
deo.deobfuscate(classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b);
if (Configuration.deobfuscationMode.get() == 0) {
try {
b.getCode().removeTraps(constants, trait, method_info.get(this.method_info), b, abc, scriptIndex, classIndex, isStatic, path);
} catch (Throwable ex) {
logger.log(Level.SEVERE, "Error during old deobfuscation: " + path, ex);
}
} else {
new AVM2DeobfuscatorSimple().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b);
new AVM2DeobfuscatorRegisters().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, method_info.get(this.method_info), b);
}
}
return b;
@@ -16,15 +16,17 @@
*/
package com.jpexs.decompiler.graph;
import java.util.Stack;
/**
*
* @author JPEXS
*/
public class ScopeStack extends TranslateStack {
public ScopeStack(boolean allowEmpty) {
super(allowEmpty ? "scope" : null);
}
public ScopeStack() {
super("scope");
this(true);
}
}
@@ -65,4 +65,14 @@ public class FalseItem extends GraphTargetItem implements LogicalOpItem, SimpleV
return true;
}
@Override
public boolean isCompileTime() {
return true;
}
@Override
public Object getResult() {
return Boolean.FALSE;
}
}
@@ -65,4 +65,15 @@ public class TrueItem extends GraphTargetItem implements LogicalOpItem, SimpleVa
public boolean isSimpleValue() {
return true;
}
@Override
public boolean isCompileTime() {
return true;
}
@Override
public Object getResult() {
return Boolean.TRUE;
}
}