AS3 deobfuscation - remove traps

This commit is contained in:
Jindra Petk
2013-01-27 19:19:43 +01:00
parent da00857837
commit c40a72e402
11 changed files with 463 additions and 167 deletions

View File

@@ -70,10 +70,18 @@ public class ABC {
}
}
public int removeTraps() {
int rem=0;
for(MethodBody body:bodies){
rem+=body.removeTraps(constants);
}
return rem;
}
public int removeDeadCode() {
int rem=0;
for(MethodBody body:bodies){
rem+=body.removeDeadCode();
rem+=body.removeDeadCode(constants);
}
return rem;
}
@@ -579,9 +587,9 @@ public class ABC {
}
public static final String[] reservedWords = {
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else",
"extends", "false", "finally", "for", "function", "if", "implements", "import", "in", "instanceof",
"interface", "internal", "is", "native", "new", "null", "package", "private", "protected", "public",
"return", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
"extends", "false", "finally", "for", "function", "get","if", "implements", "import", "in", "instanceof",
"interface", "internal", "is", "native", "new", "null","override", "package", "private", "protected", "public",
"return", "set","super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
"with", "dynamic", "default", "final", "in"};
public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
public static final String validNextCharacters = validFirstCharacters + "0123456789";

View File

@@ -36,10 +36,13 @@ import com.jpexs.asdec.abc.avm2.instructions.other.*;
import com.jpexs.asdec.abc.avm2.instructions.stack.*;
import com.jpexs.asdec.abc.avm2.instructions.types.*;
import com.jpexs.asdec.abc.avm2.instructions.xml.*;
import com.jpexs.asdec.abc.avm2.parser.ASM3Parser;
import com.jpexs.asdec.abc.avm2.parser.ParseException;
import com.jpexs.asdec.abc.avm2.treemodel.*;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.*;
import com.jpexs.asdec.abc.avm2.treemodel.operations.AndTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.operations.OrTreeItem;
import com.jpexs.asdec.abc.gui.DialogMissingSymbolHandler;
import com.jpexs.asdec.abc.types.ABCException;
import com.jpexs.asdec.abc.types.MethodBody;
import com.jpexs.asdec.abc.types.MethodInfo;
@@ -729,8 +732,24 @@ public class AVM2Code {
ret += "exceptiontarget " + e + ":";
}
}
ret += Highlighting.hilighOffset("", ofs) + ins.toStringNoAddress(constants, new ArrayList<String>()) + "\n";
if (ins.replaceWith != null) {
for (AVM2Instruction ins2 : ins.replaceWith) {
if (ins2.isIgnored()) {
continue;
}
if (ins2.definition instanceof JumpIns) {
ret += "jump ofs" + Helper.formatAddress(pos2adr(ins2.operands[0])) + "\n";
} else {
ret += Highlighting.hilighOffset("", ofs) + ins2.toStringNoAddress(constants, new ArrayList<String>()) + "\n";
}
}
} else {
if (!ins.isIgnored()) {
ret += Highlighting.hilighOffset("", ofs) + ins.toStringNoAddress(constants, new ArrayList<String>()) + "\n";
}
}
ofs += ins.getBytes().length;
}
return ret;
@@ -767,6 +786,10 @@ public class AVM2Code {
return posCache.get(pos).intValue();
}
public void invalidateCache() {
cacheActual = false;
}
private static String listToString(List<TreeItem> stack, ConstantPool constants, HashMap<Integer, String> localRegNames, List<String> fullyQualifiedNames) {
String ret = "";
for (int d = 0; d < stack.size(); d++) {
@@ -978,11 +1001,14 @@ public class AVM2Code {
return pos2adr(fixIPAfterDebugLine(adr2pos(addr)));
}
private ConvertOutput toSource(boolean isStatic, int classIndex, java.util.HashMap<Integer, TreeItem> localRegs, Stack<TreeItem> stack, Stack<TreeItem> scopeStack, ABC abc, ConstantPool constants, MethodInfo method_info[], MethodBody body, int start, int end, HashMap<Integer, String> localRegNames, List<String> fullyQualifiedNames) throws ConvertException {
private ConvertOutput toSource(boolean isStatic, int classIndex, java.util.HashMap<Integer, TreeItem> localRegs, Stack<TreeItem> stack, Stack<TreeItem> scopeStack, ABC abc, ConstantPool constants, MethodInfo method_info[], MethodBody body, int start, int end, HashMap<Integer, String> localRegNames, List<String> fullyQualifiedNames, boolean visited[]) throws ConvertException {
boolean debugMode = DEBUG_MODE;
if (debugMode) {
System.out.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString());
}
if (visited == null) {
visited = new boolean[code.size()];
}
//if(true) return "";
toSourceCount++;
if (toSourceLimit > 0) {
@@ -1061,7 +1087,7 @@ public class AVM2Code {
if (swins.operands.length >= 3) {
if (swins.operands[0] == swins.getBytes().length) {
if (adr2pos(pos2adr(f) + swins.operands[2]) < finStart) {
finallyCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, finStart, f - 1, localRegNames, fullyQualifiedNames).output;
finallyCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, finStart, f - 1, localRegNames, fullyQualifiedNames, visited).output;
returnPos = f + 1;
break;
}
@@ -1086,10 +1112,10 @@ public class AVM2Code {
}
Stack<TreeItem> substack = new Stack<TreeItem>();
substack.add(new ExceptionTreeItem(catchedExceptions.get(e)));
catchedCommands.add(toSource(isStatic, classIndex, localRegs, substack, new Stack<TreeItem>(), abc, constants, method_info, body, adr2pos(fixAddrAfterDebugLine(catchedExceptions.get(e).target)), eendpos, localRegNames, fullyQualifiedNames).output);
catchedCommands.add(toSource(isStatic, classIndex, localRegs, substack, new Stack<TreeItem>(), abc, constants, method_info, body, adr2pos(fixAddrAfterDebugLine(catchedExceptions.get(e).target)), eendpos, localRegNames, fullyQualifiedNames, visited).output);
}
List<TreeItem> tryCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip, endpos - 1, localRegNames, fullyQualifiedNames).output;
List<TreeItem> tryCommands = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip, endpos - 1, localRegNames, fullyQualifiedNames, visited).output;
output.add(new TryTreeItem(tryCommands, catchedExceptions, catchedCommands, finallyCommands));
@@ -1107,6 +1133,11 @@ public class AVM2Code {
unknownJumps.remove(new Integer(ip));
throw new UnknownJumpException(stack, ip, output);
}
if (visited[ip]) {
Logger.getLogger(AVM2Code.class.getName()).warning("Code already visited");
break;
}
visited[ip] = true;
AVM2Instruction ins = code.get(ip);
if ((ip + 8 < code.size())) { //return in finally clause
@@ -1152,13 +1183,12 @@ public class AVM2Code {
}
if (ins.definition instanceof JumpIns) { //Ifs with multiple conditions
int secondAddr = addr + ins.getBytes().length;
int jumpAddr = secondAddr + ins.operands[0];
int jumpPos = adr2pos(jumpAddr);
if (ins.operands[0] == 0) {
ip++;
} else if (ins.operands[0] > 0) {
int secondAddr = addr + ins.getBytes().length;
int jumpAddr = secondAddr + ins.operands[0];
int jumpPos = adr2pos(jumpAddr);//
if (finallyJumps.contains(jumpPos)) {
if (code.get(ip + 1).definition instanceof LabelIns) {
if (code.get(ip + 2).definition instanceof PopIns) {
@@ -1254,7 +1284,7 @@ public class AVM2Code {
throw new ConvertException("Unknown pattern: no setlocal before lookupswitch", switchPos);
}
loopList.add(new Loop(ip, switchPos + 1));
Stack<TreeItem> substack = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, jumpPos, evalTo - 1, localRegNames, fullyQualifiedNames).stack;
Stack<TreeItem> substack = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, jumpPos, evalTo - 1, localRegNames, fullyQualifiedNames, visited).stack;
TreeItem switchedValue = substack.pop();
//output.add("loop" + (switchPos + 1) + ":");
int switchBreak = switchPos + 1;
@@ -1289,7 +1319,7 @@ public class AVM2Code {
if (evalTo > -1) {
substack = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, curPos, evalTo - 1, localRegNames, fullyQualifiedNames).stack;
substack = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, curPos, evalTo - 1, localRegNames, fullyQualifiedNames, visited).stack;
casesList.add(substack.pop());
}
int substart = adr2pos(code.get(switchPos).operands[2 + casePos] + pos2adr(switchPos));
@@ -1301,7 +1331,7 @@ public class AVM2Code {
if (evalTo == -1) {
subend--;
}
List commands = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, substart, subend, localRegNames, fullyQualifiedNames).output;
List commands = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, substart, subend, localRegNames, fullyQualifiedNames, visited).output;
if ((evalTo == -1) && (casePos + 1 < code.get(switchPos).operands.length - 2)) {
if (commands.size() == 1) {
commands.remove(0);
@@ -1345,7 +1375,7 @@ public class AVM2Code {
loopList.add(currentLoop);
ConvertOutput co = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, jumpPos, adr2pos(afterBackJumpAddr) - 2, localRegNames, fullyQualifiedNames);
ConvertOutput co = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, jumpPos, adr2pos(afterBackJumpAddr) - 2, localRegNames, fullyQualifiedNames, visited);
Stack<TreeItem> substack = co.stack;
backJumpIns.definition.translate(isStatic, classIndex, localRegs, substack, scopeStack, constants, backJumpIns, method_info, output, body, abc, localRegNames, fullyQualifiedNames);
@@ -1354,7 +1384,7 @@ public class AVM2Code {
boolean isFor = false;
List<TreeItem> finalExpression = new ArrayList<TreeItem>();
try {
subins = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, adr2pos(secondAddr) + 1/*label*/, jumpPos - 1, localRegNames, fullyQualifiedNames).output;
subins = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, adr2pos(secondAddr) + 1/*label*/, jumpPos - 1, localRegNames, fullyQualifiedNames, visited).output;
} catch (UnknownJumpException uje) {
if ((uje.ip >= start) && (uje.ip <= end)) {
currentLoop.loopContinue = uje.ip;
@@ -1379,7 +1409,7 @@ public class AVM2Code {
}
}
}
finalExpression = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, uje.ip, jumpPos - 1, localRegNames, fullyQualifiedNames).output;
finalExpression = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, uje.ip, jumpPos - 1, localRegNames, fullyQualifiedNames, visited).output;
isFor = true;
} else {
throw new ConvertException("Unknown pattern: jump to nowhere", ip);
@@ -1533,7 +1563,7 @@ public class AVM2Code {
if (((GetLocalTypeIns) code.get(t).definition).getRegisterId(code.get(t)) == reg) {
if (code.get(t + 1).definition instanceof KillIns) {
if (code.get(t + 1).operands[0] == reg) {
ConvertOutput assignment = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip + 2, t - 1, localRegNames, fullyQualifiedNames);
ConvertOutput assignment = toSource(isStatic, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip + 2, t - 1, localRegNames, fullyQualifiedNames, visited);
stack.push(assignment.output.remove(assignment.output.size() - 1));
ip = t + 2;
continue iploop;
@@ -1568,9 +1598,9 @@ public class AVM2Code {
addr = addr + ins.getBytes().length + insAfter.getBytes().length + insAfter.operands[0];
nextPos = adr2pos(addr) - 1;
if (isAnd) {
stack.add(new AndTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames).stack.pop()));
stack.add(new AndTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames, visited).stack.pop()));
} else {
stack.add(new OrTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames).stack.pop()));
stack.add(new OrTreeItem(insAfter, stack.pop(), toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, ip + 3, nextPos, localRegNames, fullyQualifiedNames, visited).stack.pop()));
}
ins = code.get(nextPos + 1);
ip = nextPos + 1;
@@ -1627,13 +1657,13 @@ public class AVM2Code {
}
}
}
ConvertOutput onTrue = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, ip + 1, targetIns - 1 - ((hasElse || hasReturn) ? 1 : 0), localRegNames, fullyQualifiedNames);
ConvertOutput onTrue = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, ip + 1, targetIns - 1 - ((hasElse || hasReturn) ? 1 : 0), localRegNames, fullyQualifiedNames, visited);
ip = targetIns;
ConvertOutput onFalse = new ConvertOutput(new Stack<TreeItem>(), new ArrayList<TreeItem>());
if (hasElse) {
int finalAddr = targetAddr + code.get(targetIns - 1).operands[0];
int finalIns = adr2pos(finalAddr);
onFalse = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, targetIns, finalIns - 1, localRegNames, fullyQualifiedNames);
onFalse = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, targetIns, finalIns - 1, localRegNames, fullyQualifiedNames, visited);
ip = finalIns;
}
if ((onTrue.stack.size() > 0) && (onFalse != null) && (onFalse.stack.size() > 0)) {
@@ -1730,7 +1760,7 @@ public class AVM2Code {
ignoredIns = new ArrayList<Integer>();
HashMap<Integer, TreeItem> localRegs = new HashMap<Integer, TreeItem>();
try {
return toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), new Stack<TreeItem>(), abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames).output;
return toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), new Stack<TreeItem>(), abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames, null).output;
} catch (ConvertException ex) {
return new ArrayList<TreeItem>();
}
@@ -1819,7 +1849,7 @@ public class AVM2Code {
}
try {
list = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames).output;
list = toSource(isStatic, classIndex, localRegs, new Stack<TreeItem>(), scopeStack, abc, constants, method_info, body, 0, code.size() - 1, localRegNames, fullyQualifiedNames, null).output;
if (initTraits != null) {
for (int i = 0; i < list.size(); i++) {
TreeItem ti = list.get(i);
@@ -2101,7 +2131,7 @@ public class AVM2Code {
}
code.remove(pos);
invalidateCache();
}
public void insertInstruction(int pos, AVM2Instruction instruction) {
@@ -2145,67 +2175,16 @@ public class AVM2Code {
code.add(pos, instruction);
}
private void removeFreeBlocks(ConstantPool constants, MethodBody body) throws ConvertException {
List<Long> offsets = new ArrayList<Long>();
for (AVM2Instruction ins : code) {
offsets.addAll(ins.getOffsets());
}
for (ABCException ex : body.exceptions) {
offsets.add((long) ex.start);
offsets.add((long) ex.end);
offsets.add((long) ex.target);
}
int clearedCount = 0;
loopip:
for (int ip = 0; ip < code.size(); ip++) {
AVM2Instruction ins = code.get(ip);
if (ins.definition instanceof JumpIns) {
int secondAddr = pos2adr(ip + 1);
int jumpAddr = secondAddr + ins.operands[0];
int jumpPos = adr2pos(jumpAddr);
if (jumpPos <= ip) {
continue;
}
if (jumpPos > code.size()) {
continue;
}
for (int k = ip + 1; k < jumpPos; k++) {
if (offsets.contains((long) pos2adr(k))) {
continue loopip;
}
}
for (int k = ip; k < jumpPos; k++) {
removeInstruction(ip, body);
clearedCount++;
}
offsets.clear();
for (AVM2Instruction ins2 : code) {
offsets.addAll(ins2.getOffsets());
}
for (ABCException ex : body.exceptions) {
offsets.add((long) ex.start);
offsets.add((long) ex.end);
offsets.add((long) ex.target);
}
ip--;
//ip=jumpPos;
}
}
if (clearedCount > 0) {
//System.out.println("Cleared " + clearedCount + " lines of code TO:");
//System.out.println(toASMSource(constants));
//System.exit(1);
}
}
public void clearSecureSWF(ConstantPool constants, MethodBody body) throws ConvertException {
public int removePushTrueFalseTraps(ConstantPool constants, MethodBody body) throws ConvertException {
removeDeadCode(constants, body);
boolean isSecure = true;
if (code.size() > 4) {
AVM2Instruction first = code.get(0);
AVM2Instruction second = code.get(1);
boolean firstValue = false;
boolean secondValue = false;
boolean isSecure = true;
if (first.definition instanceof PushFalseIns) {
firstValue = false;
} else if (first.definition instanceof PushTrueIns) {
@@ -2222,16 +2201,30 @@ public class AVM2Code {
isSecure = false;
}
if (isSecure) {
AVM2Instruction third = code.get(2);
int pos = 2;
AVM2Instruction third = code.get(pos);
if (third.definition instanceof SwapIns) {
pos++;
boolean dup = firstValue;
firstValue = secondValue;
secondValue = dup;
third.ignored = true;
}
while (third.definition instanceof JumpIns) {
pos = adr2pos(pos2adr(pos) + third.getBytes().length + third.operands[0]);
third = code.get(pos);
}
AVM2Instruction firstSet = code.get(pos);
AVM2Instruction secondSet = code.get(pos + 1);
while (firstSet.definition instanceof JumpIns) {
pos = adr2pos(pos2adr(pos) + firstSet.getBytes().length + firstSet.operands[0]);
firstSet = code.get(pos);
}
pos++;
AVM2Instruction secondSet = code.get(pos);
while (secondSet.definition instanceof JumpIns) {
pos = adr2pos(pos2adr(pos) + secondSet.getBytes().length + secondSet.operands[0]);
secondSet = code.get(pos);
}
int trueIndex = -1;
int falseIndex = -1;
if (firstSet.definition instanceof SetLocalTypeIns) {
@@ -2247,24 +2240,19 @@ public class AVM2Code {
if (isSecure) {
if (secondSet.definition instanceof SetLocalTypeIns) {
if (firstValue == true) {
trueIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(firstSet);
trueIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(secondSet);
}
if (firstValue == false) {
falseIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(firstSet);
falseIndex = ((SetLocalTypeIns) secondSet.definition).getRegisterId(secondSet);
}
//Yes, secure
pos += 2;
for (int i = 0; i < pos; i++) {
code.get(i).ignored = true;
//removeInstruction(0, body);
}
System.out.println("trueIndex:" + trueIndex);
System.out.println("falseIndex:" + falseIndex);
secondSet.ignored = true;
firstSet.ignored = true;
first.ignored = true;
second.ignored = true;
boolean found;
do {
found = false;
for (int ip = pos; ip < code.size(); ip++) {
for (int ip = 0; ip < code.size(); ip++) {
if (code.get(ip).ignored) {
continue;
}
@@ -2275,10 +2263,10 @@ public class AVM2Code {
Stack<Boolean> myStack = new Stack<Boolean>();
do {
AVM2Instruction ins = code.get(ip);
if (ins.ignored) {
ip++;
continue;
} else if (ins.definition instanceof GetLocalTypeIns) {
/*if (ins.ignored) {
ip++;
continue;
} else*/ if (ins.definition instanceof GetLocalTypeIns) {
regIndex = ((GetLocalTypeIns) ins.definition).getRegisterId(ins);
if (regIndex == trueIndex) {
myStack.push(true);
@@ -2319,21 +2307,19 @@ public class AVM2Code {
} else if (ins.definition instanceof JumpIns) {
ip = adr2pos(pos2adr(ip + 1) + code.get(ip).operands[0]);
} else {
ip++;
}
} while (myStack.size() > 0);
/*for(int rem=code.size();rem>=0;rem--){
if(code.get(rem).ignored){
code.remove(rem);
}
} */
break;
}
}
}
} while (found);
removeIgnored(body);
removeDeadCode(constants, body);
} else {
//isSecure = false;
}
@@ -2342,10 +2328,13 @@ public class AVM2Code {
}
}
}
int ret=isSecure ? 1 : 0;
ret+=visitCodeTrap(body, new int[code.size()]);
removeIgnored(body);
return ret;
}
public void clearCode(ConstantPool constants, MethodBody body) throws ConvertException {
public int removePushByteTraps(ConstantPool constants, MethodBody body) throws ConvertException {
if (code.size() > 3) {
if (code.get(0).definition instanceof PushByteIns) {
if (code.get(1).definition instanceof PushByteIns) {
@@ -2356,16 +2345,25 @@ public class AVM2Code {
for (int i = 0; i < targetPos; i++) {
removeInstruction(0, body);
}
return 1;
}
}
}
}
}
return 0;
}
removeFreeBlocks(constants, body);
//clearSecureSWF(constants, body);
public int removeTraps(ConstantPool constants, MethodBody body) {
int ret = 0;
try {
ret += removePushByteTraps(constants, body);
ret += removePushTrueFalseTraps(constants, body);
} catch (ConvertException ex) {
ex.printStackTrace();
}
restoreControlFlow(constants, body);
return ret;
}
private void handleRegister(CodeStats stats, int reg) {
@@ -2464,19 +2462,30 @@ public class AVM2Code {
return stats;
}
private void visitCode(int ip, boolean visited[]) {
while ((ip < visited.length) && (!visited[ip])) {
visited[ip] = true;
private void visitCode(int ip, int visited[]) {
while (ip < visited.length) {
visited[ip]++;
if (visited[ip] > 1) {
break;
}
AVM2Instruction ins = code.get(ip);
if (ins.definition instanceof ThrowIns) {
break;
}
if (ins.definition instanceof ReturnValueIns) {
break;
}
if (ins.definition instanceof ReturnVoidIns) {
break;
}
if (ins.definition instanceof LookupSwitchIns) {
try {
for(int i=2;i<ins.operands.length;i++){
try {
for (int i = 2; i < ins.operands.length; i++) {
visitCode(adr2pos(pos2adr(ip) + ins.operands[i]), visited);
}
ip = adr2pos(pos2adr(ip) + ins.operands[0]);
continue;
} catch (ConvertException ex) {
}
}
if (ins.definition instanceof JumpIns) {
@@ -2497,10 +2506,9 @@ public class AVM2Code {
};
}
public int removeDeadCode(MethodBody body) {
boolean visited[] = new boolean[code.size()];
private void visitCode(MethodBody body, int visited[]) {
for (int i = 0; i < visited.length; i++) {
visited[i] = false;
visited[i] = 0;
}
visitCode(0, visited);
for (ABCException e : body.exceptions) {
@@ -2511,9 +2519,218 @@ public class AVM2Code {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private int visitCodeTrap(int ip, int visited[],AVM2Instruction prev) {
int ret=0;
while (ip < visited.length) {
visited[ip]++;
if (visited[ip] > 1) {
break;
}
AVM2Instruction ins = code.get(ip);
if (ins.definition instanceof ThrowIns) {
break;
}
if (ins.definition instanceof ReturnValueIns) {
break;
}
if (ins.definition instanceof ReturnVoidIns) {
break;
}
if (ins.definition instanceof LookupSwitchIns) {
try {
for (int i = 2; i < ins.operands.length; i++) {
ret+=visitCodeTrap(adr2pos(pos2adr(ip) + ins.operands[i]), visited,prev);
}
ip = adr2pos(pos2adr(ip) + ins.operands[0]);
prev=ins;
continue;
} catch (ConvertException ex) {
}
}
if (ins.definition instanceof JumpIns) {
try {
ip = adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]);
prev=ins;
continue;
} catch (ConvertException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
}
} else if (ins.definition instanceof IfTypeIns) {
if ((prev != null) && ins.definition instanceof IfTrueIns) {
if (prev.definition instanceof PushTrueIns) {
prev.ignored = true;
ins.definition = new JumpIns();
ip--;
ret++;
continue;
} else if (prev.definition instanceof PushFalseIns) {
prev.ignored = true;
ins.ignored = true;
ret++;
continue;
}
}
if ((prev != null) && ins.definition instanceof IfFalseIns) {
if (prev.definition instanceof PushFalseIns) {
prev.ignored = true;
ins.definition = new JumpIns();
ip--;
ret++;
continue;
} else if (prev.definition instanceof PushTrueIns) {
prev.ignored = true;
ins.ignored = true;
ret++;
continue;
}
}
try {
ret+=visitCodeTrap(adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]), visited,prev);
} catch (ConvertException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
}
}
ip++;
prev=ins;
};
return ret;
}
private int visitCodeTrap(MethodBody body, int visited[]) {
int ret=0;
for (int i = 0; i < visited.length; i++) {
visited[i] = 0;
}
ret+=visitCodeTrap(0, visited,null);
for (ABCException e : body.exceptions) {
try {
ret+=visitCodeTrap(adr2pos(e.start), visited,null);
ret+=visitCodeTrap(adr2pos(e.target), visited,null);
} catch (ConvertException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
}
}
return ret;
}
public void restoreControlFlow(int ip, int visited[], int visited2[], HashMap<Integer, List<AVM2Instruction>> appended) throws ConvertException {
List<AVM2Instruction> buf = new ArrayList<AVM2Instruction>();
boolean cont = false;
int continueip = 0;
AVM2Instruction prev = null;
for (; ip < visited.length; ip++) {
AVM2Instruction ins = code.get(ip);
if (visited2[ip] > 0) {
break;
}
visited2[ip]++;
if (visited[ip] > 1) {
if (cont) {
buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{ip}, new byte[0]));
}
cont = false;
}
if (ins.definition instanceof LookupSwitchIns) {
if (cont) {
buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{ip}, new byte[0]));
}
cont = false;
restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[0]), visited, visited2, appended);
for (int i = 2; i < ins.operands.length; i++) {
restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[i]), visited, visited2, appended);
}
break;
}
if (ins.definition instanceof JumpIns) {
int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]);
if ((newip < visited.length) && (visited[newip] == 1)) {
if (!cont) {
continueip = ip;
buf = new ArrayList<AVM2Instruction>();
appended.put(continueip, buf);
}
cont = true;
} else {
if (cont) {
buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{newip}, new byte[0]));
}
cont = false;
}
ip = newip - 1;
} else if (ins.definition instanceof IfTypeIns) {
int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]);
if (cont) {
buf.add(new AVM2Instruction(0, new JumpIns(), new int[]{ip}, new byte[0]));
}
cont = false;
restoreControlFlow(newip, visited, visited2, appended);
} else if ((ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ThrowIns)) {
if (cont) {
buf.add(ins);
}
break;
} else if (cont) {
buf.add(ins);
}
prev = ins;
}
}
public void restoreControlFlow(ConstantPool constants, MethodBody body) {
try {
int visited[] = new int[code.size()];
int visited2[] = new int[code.size()];
visitCode(body, visited);
HashMap<Integer, List<AVM2Instruction>> appended = new HashMap<Integer, List<AVM2Instruction>>();
restoreControlFlow(0, visited, visited2, appended);
for (ABCException e : body.exceptions) {
try {
restoreControlFlow(adr2pos(e.start), visited, visited2, appended);
restoreControlFlow(adr2pos(e.target), visited, visited2, appended);
} catch (ConvertException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
}
}
for (int ip : appended.keySet()) {
code.get(ip).replaceWith = appended.get(ip);
}
} catch (ConvertException cex) {
}
try {
String src = Highlighting.stripHilights(toASMSource(constants, body));
FileOutputStream fos = new FileOutputStream("src.txt");
fos.write(src.getBytes());
fos.close();
AVM2Code acode = ASM3Parser.parse(new ByteArrayInputStream(src.getBytes()), constants, null, body);
this.code = acode.code;
} catch (IOException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
} catch (ParseException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex);
}
invalidateCache();
removeDeadCode(constants, body);
}
private void removeIgnored(MethodBody body) {
for (int rem = code.size() - 1; rem >= 0; rem--) {
if (code.get(rem).ignored) {
removeInstruction(rem, body);
}
}
}
public int removeDeadCode(ConstantPool constants, MethodBody body) {
int visited[] = new int[code.size()];
visitCode(body, visited);
int cnt = 0;
for (int i = visited.length - 1; i >= 0; i--) {
if (!visited[i]) {
if (visited[i] == 0) {
removeInstruction(i, body);
cnt++;
}
@@ -2526,7 +2743,7 @@ public class AVM2Code {
cnt++;
}
}
}
}
return cnt;
}
}

View File

@@ -207,6 +207,12 @@ public class AVM2Instruction {
return " ;" + comment;
}
public boolean isIgnored() {
return ignored;
}
public String toString(ConstantPool constants, List<String> fullyQualifiedNames) {
String s = Helper.formatAddress(offset) + " " + Helper.padSpaceRight(Helper.byteArrToString(getBytes()), 30) + definition.instructionName;
s += getParams(constants, fullyQualifiedNames) + getComment();
@@ -218,4 +224,6 @@ public class AVM2Instruction {
s += getParams(constants, fullyQualifiedNames) + getComment();
return s;
}
public List<AVM2Instruction> replaceWith;
}

View File

@@ -147,7 +147,7 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener {
oneList.add(list.get(index));
this.abc = list.get(index).abc;
classTree.setDoABCTags(oneList);
}
}
updateConstList();
}
@@ -319,7 +319,9 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener {
}
public void reload() {
switchAbc(listIndex - 1);
switchAbc(listIndex);
decompiledTextArea.clearScriptCache();
decompiledTextArea.reloadClass();
}
public void itemStateChanged(ItemEvent e) {

View File

@@ -74,7 +74,6 @@ public class ASMSourceEditorPane extends LineMarkedEditorPane implements CaretLi
public void graph() {
Graph gr = new Graph(abc.bodies[bodyIndex].code);
//(new GraphTreeFrame(gr)).setVisible(true);
(new GraphFrame(gr, "")).setVisible(true);
}

View File

@@ -245,8 +245,12 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
}
private List<DoABCTag> abcList;
public void setScript(ScriptInfo script, ABC abc, List<DoABCTag> abcList) {
setText("//Please wait...");
public void clearScriptCache()
{
bufferedClasses.clear();
}
public void setScript(ScriptInfo script, ABC abc, List<DoABCTag> abcList) {
if (script == null) {
highlights = new ArrayList<Highlighting>();
traitHighlights = new ArrayList<Highlighting>();
@@ -254,6 +258,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
this.script = null;
return;
}
setText("//Please wait...");
String hilightedCode;
if (!bufferedClasses.containsKey(script)) {
@@ -279,11 +284,13 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
}
public void reloadClass() {
int ci=classIndex;
if (bufferedClasses.containsKey(script)) {
bufferedClasses.remove(script);
}
setScript(script, abc, abcList);
setNoTrait();
setClassIndex(classIndex);
}
public int getClassIndex() {

View File

@@ -127,9 +127,7 @@ public class DetailPanel extends JPanel implements ActionListener {
if (cardMap.get(selectedCard) instanceof TraitDetail) {
if (((TraitDetail) cardMap.get(selectedCard)).save()) {
int lasttrait = abcPanel.decompiledTextArea.lastTraitIndex;
int lastclass = abcPanel.decompiledTextArea.getClassIndex();
abcPanel.decompiledTextArea.reloadClass();
abcPanel.decompiledTextArea.setClassIndex(lastclass);
abcPanel.decompiledTextArea.reloadClass();
abcPanel.decompiledTextArea.gotoTrait(lasttrait);
JOptionPane.showMessageDialog(this, "Trait Successfully saved");
}

View File

@@ -21,7 +21,10 @@ import com.jpexs.asdec.abc.avm2.flowgraph.GraphPart;
import com.jpexs.asdec.gui.View;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
@@ -42,33 +45,40 @@ public class GraphFrame extends JFrame {
public GraphPanel(Graph graph) {
this.graph = graph;
setPreferredSize(new Dimension((BLOCK_WIDTH + SPACE_HORIZONTAL) * getPartWidth(graph.head, new ArrayList<GraphPart>()), (BLOCK_HEIGHT + SPACE_VERTICAL) * getPartHeight(graph.head, new ArrayList<GraphPart>())));
setPreferredSize(new Dimension((BLOCK_WIDTH + SPACE_HORIZONTAL) * getPartWidth(graph.head, new HashSet<GraphPart>()), (BLOCK_HEIGHT + SPACE_VERTICAL) * getPartHeight(graph.head, new ArrayList<GraphPart>())));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
paintPart(g, graph.head, 0, getPartWidth(graph.head, new ArrayList<GraphPart>()) * (BLOCK_WIDTH + SPACE_HORIZONTAL) / 2, new ArrayList<GraphPart>());
paintPart(g, graph.head, 0, getPartWidth(graph.head, new HashSet<GraphPart>()) * (BLOCK_WIDTH + SPACE_HORIZONTAL) / 2, new HashMap<GraphPart,Point>());
}
private void paintPart(Graphics g, GraphPart part, int y, int x, List<GraphPart> used) {
List<GraphPart> l = new ArrayList<GraphPart>();
l.addAll(used);
int totalWidthParts = getPartWidth(part, l);
private void paintPart(Graphics g, GraphPart part, int y, int x, HashMap<GraphPart,Point> used) {
HashMap<GraphPart,Point> l = new HashMap<GraphPart,Point>();
l.putAll(used);
HashSet<GraphPart> hs=new HashSet<GraphPart>();
hs.addAll(l.keySet());
int totalWidthParts = getPartWidth(part, hs);
int totalWidth = totalWidthParts * (BLOCK_WIDTH + SPACE_HORIZONTAL);
g.drawRect(x - BLOCK_WIDTH / 2 - SPACE_HORIZONTAL / 2, y, BLOCK_WIDTH, BLOCK_HEIGHT);
g.drawString(part.toString(), x - BLOCK_WIDTH / 2, y + BLOCK_HEIGHT);
if (used.contains(part)) {
if (used.containsKey(part)) {
g.setColor(Color.black);
Point p=used.get(part);
g.drawLine(x, y, p.x, p.y);
return;
}
used.add(part);
g.drawRect(x - BLOCK_WIDTH / 2 - SPACE_HORIZONTAL / 2, y, BLOCK_WIDTH, BLOCK_HEIGHT);
g.drawString(part.toString(), x - BLOCK_WIDTH / 2, y + BLOCK_HEIGHT);
used.put(part,new Point(x,y));
if (part.nextParts.size() > 0) {
int cx = x - totalWidth / 2;
for (int p = 0; p < part.nextParts.size(); p++) {
l = new ArrayList<GraphPart>();
l.addAll(used);
int cellWidth = getPartWidth(part.nextParts.get(p), l) * (BLOCK_WIDTH + SPACE_HORIZONTAL);
HashSet<GraphPart> k = new HashSet<GraphPart>();
k.addAll(used.keySet());
int cellWidth = getPartWidth(part.nextParts.get(p), k) * (BLOCK_WIDTH + SPACE_HORIZONTAL);
g.setColor(Color.black);
g.drawLine(x, y + BLOCK_HEIGHT, cx + cellWidth / 2, y + BLOCK_HEIGHT + SPACE_VERTICAL);
paintPart(g, part.nextParts.get(p), y + BLOCK_HEIGHT + SPACE_VERTICAL, cx + cellWidth / 2, used);
@@ -94,7 +104,7 @@ public class GraphFrame extends JFrame {
return 1 + maxH;
}
private int getPartWidth(GraphPart part, List<GraphPart> used) {
private int getPartWidth(GraphPart part, HashSet<GraphPart> used) {
if (used.contains(part)) {
return 1;

View File

@@ -85,7 +85,7 @@ public class MethodCodePanel extends JPanel implements ActionListener {
execButton.setActionCommand("EXEC");
execButton.addActionListener(this);
//buttonsPan.add(graphButton);
buttonsPanel.add(graphButton);
// buttonsPanel.add(saveButton);
// buttonsPan.add(execButton);

View File

@@ -48,8 +48,12 @@ public class MethodBody implements Cloneable {
return s;
}
public int removeDeadCode(){
return code.removeDeadCode(this);
public int removeDeadCode(ConstantPool constants){
return code.removeDeadCode(constants,this);
}
public int removeTraps(ConstantPool constants){
return code.removeTraps(constants, this);
}
public HashMap<Integer, String> getLocalRegNames(ABC abc) {

View File

@@ -236,11 +236,20 @@ public class MainFrame extends JFrame implements ActionListener {
miRemoveDeadCodeAll.setActionCommand("REMOVEDEADCODEALL");
miRemoveDeadCodeAll.addActionListener(this);
JMenuItem miTraps = new JMenuItem("Remove traps");
miTraps.setActionCommand("REMOVETRAPS");
miTraps.addActionListener(this);
JMenuItem miTrapsAll = new JMenuItem("Remove all traps");
miTrapsAll.setActionCommand("REMOVETRAPSALL");
miTrapsAll.addActionListener(this);
menuDeobfuscation.add(miSubLimiter);
menuDeobfuscation.add(miRenameIdentifiers);
menuDeobfuscation.add(miRemoveDeadCode);
menuDeobfuscation.add(miRemoveDeadCodeAll);
menuDeobfuscation.add(miTraps);
menuDeobfuscation.add(miTrapsAll);
JMenu menuTools = new JMenu("Tools");
@@ -594,6 +603,10 @@ public class MainFrame extends JFrame implements ActionListener {
return ret;
}
public boolean confirmExperimental() {
return JOptionPane.showConfirmDialog(null, "Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION;
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("EXIT")) {
setVisible(false);
@@ -724,34 +737,64 @@ public class MainFrame extends JFrame implements ActionListener {
}
}
if (e.getActionCommand().startsWith("REMOVETRAPS")) {
Main.startWork("Removing traps...");
final boolean all = e.getActionCommand().endsWith("ALL");
if ((!all) || confirmExperimental()) {
new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int cnt = 0;
if (all) {
for (DoABCTag tag : abcPanel.list) {
cnt += tag.abc.removeTraps();
}
} else {
int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
if (bi != -1) {
cnt += abcPanel.abc.bodies[bi].removeTraps(abcPanel.abc.constants);
}
abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
}
Main.stopWork();
JOptionPane.showMessageDialog(null, "Traps removed: " + cnt);
abcPanel.reload();
return true;
}
}.execute();
}
}
if (e.getActionCommand().startsWith("REMOVEDEADCODE")) {
Main.startWork("Removing dead code...");
final boolean all = e.getActionCommand().endsWith("ALL");
new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int cnt = 0;
if (all) {
for (DoABCTag tag : abcPanel.list) {
cnt += tag.abc.removeDeadCode();
if ((!all) || confirmExperimental()) {
new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int cnt = 0;
if (all) {
for (DoABCTag tag : abcPanel.list) {
cnt += tag.abc.removeDeadCode();
}
} else {
int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
if (bi != -1) {
cnt += abcPanel.abc.bodies[bi].removeDeadCode(abcPanel.abc.constants);
}
abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
}
}else{
int bi=abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
if(bi!=-1){
cnt += abcPanel.abc.bodies[bi].removeDeadCode();
}
abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
Main.stopWork();
JOptionPane.showMessageDialog(null, "Instructions removed: " + cnt);
abcPanel.reload();
return true;
}
Main.stopWork();
JOptionPane.showMessageDialog(null, "Instructions removed: " + cnt);
abcPanel.reload();
return true;
}
}.execute();
}.execute();
}
}
if (e.getActionCommand().equals("RENAMEIDENTIFIERS")) {
if (JOptionPane.showConfirmDialog(null, "Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) {
if (confirmExperimental()) {
Main.startWork("Renaming identifiers...");
new SwingWorker() {