diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index 46c5198bd..acce9f07e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -17,11 +17,38 @@ package com.jpexs.decompiler.flash; import SevenZip.Compression.LZMA.Encoder; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionGraphSource; +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.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.Null; +import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; +import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; +import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; +import com.jpexs.decompiler.flash.action.treemodel.ConstantPool; +import com.jpexs.decompiler.flash.action.treemodel.DefineLocalTreeItem; +import com.jpexs.decompiler.flash.action.treemodel.DirectValueTreeItem; import com.jpexs.decompiler.flash.flv.AUDIODATA; import com.jpexs.decompiler.flash.flv.FLVOutputStream; import com.jpexs.decompiler.flash.flv.FLVTAG; import com.jpexs.decompiler.flash.flv.VIDEODATA; +import com.jpexs.decompiler.flash.graph.GraphSourceItem; +import com.jpexs.decompiler.flash.graph.GraphTargetItem; import com.jpexs.decompiler.flash.gui.TagNode; +import com.jpexs.decompiler.flash.helpers.Helper; +import com.jpexs.decompiler.flash.helpers.Highlighting; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; @@ -36,6 +63,7 @@ import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; import com.jpexs.decompiler.flash.tags.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.Container; import com.jpexs.decompiler.flash.tags.base.ShapeTag; @@ -46,6 +74,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Random; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; import javax.imageio.ImageIO; @@ -736,4 +769,265 @@ public class SWF { public void exportShapes(String outdir) throws IOException { exportShapes(outdir, tags); } + public static final String[] reservedWords = { + "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", + "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"}; + + private boolean isReserved(String s) { + for (String rw : reservedWords) { + if (rw.equals(s.trim())) { + return true; + } + } + return false; + } + private HashMap deobfuscated = new HashMap(); + private Random rnd = new Random(); + private final int DEFAULT_FOO_SIZE = 10; + public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + public static final String validNextCharacters = validFirstCharacters + "0123456789"; + public static final String fooCharacters = "bcdfghjklmnpqrstvwz"; + public static final String fooJoinCharacters = "aeiouy"; + private HashMap allVariableNames = new HashMap(); + private HashSet allVariableNamesStr=new HashSet(); + private List allFunctions = new ArrayList(); + + private String fooString(String orig, boolean firstUppercase, int rndSize) { + boolean exists; + String ret; + loopfoo: + do { + exists = false; + int len = 3 + rnd.nextInt(rndSize - 3); + ret = ""; + for (int i = 0; i < len; i++) { + String c = ""; + if ((i % 2) == 0) { + c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length())); + } else { + c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length())); + } + if (i == 0 && firstUppercase) { + c = c.toUpperCase(); + } + ret += c; + } + if (allVariableNamesStr.contains(ret)) { + exists = true; + rndSize = rndSize + 1; + continue loopfoo; + } + if (isReserved(ret)) { + exists = true; + rndSize = rndSize + 1; + continue; + } + if (deobfuscated.containsValue(ret)) { + exists = true; + rndSize = rndSize + 1; + continue; + } + } while (exists); + deobfuscated.put(orig, ret); + return ret; + } + + public String deobfuscateName(HashMap namesMap, String s, boolean firstUppercase) { + boolean isValid = true; + if (isReserved(s)) { + isValid = false; + } + + if (isValid) { + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) > 127) { + isValid = false; + break; + } + } + } + + if (isValid) { + Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$"); + if (!pat.matcher(s).matches()) { + isValid = false; + } + } + + if (!isValid) { + if (namesMap.containsKey(s)) { + return namesMap.get(s); + } else { + String ret = fooString(s, firstUppercase, DEFAULT_FOO_SIZE); + return ret; + } + } + return null; + } + + private static void getVariables(ConstantPool constantPool, List localData, Stack stack, List output, ActionGraphSource code, int ip, int lastIp, HashMap variables,List functions, List visited) { + boolean debugMode = false; + while ((ip > -1) && ip < code.size()) { + if (visited.contains(ip)) { + break; + } + + lastIp = ip; + GraphSourceItem ins = code.get(ip); + if (debugMode) { + System.out.println("Visit " + ip + ": " + ins + " stack:" + Highlighting.stripHilights(stack.toString())); + } + + GraphTargetItem name = null; + if ((ins instanceof ActionGetVariable) + ||(ins instanceof ActionGetMember) + ||(ins instanceof ActionDefineLocal2) + ||(ins instanceof ActionNewMethod) + ||(ins instanceof ActionNewObject) + ||(ins instanceof ActionCallMethod) + ||(ins instanceof ActionCallFunction) + ) { + name = stack.peek(); + } + + if((ins instanceof ActionDefineFunction)||(ins instanceof ActionDefineFunction2)){ + functions.add(ins); + } + if ((ins instanceof ActionSetVariable)||(ins instanceof ActionSetMember)||(ins instanceof ActionDefineLocal)) { + name = stack.get(stack.size() - 2); + } + if (name instanceof DirectValueTreeItem) { + variables.put((DirectValueTreeItem)name,constantPool); + } + + //for..in return + if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueTreeItem)) { + stack.push(new DirectValueTreeItem(null, 0, new Null(), new ArrayList())); + } + + if (ins instanceof ActionConstantPool) { + constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); + } + + ins.translate(localData, stack, output); + if (ins.isExit()) { + break; + } + + if (ins.isBranch() || ins.isJump()) { + + if (false){ //ins instanceof ActionIf && !stack.isEmpty() && (stack.peek().isCompileTime())) { + boolean condition = stack.peek().toBoolean(); + if (debugMode) { + if (condition) { + System.out.println("JUMP"); + } else { + System.out.println("SKIP"); + } + } + stack.pop(); + getVariables(constantPool, localData, stack, output, code, condition ? (code.adr2pos(((ActionIf) ins).getAddress() + ((ActionIf) ins).getBytes(code.version).length + ((ActionIf) ins).offset)) : ip + 1, ip, variables,functions, visited); + } else { + if (ins instanceof ActionIf) { + stack.pop(); + } + visited.add(ip); + List branches = ins.getBranches(code); + for (int b : branches) { + Stack brStack = (Stack) stack.clone(); + if (b >= 0) { + getVariables(constantPool, localData, brStack, output, code, b, ip, variables, functions,visited); + } else { + if (debugMode) { + System.out.println("Negative branch:" + b); + } + } + } + } + break; + } + ip++; + }; + } + + private static void getVariables(HashMap variables,List functions,ActionGraphSource code, int addr) { + List localData = Helper.toList(new HashMap(), new HashMap(), new HashMap()); + try{ + getVariables(null, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), 0, variables,functions, new ArrayList()); + }catch(Exception ex){ + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Getting variables error",ex); + } + } + + private HashMap getVariables(HashMap variables,List functions,ASMSource src){ + HashMap ret=new HashMap(); + List actions = src.getActions(version); + actionsMap.put(src, actions); + getVariables(variables,functions,new ActionGraphSource(actions, version, new HashMap(), new HashMap(), new HashMap()),0); + return ret; + } + + private HashMap> actionsMap=new HashMap>(); + + private void getVariables(List objs) { + for (Object o : objs) { + if (o instanceof ASMSource) { + getVariables(allVariableNames,allFunctions,(ASMSource) o); + } + if (o instanceof Container) { + getVariables(((Container) o).getSubItems()); + } + } + } + + public int deobfuscateAS2Identifiers() { + actionsMap=new HashMap>(); + allFunctions = new ArrayList(); + allVariableNames=new HashMap(); + List objs = new ArrayList(); + int ret=0; + objs.addAll(tags); + getVariables(objs); + for(GraphSourceItem fun:allFunctions){ + if(fun instanceof ActionDefineFunction){ + ActionDefineFunction f=(ActionDefineFunction)fun; + String changed=deobfuscateName(deobfuscated, f.functionName, false); + if(changed!=null){ + f.replacedFunctionName=changed; + } + } + if(fun instanceof ActionDefineFunction2){ + ActionDefineFunction2 f=(ActionDefineFunction2)fun; + String changed=deobfuscateName(deobfuscated, f.functionName, false); + if(changed!=null){ + f.replacedFunctionName=changed; + } + } + } + for(DirectValueTreeItem ti:allVariableNames.keySet()){ + String name=ti.toStringNoH(allVariableNames.get(ti)); + allVariableNamesStr.add(name); + } + for(DirectValueTreeItem ti:allVariableNames.keySet()){ + String name=ti.toStringNoH(allVariableNames.get(ti)); + String changed=deobfuscateName(deobfuscated, name, false); + if(changed!=null){ + ActionPush pu=(ActionPush)ti.src; + if(pu.replacement==null){ + pu.replacement=new ArrayList(); + pu.replacement.addAll(pu.values); + } + pu.replacement.set(ti.pos,changed); + ret++; + } + } + for(ASMSource src:actionsMap.keySet()){ + actionsMap.put(src,Action.removeNops(actionsMap.get(src), version)); + src.setActions(actionsMap.get(src), version); + } + return ret; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index d6c124995..ef69c70ec 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -562,7 +562,11 @@ public class SWFInputStream extends InputStream { public static List getConstantPool(ActionGraphSource code, int addr) { List ret = new ArrayList(); List localData = Helper.toList(new HashMap(), new HashMap(), new HashMap()); + try{ getConstantPool(localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), 0, ret, new ArrayList()); + }catch(Exception ex){ + log.log(Level.SEVERE, "Error during getting constantpool",ex); + } return ret; } @@ -621,7 +625,7 @@ public class SWFInputStream extends InputStream { List pools = new ArrayList(); pools = getConstantPool(new ActionGraphSource(ret, version, new HashMap(), new HashMap(), new HashMap()), ip); - + if (pools.size() == 1) { Action.setConstantPool(ret, pools.get(0)); } @@ -635,7 +639,7 @@ public class SWFInputStream extends InputStream { } String s = null; //Action.setConstantPool(ret, cpool); - + try { s = Highlighting.stripHilights(Action.actionsToString(ret, null, version, false)); ret = ASMParser.parse(false, new ByteArrayInputStream(s.getBytes()), SWF.DEFAULT_VERSION); diff --git a/trunk/src/com/jpexs/decompiler/flash/action/Action.java b/trunk/src/com/jpexs/decompiler/flash/action/Action.java index 60052a091..e3d142fa8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/Action.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/Action.java @@ -397,7 +397,8 @@ public class Action implements GraphSourceItem { ret += a.beforeInsert.getASMSource(importantOffsets, constantPool, version, hex) + "\r\n"; } //if (!(a instanceof ActionNop)) { - ret += Highlighting.hilighOffset("", offset) + a.getASMSource(importantOffsets, constantPool, version, hex) + (a.ignored ? "; ignored" : "") + "\r\n"; + ret += Highlighting.hilighOffset("", offset) + a.getASMSourceReplaced(importantOffsets, constantPool, version, hex) + (a.ignored ? "; ignored" : "") + "\r\n"; + //} if (a.afterInsert != null) { ret += a.afterInsert.getASMSource(importantOffsets, constantPool, version, hex) + "\r\n"; @@ -1062,4 +1063,8 @@ public class Action implements GraphSourceItem { } } } + + public String getASMSourceReplaced(List knownAddreses, List constantPool, int version, boolean hex) { + return getASMSource(knownAddreses, constantPool, version, hex); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java index 06f036734..2d36d5f28 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java @@ -33,9 +33,11 @@ import java.util.HashMap; import java.util.List; import java.util.Stack; + public class ActionPush extends Action { public List values; + public List replacement; public List constantPool; public List ignoredParts = new ArrayList(); @@ -182,6 +184,17 @@ public class ActionPush extends Action { } } + @Override + public String getASMSourceReplaced(List knownAddreses, List constantPool, int version, boolean hex) { + if(replacement == null || replacement.size() oldVal=values; + values=replacement; + String ts=toString(); + values=oldVal; + return ts; + } @Override public String toString() { String ret = "Push "; diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java index 141da0c2f..7c1d42aa8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java @@ -41,7 +41,9 @@ import java.util.Stack; public class ActionDefineFunction extends Action implements ActionContainer { public String functionName; + public String replacedFunctionName; public List paramNames = new ArrayList(); + public List replacedParamNames; public List code; public int codeSize; private int version; @@ -166,6 +168,25 @@ public class ActionDefineFunction extends Action implements ActionContainer { return "DefineFunction \"" + Helper.escapeString(functionName) + "\" " + paramNames.size() + " " + paramStr + " {\r\n" + Action.actionsToString(code, knownAddreses, constantPool, version, hex) + "}"; } + @Override + public String getASMSourceReplaced(List knownAddreses, List constantPool, int version, boolean hex) { + List oldParamNames=paramNames; + if(replacedParamNames!=null){ + paramNames=replacedParamNames; + } + String oldFunctionName=functionName; + if(replacedFunctionName!=null){ + functionName=replacedFunctionName; + } + String ret=getASMSource(knownAddreses, constantPool, version, hex); + paramNames=oldParamNames; + functionName=oldFunctionName; + return ret; + + } + + + @Override public List getAllRefs(int version) { return Action.getActionsAllRefs(code, version); diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java b/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java index 57163224d..7ed4a50b9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java @@ -41,7 +41,9 @@ import java.util.Stack; public class ActionDefineFunction2 extends Action implements ActionContainer { public String functionName; + public String replacedFunctionName; public List paramNames = new ArrayList(); + public List replacedParamNames; public List paramRegisters = new ArrayList(); public boolean preloadParentFlag; public boolean preloadRootFlag; @@ -224,6 +226,23 @@ public class ActionDefineFunction2 extends Action implements ActionContainer { super.setAddress(address, version); Action.setActionsAddresses(code, address + getPreLen(version), version); } + + @Override + public String getASMSourceReplaced(List knownAddreses, List constantPool, int version, boolean hex) { + List oldParamNames=paramNames; + if(replacedParamNames!=null){ + paramNames=replacedParamNames; + } + String oldFunctionName=functionName; + if(replacedFunctionName!=null){ + functionName=replacedFunctionName; + } + String ret=getASMSource(knownAddreses, constantPool, version, hex); + paramNames=oldParamNames; + functionName=oldFunctionName; + return ret; + + } @Override public String getASMSource(List knownAddreses, List constantPool, int version, boolean hex) { diff --git a/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java b/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java index 99d1a7b0a..50f7909ad 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java @@ -88,6 +88,26 @@ public class DirectValueTreeItem extends TreeItem { return hilight(value.toString()); } + public String toStringNoH(ConstantPool constants) { + if (value instanceof Double) { + if (Double.compare((double) (Double) value, 0) == 0) { + return ("0"); + } + } + if (value instanceof Float) { + if (Float.compare((float) (Float) value, 0) == 0) { + return ("0"); + } + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof ConstantIndex) { + return (this.constants.get(((ConstantIndex) value).index)); + } + return value.toString(); + } + @Override public String toString(ConstantPool constants) { if (value instanceof Double) { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index 94914033c..ad6e0f06b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -382,7 +382,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi actionPanel = new ActionPanel(); addTab(tabPane, actionPanel, "ActionScript", View.getIcon("as16")); - menuDeobfuscation.setEnabled(false); + miDeobfuscation.setEnabled(false); } @@ -1312,14 +1312,21 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi @Override protected Object doInBackground() throws Exception { int cnt = 0; - HashMap namesMap = new HashMap(); - for (DoABCTag tag : abcPanel.list) { - cnt += tag.abc.deobfuscateIdentifiers(namesMap); + + if (abcPanel != null) { + HashMap namesMap = new HashMap(); + for (DoABCTag tag : abcPanel.list) { + cnt += tag.abc.deobfuscateIdentifiers(namesMap); + } } + cnt = swf.deobfuscateAS2Identifiers(); Main.stopWork(); JOptionPane.showMessageDialog(null, "Identifiers renamed: " + cnt); - abcPanel.reload(); + if(abcPanel!=null){ + abcPanel.reload(); + } doFilter(); + reload(true); return true; } }.execute(); @@ -1399,7 +1406,10 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi private File tempFile; @Override - public void valueChanged(TreeSelectionEvent e) { + public void valueChanged(TreeSelectionEvent e) { + reload(false); + } + public void reload(boolean forceReload){ Object tagObj = tagTree.getLastSelectedPathComponent(); if (tagObj == null) { return; @@ -1412,11 +1422,10 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi tagObj = ((TreeElement) tagObj).getItem(); } - if (tagObj == oldValue) { + if (!forceReload && (tagObj == oldValue)) { return; } oldValue = tagObj; - if (tagObj instanceof TreeLeafScript) { final TreeLeafScript scriptLeaf = (TreeLeafScript) tagObj; if (!Main.isWorking()) {