From 8f4fbb667907770a1f68b484025f00e786364edd Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 3 Sep 2013 00:35:24 +0200 Subject: [PATCH] AS2 disassembler fixed (jump to the middle of the instuction) --- .../decompiler/flash/SWFInputStream.java | 918 +++++------------- .../jpexs/decompiler/flash/action/Action.java | 5 +- .../flash/action/ActionListReader.java | 844 ++++++++++++++++ .../flash/action/model/ConstantPool.java | 21 +- .../flash/action/parser/pcode/ASMParser.java | 8 +- .../flash/action/swf3/ActionGotoFrame.java | 4 +- .../flash/action/swf3/ActionWaitForFrame.java | 11 +- .../flash/action/swf4/ActionCall.java | 4 + .../flash/action/swf4/ActionGetURL2.java | 4 +- .../flash/action/swf4/ActionIf.java | 4 +- .../flash/action/swf4/ActionJump.java | 4 +- .../action/swf4/ActionWaitForFrame2.java | 10 +- .../action/swf5/ActionDefineFunction.java | 9 + .../action/swf5/ActionStoreRegister.java | 4 +- .../flash/action/swf5/ActionWith.java | 14 +- .../action/swf7/ActionDefineFunction2.java | 9 + .../flash/action/swf7/ActionTry.java | 21 + .../jpexs/decompiler/flash/gui/MainFrame.java | 2 +- .../flash/gui/action/ActionPanel.java | 85 +- .../flash/tags/DefineButtonTag.java | 7 +- .../decompiler/flash/tags/DoActionTag.java | 7 +- .../flash/tags/DoInitActionTag.java | 7 +- .../flash/types/BUTTONCONDACTION.java | 7 +- .../flash/types/CLIPACTIONRECORD.java | 7 +- .../src/com/jpexs/decompiler/graph/Graph.java | 2 +- .../graph/GraphSourceItemContainer.java | 2 + 26 files changed, 1254 insertions(+), 766 deletions(-) create mode 100644 trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index c26af54ef..8570680b5 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -17,7 +17,7 @@ package com.jpexs.decompiler.flash; import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionGraphSource; +import com.jpexs.decompiler.flash.action.ActionListReader; import com.jpexs.decompiler.flash.action.StoreTypeAction; import com.jpexs.decompiler.flash.action.model.ConstantPool; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; @@ -31,8 +31,8 @@ import com.jpexs.decompiler.flash.action.swf6.*; import com.jpexs.decompiler.flash.action.swf7.*; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.helpers.Highlighting; import com.jpexs.decompiler.flash.tags.*; +import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.types.*; import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; import com.jpexs.decompiler.flash.types.filters.BLURFILTER; @@ -63,9 +63,11 @@ import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Queue; import java.util.Scanner; import java.util.Stack; import java.util.concurrent.Callable; @@ -241,8 +243,8 @@ public class SWFInputStream extends InputStream { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int r; while (true) { - r = read(); - if (r <= 0) { + r = readEx(); + if (r == 0) { return new String(baos.toByteArray(), "utf8"); } baos.write(r); @@ -539,461 +541,11 @@ public class SWFInputStream extends InputStream { public List readActionList(List listeners, long containerSWFOffset, String path) throws IOException { ReReadableInputStream rri = new ReReadableInputStream(this); - return readActionList(listeners, containerSWFOffset, rri, version, 0, -1, path); + return ActionListReader.readActionList(listeners, containerSWFOffset, rri, version, 0, -1, path); } public List readActionList(List listeners, long containerSWFOffset, ReReadableInputStream rri, int maxlen, String path) throws IOException { - return readActionList(listeners, containerSWFOffset, rri, version, rri.getPos(), rri.getPos() + maxlen, path); - } - - private static List prepareLocalBranch(List localData) { - @SuppressWarnings("unchecked") - HashMap regNames = (HashMap) localData.get(0); - @SuppressWarnings("unchecked") - HashMap variables = (HashMap) localData.get(1); - @SuppressWarnings("unchecked") - HashMap functions = (HashMap) localData.get(2); - List ret = new ArrayList<>(); - ret.add(new HashMap(regNames)); - ret.add(new HashMap(variables)); - ret.add(new HashMap(functions)); - return ret; - } - - /** - * Reads list of actions from the stream. Reading ends with - * ActionEndFlag(=0) or end of the stream. - * - * @param listeners - * @param address - * @param ip - * @param rri - * @param version - * @param containerSWFOffset - * @param endip - * @param path - * @return List of actions - * @throws IOException - */ - public static List readActionList(List listeners, long containerSWFOffset, ReReadableInputStream rri, int version, int ip, int endip, String path) throws IOException { - List retdups = new ArrayList<>(); - ConstantPool cpool = new ConstantPool(); - - Stack stack = new Stack<>(); - - List localData = Helper.toList(new HashMap(), new HashMap(), new HashMap()); - - - SWFInputStream sis = new SWFInputStream(rri, version); - boolean goesPrev = false; - int method = 1; - /*try { - goesPrev = readActionListAtPos(false, localData, stack, cpool, sis, rri, ip, retdups, ip); - } catch (Exception ex) { - method = 2; - goesPrev = readActionListAtPos(true, localData, stack, cpool, sis, rri, ip, retdups, ip); - }*/ - goesPrev = readActionListAtPos(listeners, new ArrayList(), new HashMap>(), containerSWFOffset, localData, stack, cpool, sis, rri, ip, retdups, ip, endip, path, new HashMap(), false, new HashMap>()); - - if (goesPrev) { - } else { - if (!retdups.isEmpty()) { - for (int i = 0; i < ip; i++) { - retdups.remove(0); - } - } - } - List ret = new ArrayList<>(); - Action last = null; - for (Action a : retdups) { - if (a != last) { - ret.add(a); - } - last = a; - } - for (int i = 0; i < retdups.size(); i++) { - Action a = retdups.get(i); - if (a instanceof ActionEnd) { - if (i < retdups.size() - 1) { - ActionJump jmp = new ActionJump(0); - jmp.setJumpOffset(retdups.size() - i - jmp.getBytes(version).length); - a.replaceWith = jmp; - } - } - } - - //List pools; - if (Configuration.getConfig("removeNops", true)) { - ret = Action.removeNops(0, ret, version, 0, path); - } - //pools = getConstantPool(listeners, new ActionGraphSource(ret, version, new HashMap(), new HashMap(), new HashMap()), 0, version, path); - - /*if (pools.size() == 1) { - Action.setConstantPool(ret, pools.get(0)); - }*/ - if (goesPrev && (!DEOBFUSCATION_ALL_CODE_IN_PREVIOUS_TAG)) { - ActionJump aj = new ActionJump(ip); - int skip = aj.getBytes(version).length; - for (GraphSourceItem s : ret) { - - if (s instanceof Action) { - Action a = (Action) s; - a.setAddress(a.getAddress() + skip, version); - } - } - ret.add(0, aj); - } - String s = null; - List reta = new ArrayList<>(); - for (Object o : ret) { - if (o instanceof Action) { - reta.add((Action) o); - } - } - return reta; - } - - @SuppressWarnings("unchecked") - private static boolean readActionListAtPos(List listeners, List output, HashMap> containers, long containerSWFOffset, List localData, Stack stack, ConstantPool cpool, SWFInputStream sis, ReReadableInputStream rri, int ip, List ret, int startIp, int endip, String path, Map visited, boolean indeterminate, Map> decisionStates) throws IOException { - boolean debugMode = false; - boolean decideBranch = false; - - boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); - boolean retv = false; - rri.setPos(ip); - Action a; - long filePos = rri.getPos(); - Scanner sc = new Scanner(System.in, "utf-8"); - int prevIp = ip; - loopip: - while (((endip == -1) || (endip > ip)) && (a = sis.readAction(rri, cpool)) != null) { - if (!visited.containsKey(ip)) { - visited.put(ip, 0); - } - int curVisited = visited.get(ip); - curVisited++; - visited.put(ip, curVisited); - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).progress("Reading", rri.getCount(), rri.length()); - } - if ((ip < ret.size()) && (!(ret.get(ip) instanceof ActionNop))) { - a = ret.get(ip); - if (a.getAddress() != ip) { - Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "Jump to the middle of the instruction ip " + ip + " ins " + a.getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), SWF.DEFAULT_VERSION, false)); - } - } - a.containerSWFOffset = containerSWFOffset; - a.setAddress(prevIp, SWF.DEFAULT_VERSION, false); - int info = a.actionLength + 1 + ((a.actionCode > 0x80) ? 2 : 0); - byte b[] = a.getBytes(sis.version); - int infoCorrect = info; - /*if (b.length != infoCorrect) { - throw new RuntimeException("Wrong length "+a.toString()+" info:"+infoCorrect+" actual:"+b.length+" datalen:"+(infoCorrect-info)); - }*/ - //int actual = a.getBytes(sis.version).length; - if ((!(a instanceof ActionStore)) && (!(a instanceof GraphSourceItemContainer))) { - int change = info - (rri.getPos() - ip); - if (change > 0) { - a.afterInsert = new ActionJump(change); - } - } else { - info = rri.getPos() - ip; - } - if (ip < startIp) { - retv = true; - } - - - /*if(a instanceof ActionConstantPool){ - throw new IllegalArgumentException("CP found"); - } */ - if (a instanceof ActionPush) { - if (cpool != null) { - ((ActionPush) a).constantPool = cpool.constants; - cpool.count++; - } - } - if (a instanceof ActionDefineFunction) { - if (cpool != null) { - //((ActionDefineFunction) a).setConstantPool(cpool.constants); - cpool.count++; - } - } - if (a instanceof ActionDefineFunction2) { - if (cpool != null) { - //((ActionDefineFunction2) a).setConstantPool(cpool.constants); - cpool.count++; - } - } - - if (debugMode) { - //if(a instanceof ActionIf){ - String atos = a.getASMSource(new ArrayList(), new ArrayList(), cpool.constants, sis.version, false); - if (a instanceof GraphSourceItemContainer) { - atos = a.toString(); - } - System.err.println("readActionListAtPos ip: " + (ip - startIp) + " (0x" + Helper.formatAddress(ip - startIp) + ") " + " action(len " + a.actionLength + "): " + atos + (a.isIgnored() ? " (ignored)" : "") + " stack:" + Helper.stackToString(stack, Helper.toList(cpool)) + " " + Helper.byteArrToString(a.getBytes(SWF.DEFAULT_VERSION))); - @SuppressWarnings("unchecked") - HashMap vars = (HashMap) localData.get(1); - System.err.print("variables: "); - for (Entry v : vars.entrySet()) { - System.err.print("'" + v + "' = " + v.getValue().toString(false, cpool) + ", "); - } - System.err.println(); - String add = ""; - if (a instanceof ActionIf) { - add = " change: " + ((ActionIf) a).getJumpOffset(); - } - if (a instanceof ActionJump) { - add = " change: " + ((ActionJump) a).getJumpOffset(); - } - System.err.println(add); - } - long newFilePos = rri.getPos(); - long actionLen = newFilePos - filePos; - - ensureCapacity(ret, ip); - int newip = -1; - - if (a instanceof ActionConstantPool) { - if (cpool == null) { - cpool = new ConstantPool(); - } - cpool.setNew(((ActionConstantPool) a).constantPool); - } - ActionIf aif = null; - boolean goaif = false; - if (!a.isIgnored()) { - String varname = null; - if (a instanceof StoreTypeAction) { - StoreTypeAction sta = (StoreTypeAction) a; - varname = sta.getVariableName(stack, cpool); - } - - try { - if (a instanceof ActionIf) { - aif = (ActionIf) a; - - GraphTargetItem top = null; - if (deobfuscate) { - top = stack.pop(); - } - int nip = rri.getPos() + aif.getJumpOffset(); - - if (decideBranch) { - System.out.print("newip " + nip + ", "); - System.out.print("Action: jump(j),ignore(i),compute(c)?"); - String next = sc.next(); - if (next.equals("j")) { - newip = rri.getPos() + aif.getJumpOffset(); - rri.setPos(newip); - - } else if (next.equals("i")) { - } else if (next.equals("c")) { - goaif = true; - } - } else if (deobfuscate && top.isCompileTime() && (!top.hasSideEffect())) { - ((ActionIf) a).compileTime = true; - if (debugMode) { - System.err.print("is compiletime -> "); - } - if (EcmaScript.toBoolean(top.getResult())) { - newip = rri.getPos() + aif.getJumpOffset(); - aif.jumpUsed = true; - if (aif.ignoreUsed) { - aif.compileTime = false; - } - if (debugMode) { - System.err.println("jump"); - } - } else { - aif.ignoreUsed = true; - if (aif.jumpUsed) { - aif.compileTime = false; - } - if (debugMode) { - System.err.println("ignore"); - } - } - } else { - if (debugMode) { - System.err.println("goaif"); - } - //throw new RuntimeException("goaif"); - goaif = true; - } - } else if (a instanceof ActionJump) { - newip = rri.getPos() + ((ActionJump) a).getJumpOffset(); - //if(newip>=0){ - //rri.setPos(newip); - //} - } else if (!(a instanceof GraphSourceItemContainer)) { - if (deobfuscate) { - //return in for..in, TODO:Handle this better way - if (((a instanceof ActionEquals) || (a instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { - stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); - } - if ((a instanceof ActionStoreRegister) && stack.isEmpty()) { - stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); - } - a.translate(localData, stack, output, Graph.SOP_USE_STATIC/*Graph.SOP_SKIP_STATIC*/, path); - } - } - } catch (RuntimeException ex) { - /*if (!enableVariables) { - throw ex; - }*/ - log.log(Level.SEVERE, "Disassembly exception", ex); - break; - } - - HashMap vars = (HashMap) localData.get(1); - if (varname != null) { - GraphTargetItem varval = vars.get(varname); - if (varval != null && varval.isCompileTime() && indeterminate) { - vars.put(varname, new NotCompileTimeItem(null, varval)); - } - } - } - int nopos = -1; - for (int i = 0; i < actionLen; i++) { - ensureCapacity(ret, ip + i); - if (a instanceof ActionNop) { - int prevPos = (int) a.getAddress(); - a = new ActionNop(); - a.setAddress(prevPos, SWF.DEFAULT_VERSION); - nopos++; - if (nopos > 0) { - a.setAddress(a.getAddress() + 1, SWF.DEFAULT_VERSION); - } - - } - ret.set(ip + i, a); - } - - if (a instanceof GraphSourceItemContainer) { - GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; - if (a instanceof Action) { - long endAddr = a.getAddress() + cnt.getHeaderSize(); - /*if(!containers.containsKey(endAddr)){ - containers.put(endAddr, new ArrayList()); - } - containers.get(endAddr).add((ActionContainer)a); - */ - String cntName = cnt.getName(); - List> output2s = new ArrayList<>(); - for (long size : cnt.getContainerSizes()) { - if (size == 0) { - output2s.add(new ArrayList()); - continue; - } - List localData2; - List output2 = new ArrayList<>(); - if ((cnt instanceof ActionDefineFunction) || (cnt instanceof ActionDefineFunction2)) { - localData2 = Helper.toList(new HashMap(), new HashMap(), new HashMap()); - } else { - localData2 = localData; - } - readActionListAtPos(listeners, output2, containers, containerSWFOffset, localData2, new Stack(), cpool, sis, rri, (int) endAddr, ret, startIp, (int) (endAddr + size), path + (cntName == null ? "" : "/" + cntName), visited, indeterminate, decisionStates); - output2s.add(output2); - endAddr += size; - } - if (deobfuscate) { - cnt.translateContainer(output2s, stack, output, (HashMap) localData.get(0), (HashMap) localData.get(1), (HashMap) localData.get(2)); - } - ip = (int) endAddr; - prevIp = ip; - rri.setPos(ip); - filePos = rri.getPos(); - continue; - } - //infoCorrect += ((ActionContainer) a).getDataLength(); - } - - if (a instanceof ActionEnd) { - break; - } - if (newip > -1) { - ip = newip; - } else { - ip = ip + info; //(int) actionLen; - } - rri.setPos(ip); - filePos = rri.getPos(); - if (goaif) { - aif.ignoreUsed = true; - aif.jumpUsed = true; - indeterminate = true; - - HashMap vars = (HashMap) localData.get(1); - boolean stateChanged = false; - if (decisionStates.containsKey(ip)) { - HashMap oldstate = decisionStates.get(ip); - if (oldstate.size() != vars.size()) { - stateChanged = true; - } else { - for (String k : vars.keySet()) { - if (!oldstate.containsKey(k)) { - stateChanged = true; - break; - } - if (!vars.get(k).isCompileTime() && oldstate.get(k).isCompileTime()) { - stateChanged = true; - break; - } - } - } - } - HashMap curstate = new HashMap<>(); - curstate.putAll(vars); - decisionStates.put(ip, curstate); - - if ((!stateChanged) && curVisited > 1) { - List branches = new ArrayList<>(); - branches.add(rri.getPos() + aif.getJumpOffset()); - branches.add(rri.getPos()); - for (int br : branches) { - int visc = 0; - if (visited.containsKey(br)) { - visc = visited.get(br); - } - if (visc == 0) {// substack = (Stack) stack.clone(); - if (readActionListAtPos(listeners, output, containers, containerSWFOffset, prepareLocalBranch(localData), substack, cpool, sis, rri, rri.getPos() + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates)) { - retv = true; - } - rri.setPos(oldPos); - } - prevIp = ip; - if (a.isExit()) { - break; - } - } - for (DisassemblyListener listener : listeners) { - listener.progress("Reading", rri.getCount(), rri.length()); - } - return retv; - } - - private static void ensureCapacity(List ret, int index) { - int pos = ret.size(); - while (ret.size() <= index) { - Action a = new ActionNop(); - a.setAddress(pos++, SWF.DEFAULT_VERSION); - ret.add(a); - } + return ActionListReader.readActionList(listeners, containerSWFOffset, rri, version, rri.getPos(), rri.getPos() + maxlen, path); } private static void dumpTag(PrintStream out, int version, Tag tag, int level) { @@ -1076,7 +628,7 @@ public class SWFInputStream extends InputStream { /** * Reads list of tags from the stream. Reading ends with End tag(=0) or end - * of the stream. Optinally can skip AS1/2 tags when file is AS3 + * of the stream. Optionally can skip AS1/2 tags when file is AS3 * * @param swf * @param level @@ -1529,234 +1081,234 @@ public class SWFInputStream extends InputStream { try { actionCode = readUI8(); - } catch (EndOfStreamException eos) { + if (actionCode == 0) { + return new ActionEnd(); + } + if (actionCode == -1) { + return null; + } + int actionLength = 0; + if (actionCode >= 0x80) { + actionLength = readUI16(); + } + switch (actionCode) { + //SWF3 Actions + case 0x81: + return new ActionGotoFrame(actionLength, this); + case 0x83: + return new ActionGetURL(actionLength, this, version); + case 0x04: + return new ActionNextFrame(); + case 0x05: + return new ActionPrevFrame(); + case 0x06: + return new ActionPlay(); + case 0x07: + return new ActionStop(); + case 0x08: + return new ActionToggleQuality(); + case 0x09: + return new ActionStopSounds(); + case 0x8A: + return new ActionWaitForFrame(actionLength, this, cpool); + case 0x8B: + return new ActionSetTarget(actionLength, this, version); + case 0x8C: + return new ActionGoToLabel(actionLength, this, version); + //SWF4 Actions + case 0x96: + return new ActionPush(actionLength, this, version); + case 0x17: + return new ActionPop(); + case 0x0A: + return new ActionAdd(); + case 0x0B: + return new ActionSubtract(); + case 0x0C: + return new ActionMultiply(); + case 0x0D: + return new ActionDivide(); + case 0x0E: + return new ActionEquals(); + case 0x0F: + return new ActionLess(); + case 0x10: + return new ActionAnd(); + case 0x11: + return new ActionOr(); + case 0x12: + return new ActionNot(); + case 0x13: + return new ActionStringEquals(); + case 0x14: + return new ActionStringLength(); + case 0x21: + return new ActionStringAdd(); + case 0x15: + return new ActionStringExtract(); + case 0x29: + return new ActionStringLess(); + case 0x31: + return new ActionMBStringLength(); + case 0x35: + return new ActionMBStringExtract(); + case 0x18: + return new ActionToInteger(); + case 0x32: + return new ActionCharToAscii(); + case 0x33: + return new ActionAsciiToChar(); + case 0x36: + return new ActionMBCharToAscii(); + case 0x37: + return new ActionMBAsciiToChar(); + case 0x99: + return new ActionJump(actionLength, this); + case 0x9D: + return new ActionIf(actionLength, this); + case 0x9E: + return new ActionCall(actionLength); + case 0x1C: + return new ActionGetVariable(); + case 0x1D: + return new ActionSetVariable(); + case 0x9A: + return new ActionGetURL2(actionLength, this); + case 0x9F: + return new ActionGotoFrame2(actionLength, this); + case 0x20: + return new ActionSetTarget2(); + case 0x22: + return new ActionGetProperty(); + case 0x23: + return new ActionSetProperty(); + case 0x24: + return new ActionCloneSprite(); + case 0x25: + return new ActionRemoveSprite(); + case 0x27: + return new ActionStartDrag(); + case 0x28: + return new ActionEndDrag(); + case 0x8D: + return new ActionWaitForFrame2(actionLength, this, cpool); + case 0x26: + return new ActionTrace(); + case 0x34: + return new ActionGetTime(); + case 0x30: + return new ActionRandomNumber(); + //SWF5 Actions + case 0x3D: + return new ActionCallFunction(); + case 0x52: + return new ActionCallMethod(); + case 0x88: + return new ActionConstantPool(actionLength, this, version); + case 0x9B: + return new ActionDefineFunction(actionLength, this, rri, version); + case 0x3C: + return new ActionDefineLocal(); + case 0x41: + return new ActionDefineLocal2(); + case 0x3A: + return new ActionDelete(); + case 0x3B: + return new ActionDelete2(); + case 0x46: + return new ActionEnumerate(); + case 0x49: + return new ActionEquals2(); + case 0x4E: + return new ActionGetMember(); + case 0x42: + return new ActionInitArray(); + case 0x43: + return new ActionInitObject(); + case 0x53: + return new ActionNewMethod(); + case 0x40: + return new ActionNewObject(); + case 0x4F: + return new ActionSetMember(); + case 0x45: + return new ActionTargetPath(); + case 0x94: + return new ActionWith(actionLength, this, rri, version); + case 0x4A: + return new ActionToNumber(); + case 0x4B: + return new ActionToString(); + case 0x44: + return new ActionTypeOf(); + case 0x47: + return new ActionAdd2(); + case 0x48: + return new ActionLess2(); + case 0x3F: + return new ActionModulo(); + case 0x60: + return new ActionBitAnd(); + case 0x63: + return new ActionBitLShift(); + case 0x61: + return new ActionBitOr(); + case 0x64: + return new ActionBitRShift(); + case 0x65: + return new ActionBitURShift(); + case 0x62: + return new ActionBitXor(); + case 0x51: + return new ActionDecrement(); + case 0x50: + return new ActionIncrement(); + case 0x4C: + return new ActionPushDuplicate(); + case 0x3E: + return new ActionReturn(); + case 0x4D: + return new ActionStackSwap(); + case 0x87: + return new ActionStoreRegister(actionLength, this); + //SWF6 Actions + case 0x54: + return new ActionInstanceOf(); + case 0x55: + return new ActionEnumerate2(); + case 0x66: + return new ActionStrictEquals(); + case 0x67: + return new ActionGreater(); + case 0x68: + return new ActionStringGreater(); + //SWF7 Actions + case 0x8E: + return new ActionDefineFunction2(actionLength, this, rri, version); + case 0x69: + return new ActionExtends(); + case 0x2B: + return new ActionCastOp(); + case 0x2C: + return new ActionImplementsOp(); + case 0x8F: + return new ActionTry(actionLength, this, rri, version); + case 0x2A: + return new ActionThrow(); + default: + /*if (actionLength > 0) { + //skip(actionLength); + }*/ + //throw new UnknownActionException(actionCode); + Action r = new ActionNop(); + r.actionCode = actionCode; + r.actionLength = actionLength; + return r; + //return new Action(actionCode, actionLength); + } + } catch (EndOfStreamException | ArrayIndexOutOfBoundsException eos) { return null; } - if (actionCode == 0) { - return new ActionEnd(); - } - if (actionCode == -1) { - return null; - } - int actionLength = 0; - if (actionCode >= 0x80) { - actionLength = readUI16(); - } - switch (actionCode) { - //SWF3 Actions - case 0x81: - return new ActionGotoFrame(this); - case 0x83: - return new ActionGetURL(actionLength, this, version); - case 0x04: - return new ActionNextFrame(); - case 0x05: - return new ActionPrevFrame(); - case 0x06: - return new ActionPlay(); - case 0x07: - return new ActionStop(); - case 0x08: - return new ActionToggleQuality(); - case 0x09: - return new ActionStopSounds(); - case 0x8A: - return new ActionWaitForFrame(this, cpool); - case 0x8B: - return new ActionSetTarget(actionLength, this, version); - case 0x8C: - return new ActionGoToLabel(actionLength, this, version); - //SWF4 Actions - case 0x96: - return new ActionPush(actionLength, this, version); - case 0x17: - return new ActionPop(); - case 0x0A: - return new ActionAdd(); - case 0x0B: - return new ActionSubtract(); - case 0x0C: - return new ActionMultiply(); - case 0x0D: - return new ActionDivide(); - case 0x0E: - return new ActionEquals(); - case 0x0F: - return new ActionLess(); - case 0x10: - return new ActionAnd(); - case 0x11: - return new ActionOr(); - case 0x12: - return new ActionNot(); - case 0x13: - return new ActionStringEquals(); - case 0x14: - return new ActionStringLength(); - case 0x21: - return new ActionStringAdd(); - case 0x15: - return new ActionStringExtract(); - case 0x29: - return new ActionStringLess(); - case 0x31: - return new ActionMBStringLength(); - case 0x35: - return new ActionMBStringExtract(); - case 0x18: - return new ActionToInteger(); - case 0x32: - return new ActionCharToAscii(); - case 0x33: - return new ActionAsciiToChar(); - case 0x36: - return new ActionMBCharToAscii(); - case 0x37: - return new ActionMBAsciiToChar(); - case 0x99: - return new ActionJump(this); - case 0x9D: - return new ActionIf(this); - case 0x9E: - return new ActionCall(); - case 0x1C: - return new ActionGetVariable(); - case 0x1D: - return new ActionSetVariable(); - case 0x9A: - return new ActionGetURL2(this); - case 0x9F: - return new ActionGotoFrame2(actionLength, this); - case 0x20: - return new ActionSetTarget2(); - case 0x22: - return new ActionGetProperty(); - case 0x23: - return new ActionSetProperty(); - case 0x24: - return new ActionCloneSprite(); - case 0x25: - return new ActionRemoveSprite(); - case 0x27: - return new ActionStartDrag(); - case 0x28: - return new ActionEndDrag(); - case 0x8D: - return new ActionWaitForFrame2(this, cpool); - case 0x26: - return new ActionTrace(); - case 0x34: - return new ActionGetTime(); - case 0x30: - return new ActionRandomNumber(); - //SWF5 Actions - case 0x3D: - return new ActionCallFunction(); - case 0x52: - return new ActionCallMethod(); - case 0x88: - return new ActionConstantPool(actionLength, this, version); - case 0x9B: - return new ActionDefineFunction(actionLength, this, rri, version); - case 0x3C: - return new ActionDefineLocal(); - case 0x41: - return new ActionDefineLocal2(); - case 0x3A: - return new ActionDelete(); - case 0x3B: - return new ActionDelete2(); - case 0x46: - return new ActionEnumerate(); - case 0x49: - return new ActionEquals2(); - case 0x4E: - return new ActionGetMember(); - case 0x42: - return new ActionInitArray(); - case 0x43: - return new ActionInitObject(); - case 0x53: - return new ActionNewMethod(); - case 0x40: - return new ActionNewObject(); - case 0x4F: - return new ActionSetMember(); - case 0x45: - return new ActionTargetPath(); - case 0x94: - return new ActionWith(this, rri, version); - case 0x4A: - return new ActionToNumber(); - case 0x4B: - return new ActionToString(); - case 0x44: - return new ActionTypeOf(); - case 0x47: - return new ActionAdd2(); - case 0x48: - return new ActionLess2(); - case 0x3F: - return new ActionModulo(); - case 0x60: - return new ActionBitAnd(); - case 0x63: - return new ActionBitLShift(); - case 0x61: - return new ActionBitOr(); - case 0x64: - return new ActionBitRShift(); - case 0x65: - return new ActionBitURShift(); - case 0x62: - return new ActionBitXor(); - case 0x51: - return new ActionDecrement(); - case 0x50: - return new ActionIncrement(); - case 0x4C: - return new ActionPushDuplicate(); - case 0x3E: - return new ActionReturn(); - case 0x4D: - return new ActionStackSwap(); - case 0x87: - return new ActionStoreRegister(this); - //SWF6 Actions - case 0x54: - return new ActionInstanceOf(); - case 0x55: - return new ActionEnumerate2(); - case 0x66: - return new ActionStrictEquals(); - case 0x67: - return new ActionGreater(); - case 0x68: - return new ActionStringGreater(); - //SWF7 Actions - case 0x8E: - return new ActionDefineFunction2(actionLength, this, rri, version); - case 0x69: - return new ActionExtends(); - case 0x2B: - return new ActionCastOp(); - case 0x2C: - return new ActionImplementsOp(); - case 0x8F: - return new ActionTry(actionLength, this, rri, version); - case 0x2A: - return new ActionThrow(); - default: - /*if (actionLength > 0) { - //skip(actionLength); - }*/ - //throw new UnknownActionException(actionCode); - Action r = new ActionNop(); - r.actionCode = actionCode; - r.actionLength = actionLength; - return r; - //return new Action(actionCode, actionLength); - } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/Action.java b/trunk/src/com/jpexs/decompiler/flash/action/Action.java index 4666ce378..e72a3c626 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/Action.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/Action.java @@ -203,7 +203,6 @@ public class Action implements GraphSourceItem { List part = a.getAllRefs(version); ret.addAll(part); if (a.afterInsert != null) { - a.afterInsert.setAddress(a.getAddress(), version, false); ret.addAll(a.afterInsert.getAllRefs(version)); } } @@ -1199,9 +1198,9 @@ public class Action implements GraphSourceItem { String s = null; try { s = Action.actionsToString(new ArrayList(), address, ret, null, version, false, false, swfPos, path); - ret = ASMParser.parse(address, swfPos, true, s, SWF.DEFAULT_VERSION); + ret = ASMParser.parse(address, swfPos, true, s, SWF.DEFAULT_VERSION, false); } catch (Exception ex) { - Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "parsing error", ex); + Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "parsing error. path: " + path, ex); } return ret; } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java new file mode 100644 index 000000000..c03de1b56 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.action; + +import com.jpexs.decompiler.flash.Configuration; +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.action.model.ConstantPool; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.special.ActionEnd; +import com.jpexs.decompiler.flash.action.special.ActionNop; +import com.jpexs.decompiler.flash.action.special.ActionStore; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; +import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.NotCompileTimeItem; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.ReReadableInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Scanner; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Class for reading data from SWF file + * + * @author JPEXS + */ +public class ActionListReader { + + private static final Logger log = Logger.getLogger(SWFInputStream.class.getName()); + + /** + * Reads list of actions from the stream. Reading ends with + * ActionEndFlag(=0) or end of the stream. + * + * @param listeners + * @param address + * @param ip + * @param rri + * @param version + * @param containerSWFOffset + * @param endip + * @param path + * @return List of actions + * @throws IOException + */ + public static List readActionList(List listeners, long containerSWFOffset, ReReadableInputStream rri, int version, int ip, int endIp, String path) throws IOException { + boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); + + ConstantPool cpool = new ConstantPool(); + + SWFInputStream sis = new SWFInputStream(rri, version); + + // List of the actions. Item 0 contains the first (entry) action + List actionMap = new ArrayList<>(); + List nextOffsets = new ArrayList<>(); + Action entryAction = readActionListAtPos(listeners, containerSWFOffset, cpool, + sis, rri, + actionMap, nextOffsets, + ip, ip, endIp, version, path, false, new ArrayList()); + + Map> containerLastActions = new HashMap<>(); + getContainerLastActions(actionMap, containerLastActions); + + List actions = new ArrayList<>(); + + // jump to the entry action when it is diffrent from the first action in the map + int index = getNextNotNullIndex(actionMap, 0); + if (index != -1 && entryAction != actionMap.get(index)) { + ActionJump jump = new ActionJump(0); + int size = getTotalActionLength(jump); + jump.setJumpOffset((int) (entryAction.getAddress() - size)); + actions.add(jump); + } + + // remove nulls + index = getNextNotNullIndex(actionMap, index); + while (index > -1) { + Action action = actionMap.get(index); + long nextOffset = nextOffsets.get(index); + int nextIndex = getNextNotNullIndex(actionMap, index + 1); + actions.add(action); + if (nextIndex != -1 && nextOffset != nextIndex) { + if (!action.isExit() && !(action instanceof ActionJump)) { + ActionJump jump = new ActionJump(0); + jump.setAddress(action.getAddress(), version); + int size = getTotalActionLength(jump); + jump.setJumpOffset((int) (nextOffset - action.getAddress() - size)); + actions.add(jump); + } + } + index = nextIndex; + } + + // Map for storing the targers of the "jump" actions + // "jump" action can be ActionIf, ActionJump and any ActionStore + Map jumps = new HashMap<>(); + getJumps(actions, jumps); + + long endAddress = updateAddresses(actions, ip, version); + updateJumps(actions, jumps, containerLastActions, endAddress, version); + updateActionStores(actions, jumps); + updateContainerSizes(actions, containerLastActions); + updateActionLengths(actions, version); + + // add end action + Action lastAction = actions.get(actions.size() - 1); + if (!(lastAction instanceof ActionEnd)) { + Action aEnd = new ActionEnd(); + aEnd.setAddress(endAddress, version); + actions.add(aEnd); + endAddress += getTotalActionLength(aEnd); + } + + if (deobfuscate) { + return deobfuscateActionList(listeners, containerSWFOffset, actions, version, 0, path); + } + + return actions; + } + + /** + * Reads list of actions from the stream. Reading ends with + * ActionEndFlag(=0) or end of the stream. + * + * @param listeners + * @param address + * @param ip + * @param rri + * @param version + * @param containerSWFOffset + * @param endip + * @param path + * @return List of actions + * @throws IOException + */ + public static List deobfuscateActionList(List listeners, long containerSWFOffset, List actions, int version, int ip, String path) throws IOException { + byte[] data = Action.actionsToBytes(actions, true, version); + ReReadableInputStream rri = new ReReadableInputStream(new ByteArrayInputStream(data)); + int endIp = data.length; + + List retdups = new ArrayList<>(); + for (int i = 0; i <= endIp; i++) { + Action a = new ActionNop(); + a.setAddress(i, version); + retdups.add(a); + } + ConstantPool cpool = new ConstantPool(); + + Stack stack = new Stack<>(); + + List localData = Helper.toList(new HashMap(), new HashMap(), new HashMap()); + + SWFInputStream sis = new SWFInputStream(rri, version); + deobfustaceActionListAtPosRecursive(listeners, new ArrayList(), new HashMap>(), containerSWFOffset, localData, stack, cpool, sis, rri, ip, retdups, ip, endIp, path, new HashMap(), false, new HashMap>(), version); + + if (!retdups.isEmpty()) { + for (int i = 0; i < ip; i++) { + retdups.remove(0); + } + } + List ret = new ArrayList<>(); + Action last = null; + for (Action a : retdups) { + if (a != last) { + ret.add(a); + } + last = a; + } + for (int i = 0; i < retdups.size(); i++) { + Action a = retdups.get(i); + if (a instanceof ActionEnd) { + if (i < retdups.size() - 1) { + ActionJump jmp = new ActionJump(0); + jmp.setJumpOffset(retdups.size() - i - jmp.getBytes(version).length); + a.replaceWith = jmp; + } + } + } + + if (Configuration.getConfig("removeNops", true)) { + ret = Action.removeNops(0, ret, version, 0, path); + } + List reta = new ArrayList<>(); + for (Object o : ret) { + if (o instanceof Action) { + reta.add((Action) o); + } + } + return reta; + } + + private static int getPrevNotNullIndex(List actionMap, int startIndex) { + startIndex = Math.min(startIndex, actionMap.size() - 1); + for (int i = startIndex; i >= 0; i--) { + if (actionMap.get(i) != null) { + return i; + } + } + return -1; + } + + private static int getNextNotNullIndex(List actionMap, int startIndex) { + for (int i = startIndex; i < actionMap.size(); i++) { + if (actionMap.get(i) != null) { + return i; + } + } + return -1; + } + + private static Map actionListToMap(List actions) { + Map map = new HashMap<>(actions.size()); + for (Action a : actions) { + long address = a.getAddress(); + // There are multiple action in the same address (2nd action is a jump for deobfuscated code) + // So this check is required + if (!map.containsKey(address)) { + map.put(a.getAddress(), a); + } + } + return map; + } + + private static void getJumps(List actions, Map jumps) { + Map actionMap = actionListToMap(actions); + for (Action a : actions) { + long target = -1; + if (a instanceof ActionIf) { + ActionIf aIf = (ActionIf) a; + target = aIf.getAddress() + getTotalActionLength(a) + aIf.getJumpOffset(); + } else if (a instanceof ActionJump) { + ActionJump aJump = (ActionJump) a; + target = aJump.getAddress() + getTotalActionLength(a) + aJump.getJumpOffset(); + } else if (a instanceof ActionStore) { + ActionStore aStore = (ActionStore) a; + int storeSize = aStore.getStoreSize(); + // skip storeSize + 1 actions (+1 is the current action) + Action targetAction = a; + for (int i = 0; i <= storeSize; i++) { + long address = targetAction.getAddress() + getTotalActionLength(targetAction); + targetAction = actionMap.get(address); + if (targetAction == null) { + break; + } + } + jumps.put(a, targetAction); + } + if (target >= 0) { + Action targetAction = actionMap.get(target); + jumps.put(a, targetAction); + } + } + } + + private static void getContainerLastActions(List actionMap, Map> lastActions) { + for (int i = 0; i < actionMap.size(); i++) { + Action a = actionMap.get(i); + if (a == null) { + continue; + } + + if (a instanceof GraphSourceItemContainer) { + GraphSourceItemContainer container = (GraphSourceItemContainer) a; + List sizes = container.getContainerSizes(); + long endAddress = a.getAddress() + container.getHeaderSize(); + List lasts = new ArrayList<>(sizes.size()); + for (long size : sizes) { + endAddress += size; + int lastActionIndex = getPrevNotNullIndex(actionMap, (int) (endAddress - 1)); + Action lastAction = null; + if (lastActionIndex != -1) { + lastAction = actionMap.get(lastActionIndex); + } + lasts.add(lastAction); + } + lastActions.put(a, lasts); + } + } + } + + private static long updateAddresses(List actions, long address, int version) { + for (int i = 0; i < actions.size(); i++) { + Action a = actions.get(i); + a.setAddress(address, version); + int length = a.getBytes(version).length; + //a.actionLength = length - 1 - ((a.actionCode >= 0x80) ? 2 : 0); + if ((i != actions.size() - 1) && (a instanceof ActionEnd)) { + // placeholder for jump action + length = getTotalActionLength(new ActionJump(0)); + } + address += length; + } + return address; + } + + private static void updateActionLengths(List actions, int version) { + for (int i = 0; i < actions.size(); i++) { + Action a = actions.get(i); + int length = a.getBytes(version).length; + a.actionLength = length - 1 - ((a.actionCode >= 0x80) ? 2 : 0); + } + } + + private static void updateActionStores(List actions, Map jumps) { + Map actionMap = actionListToMap(actions); + for (int i = 0; i < actions.size(); i++) { + Action a = actions.get(i); + if (a instanceof ActionStore) { + ActionStore aStore = (ActionStore) a; + Action nextActionAfterStore = jumps.get(a); + Action a1 = a; + List store = new ArrayList<>(); + while (true) { + long address = a1.getAddress() + getTotalActionLength(a1); + a1 = actionMap.get(address); + if (a1 == null || a1 == nextActionAfterStore) { + break; + } + actions.remove(a1); + store.add(a1); + } + aStore.setStore(store); + } + } + } + + private static void updateContainerSizes(List actions, Map> containerLastActions) { + for (int i = 0; i < actions.size(); i++) { + Action a = actions.get(i); + if (a instanceof GraphSourceItemContainer) { + GraphSourceItemContainer container = (GraphSourceItemContainer) a; + List lastActions = containerLastActions.get(a); + long startAddress = a.getAddress() + container.getHeaderSize(); + for (int j = 0; j < lastActions.size(); j++) { + Action lastAction = lastActions.get(j); + int length = (int) (lastAction.getAddress() + getTotalActionLength(lastAction) - startAddress); + container.setContainerSize(j, length); + startAddress += length; + } + } + } + } + + private static void replaceJumpTargets(Map jumps, Action oldTarget, Action newTarget) { + for (Action a : jumps.keySet()) { + if (jumps.get(a) == oldTarget) { + jumps.put(a, newTarget); + } + } + } + + private static void replaceContainerLastActions(Map> containerLastActions, Action oldTarget, Action newTarget) { + for (Action a : containerLastActions.keySet()) { + List targets = containerLastActions.get(a); + for (int i = 0; i < targets.size(); i++) { + if (targets.get(i) == oldTarget) { + targets.set(i, newTarget); + } + } + } + } + + private static void updateJumps(List actions, Map jumps, Map> containerLastActions, long endAddress, int version) { + if (actions.isEmpty()) { + return; + } + + for (int i = 0; i < actions.size(); i++) { + Action a = actions.get(i); + if ((i != actions.size() - 1) && (a instanceof ActionEnd)) { + ActionJump aJump = new ActionJump(0); + aJump.setJumpOffset((int) (endAddress - a.getAddress() - getTotalActionLength(aJump))); + aJump.setAddress(a.getAddress(), version); + replaceJumpTargets(jumps, a, aJump); + replaceContainerLastActions(containerLastActions, a, aJump); + a = aJump; + actions.set(i, a); + } else if (a instanceof ActionIf) { + ActionIf aIf = (ActionIf) a; + Action target = jumps.get(a); + long offset; + if (target != null) { + offset = target.getAddress() - a.getAddress() - getTotalActionLength(a); + } else { + offset = endAddress - a.getAddress() - getTotalActionLength(a); + } + aIf.setJumpOffset((int) offset); + } else if (a instanceof ActionJump) { + ActionJump aJump = (ActionJump) a; + Action target = jumps.get(a); + long offset; + if (target != null) { + offset = target.getAddress() - a.getAddress() - getTotalActionLength(a); + } else { + offset = endAddress - a.getAddress() - getTotalActionLength(a); + } + aJump.setJumpOffset((int) offset); + } + } + } + + @SuppressWarnings("unchecked") + private static Action readActionListAtPos(List listeners, long containerSWFOffset, ConstantPool cpool, + SWFInputStream sis, ReReadableInputStream rri, + List actions, List nextOffsets, + long ip, long startIp, long endIp, int version, String path, boolean indeterminate, List visitedContainers) throws IOException { + + Action entryAction = null; + + if (visitedContainers.contains(ip)) { + return null; + } + visitedContainers.add(ip); + + Queue jumpQueue = new LinkedList<>(); + jumpQueue.add(ip); + while (!jumpQueue.isEmpty()) { + ip = jumpQueue.remove(); + while ((endIp == -1) || (endIp > ip)) { + rri.setPos((int) ip); + + Action a; + if ((a = sis.readAction(rri, cpool)) == null) { + break; + } + + int actionLengthWithHeader = getTotalActionLength(a); + + // unknown action, replace with jump + if (a instanceof ActionNop) { + ActionJump aJump = new ActionJump(0); + int jumpLength = getTotalActionLength(aJump); + aJump.setAddress(a.getAddress(), version); + aJump.setJumpOffset(actionLengthWithHeader - jumpLength); + a = aJump; + actionLengthWithHeader = getTotalActionLength(a); + } + + if (entryAction == null) { + entryAction = a; + } + + ensureCapacity(actions, nextOffsets, ip); + + Action existingAction = actions.get((int) ip); + if (existingAction != null) { + break; + } + + actions.set((int) ip, a); + nextOffsets.set((int) ip, ip + actionLengthWithHeader); + + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).progress("Reading", rri.getCount(), rri.length()); + } + + a.containerSWFOffset = containerSWFOffset; + a.setAddress(ip, version, false); + + if (a instanceof ActionPush && cpool != null) { + ((ActionPush) a).constantPool = cpool.constants; + } else if (a instanceof ActionConstantPool) { + if (cpool == null) { + cpool = new ConstantPool(); + } + cpool.setNew(((ActionConstantPool) a).constantPool); + } else if (a instanceof ActionIf) { + ActionIf aIf = (ActionIf) a; + long nIp = ip + actionLengthWithHeader + aIf.getJumpOffset(); + if (nIp >= 0) { + jumpQueue.add(nIp); + } + } else if (a instanceof ActionJump) { + ActionJump aJump = (ActionJump) a; + long nIp = ip + actionLengthWithHeader + aJump.getJumpOffset(); + if (nIp >= 0) { + jumpQueue.add(nIp); + } + break; + } else if (a instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; + String cntName = cnt.getName(); + String newPath = path + (cntName == null ? "" : "/" + cntName); + for (long size : cnt.getContainerSizes()) { + if (size != 0) { + long ip2 = ip + actionLengthWithHeader; + //long endIp2 = ip + actionLengthWithHeader + size; + readActionListAtPos(listeners, containerSWFOffset, cpool, + sis, rri, + actions, nextOffsets, + ip2, startIp, endIp, version, newPath, indeterminate, visitedContainers); + actionLengthWithHeader += size; + } + } + } + + ip = ip + actionLengthWithHeader; + + if (a.isExit()) { + break; + } + } + } + return entryAction; + } + + private static int getTotalActionLength(Action action) { + return action.actionLength + 1 + ((action.actionCode >= 0x80) ? 2 : 0); + } + + private static void ensureCapacity(List actions, List nextOffsets, long index) { + while (actions.size() <= index) { + actions.add(null); + nextOffsets.add(-1L); + } + } + + @SuppressWarnings("unchecked") + private static void deobfustaceActionListAtPosRecursive(List listeners, List output, HashMap> containers, long containerSWFOffset, List localData, Stack stack, ConstantPool cpool, SWFInputStream sis, ReReadableInputStream rri, int ip, List ret, int startIp, int endip, String path, Map visited, boolean indeterminate, Map> decisionStates, int version) throws IOException { + boolean debugMode = false; + boolean decideBranch = false; + + boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); + rri.setPos(ip); + Action a; + long filePos = rri.getPos(); + Scanner sc = new Scanner(System.in, "utf-8"); + int prevIp = ip; + loopip: + while (((endip == -1) || (endip > ip)) && (a = sis.readAction(rri, cpool)) != null) { + if (!visited.containsKey(ip)) { + visited.put(ip, 0); + } + int curVisited = visited.get(ip); + curVisited++; + visited.put(ip, curVisited); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).progress("Deobfuscating", rri.getCount(), rri.length()); + } + if ((ip < ret.size()) && (!(ret.get(ip) instanceof ActionNop))) { + a = ret.get(ip); + } + a.containerSWFOffset = containerSWFOffset; + a.setAddress(prevIp, version, false); + int info = a.actionLength + 1 + ((a.actionCode >= 0x80) ? 2 : 0); + + if (a instanceof ActionPush) { + if (cpool != null) { + ((ActionPush) a).constantPool = cpool.constants; + } + } + + if (debugMode) { + String atos = a.getASMSource(new ArrayList(), new ArrayList(), cpool.constants, version, false); + if (a instanceof GraphSourceItemContainer) { + atos = a.toString(); + } + System.err.println("readActionListAtPos ip: " + (ip - startIp) + " (0x" + Helper.formatAddress(ip - startIp) + ") " + " action(len " + a.actionLength + "): " + atos + (a.isIgnored() ? " (ignored)" : "") + " stack:" + Helper.stackToString(stack, Helper.toList(cpool)) + " " + Helper.byteArrToString(a.getBytes(version))); + @SuppressWarnings("unchecked") + HashMap vars = (HashMap) localData.get(1); + System.err.print("variables: "); + for (Map.Entry v : vars.entrySet()) { + System.err.print("'" + v + "' = " + v.getValue().toString(false, cpool) + ", "); + } + System.err.println(); + String add = ""; + if (a instanceof ActionIf) { + add = " change: " + ((ActionIf) a).getJumpOffset(); + } + if (a instanceof ActionJump) { + add = " change: " + ((ActionJump) a).getJumpOffset(); + } + System.err.println(add); + } + long newFilePos = rri.getPos(); + long actionLen = newFilePos - filePos; + + int newip = -1; + + if (a instanceof ActionConstantPool) { + if (cpool == null) { + cpool = new ConstantPool(); + } + cpool.setNew(((ActionConstantPool) a).constantPool); + } + ActionIf aif = null; + boolean goaif = false; + if (!a.isIgnored()) { + String varname = null; + if (a instanceof StoreTypeAction) { + StoreTypeAction sta = (StoreTypeAction) a; + varname = sta.getVariableName(stack, cpool); + } + + try { + if (a instanceof ActionIf) { + aif = (ActionIf) a; + + GraphTargetItem top = null; + if (deobfuscate) { + top = stack.pop(); + } + int nip = rri.getPos() + aif.getJumpOffset(); + + if (decideBranch) { + System.out.print("newip " + nip + ", "); + System.out.print("Action: jump(j),ignore(i),compute(c)?"); + String next = sc.next(); + if (next.equals("j")) { + newip = rri.getPos() + aif.getJumpOffset(); + rri.setPos(newip); + + } else if (next.equals("i")) { + } else if (next.equals("c")) { + goaif = true; + } + } else if (deobfuscate && top.isCompileTime() && (!top.hasSideEffect())) { + ((ActionIf) a).compileTime = true; + if (debugMode) { + System.err.print("is compiletime -> "); + } + if (EcmaScript.toBoolean(top.getResult())) { + newip = rri.getPos() + aif.getJumpOffset(); + aif.jumpUsed = true; + if (aif.ignoreUsed) { + aif.compileTime = false; + } + if (debugMode) { + System.err.println("jump"); + } + } else { + aif.ignoreUsed = true; + if (aif.jumpUsed) { + aif.compileTime = false; + } + if (debugMode) { + System.err.println("ignore"); + } + } + } else { + if (debugMode) { + System.err.println("goaif"); + } + goaif = true; + } + } else if (a instanceof ActionJump) { + newip = rri.getPos() + ((ActionJump) a).getJumpOffset(); + } else if (!(a instanceof GraphSourceItemContainer)) { + if (deobfuscate) { + //return in for..in, TODO:Handle this better way + if (((a instanceof ActionEquals) || (a instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { + stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); + } + if ((a instanceof ActionStoreRegister) && stack.isEmpty()) { + stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); + } + a.translate(localData, stack, output, Graph.SOP_USE_STATIC/*Graph.SOP_SKIP_STATIC*/, path); + } + } + } catch (RuntimeException ex) { + log.log(Level.SEVERE, "Disassembly exception", ex); + break; + } + + HashMap vars = (HashMap) localData.get(1); + if (varname != null) { + GraphTargetItem varval = vars.get(varname); + if (varval != null && varval.isCompileTime() && indeterminate) { + vars.put(varname, new NotCompileTimeItem(null, varval)); + } + } + } + int nopos = -1; + for (int i = 0; i < actionLen; i++) { + if (a instanceof ActionNop) { + int prevPos = (int) a.getAddress(); + a = new ActionNop(); + a.setAddress(prevPos, version); + nopos++; + if (nopos > 0) { + a.setAddress(a.getAddress() + 1, version); + } + + } + ret.set(ip + i, a); + } + + if (a instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; + if (a instanceof Action) { + long endAddr = a.getAddress() + cnt.getHeaderSize(); + String cntName = cnt.getName(); + List> output2s = new ArrayList<>(); + for (long size : cnt.getContainerSizes()) { + if (size == 0) { + output2s.add(new ArrayList()); + continue; + } + List localData2; + List output2 = new ArrayList<>(); + if ((cnt instanceof ActionDefineFunction) || (cnt instanceof ActionDefineFunction2)) { + localData2 = Helper.toList(new HashMap(), new HashMap(), new HashMap()); + } else { + localData2 = localData; + } + deobfustaceActionListAtPosRecursive(listeners, output2, containers, containerSWFOffset, localData2, new Stack(), cpool, sis, rri, (int) endAddr, ret, startIp, (int) (endAddr + size), path + (cntName == null ? "" : "/" + cntName), visited, indeterminate, decisionStates, version); + output2s.add(output2); + endAddr += size; + } + if (deobfuscate) { + cnt.translateContainer(output2s, stack, output, (HashMap) localData.get(0), (HashMap) localData.get(1), (HashMap) localData.get(2)); + } + ip = (int) endAddr; + prevIp = ip; + rri.setPos(ip); + filePos = rri.getPos(); + continue; + } + } + + if (a instanceof ActionEnd) { + break; + } + if (newip > -1) { + ip = newip; + } else { + ip = ip + info; + } + rri.setPos(ip); + filePos = rri.getPos(); + if (goaif) { + aif.ignoreUsed = true; + aif.jumpUsed = true; + indeterminate = true; + + HashMap vars = (HashMap) localData.get(1); + boolean stateChanged = false; + if (decisionStates.containsKey(ip)) { + HashMap oldstate = decisionStates.get(ip); + if (oldstate.size() != vars.size()) { + stateChanged = true; + } else { + for (String k : vars.keySet()) { + if (!oldstate.containsKey(k)) { + stateChanged = true; + break; + } + if (!vars.get(k).isCompileTime() && oldstate.get(k).isCompileTime()) { + stateChanged = true; + break; + } + } + } + } + HashMap curstate = new HashMap<>(); + curstate.putAll(vars); + decisionStates.put(ip, curstate); + + if ((!stateChanged) && curVisited > 1) { + List branches = new ArrayList<>(); + branches.add(rri.getPos() + aif.getJumpOffset()); + branches.add(rri.getPos()); + for (int br : branches) { + int visc = 0; + if (visited.containsKey(br)) { + visc = visited.get(br); + } + if (visc == 0) {// substack = (Stack) stack.clone(); + deobfustaceActionListAtPosRecursive(listeners, output, containers, containerSWFOffset, prepareLocalBranch(localData), substack, cpool, sis, rri, rri.getPos() + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version); + rri.setPos(oldPos); + } + prevIp = ip; + if (a.isExit()) { + break; + } + } + for (DisassemblyListener listener : listeners) { + listener.progress("Deobfuscating", rri.getCount(), rri.length()); + } + } + + private static List prepareLocalBranch(List localData) { + @SuppressWarnings("unchecked") + HashMap regNames = (HashMap) localData.get(0); + @SuppressWarnings("unchecked") + HashMap variables = (HashMap) localData.get(1); + @SuppressWarnings("unchecked") + HashMap functions = (HashMap) localData.get(2); + List ret = new ArrayList<>(); + ret.add(new HashMap<>(regNames)); + ret.add(new HashMap<>(variables)); + ret.add(new HashMap<>(functions)); + return ret; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/action/model/ConstantPool.java b/trunk/src/com/jpexs/decompiler/flash/action/model/ConstantPool.java index 5e439ef23..73d26c7e2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/model/ConstantPool.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/model/ConstantPool.java @@ -21,10 +21,7 @@ import java.util.List; public class ConstantPool { - public List> archive = new ArrayList<>(); - public List archiveCounts = new ArrayList<>(); public List constants = new ArrayList<>(); - public int count; public ConstantPool() { } @@ -34,31 +31,15 @@ public class ConstantPool { } public void setNew(List constants) { - archive.add(this.constants); this.constants = constants; - archiveCounts.add(count); - count = 0; } @Override public String toString() { - return "" + count + "x " + constants.toString(); + return "x " + constants.toString(); } public boolean isEmpty() { return constants.isEmpty(); } - - public void getLastUsed() { - if (count > 0) { - return; - } - for (int i = archive.size() - 1; i >= 0; i--) { - if (archiveCounts.get(i) > 0) { - count = archiveCounts.get(i); - constants = archive.get(i); - break; - } - } - } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java b/trunk/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java index 8f10ebe59..3b9cf372c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java @@ -377,7 +377,7 @@ public class ASMParser { } } - public static List parse(long address, long containerSWFOffset, boolean ignoreNops, String source, int version) throws IOException, ParseException { + public static List parse(long address, long containerSWFOffset, boolean ignoreNops, String source, int version, boolean throwOnError) throws IOException, ParseException { FlasmLexer lexer = new FlasmLexer(new StringReader(source)); List list = parseAllActions(lexer, version); @@ -421,7 +421,11 @@ public class ASMParser { } if ((link instanceof ActionJump) || (link instanceof ActionIf)) { if (!found) { - Logger.getLogger(ASMParser.class.getName()).log(Level.SEVERE, "TARGET NOT FOUND - identifier:" + identifier + " addr: ofs" + Helper.formatAddress(link.getAddress())); + if (throwOnError) { + throw new ParseException("TARGET NOT FOUND - identifier:" + identifier + " addr: ofs" + Helper.formatAddress(link.getAddress()), -1); + } else { + Logger.getLogger(ASMParser.class.getName()).log(Level.SEVERE, "TARGET NOT FOUND - identifier:" + identifier + " addr: ofs" + Helper.formatAddress(link.getAddress())); + } } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionGotoFrame.java b/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionGotoFrame.java index a56fc5442..591b7be38 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionGotoFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionGotoFrame.java @@ -38,8 +38,8 @@ public class ActionGotoFrame extends Action { this.frame = frame; } - public ActionGotoFrame(SWFInputStream sis) throws IOException { - super(0x81, 2); + public ActionGotoFrame(int actionLength, SWFInputStream sis) throws IOException { + super(0x81, actionLength); frame = sis.readUI16(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java b/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java index ff0381351..a1b58e0cd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java @@ -30,10 +30,8 @@ import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.action.special.ActionEnd; import com.jpexs.decompiler.flash.action.special.ActionStore; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -47,12 +45,12 @@ public class ActionWaitForFrame extends Action implements ActionStore { public int skipCount; public List skipped; - public ActionWaitForFrame(SWFInputStream sis, ConstantPool cpool) throws IOException { - super(0x8A, 3); + public ActionWaitForFrame(int actionLength, SWFInputStream sis, ConstantPool cpool) throws IOException { + super(0x8A, actionLength); frame = sis.readUI16(); skipCount = sis.readUI8(); skipped = new ArrayList<>(); - for (int i = 0; i < skipCount; i++) { + /*for (int i = 0; i < skipCount; i++) { Action a = sis.readAction(cpool); if (a instanceof ActionEnd) { skipCount = i; @@ -65,7 +63,6 @@ public class ActionWaitForFrame extends Action implements ActionStore { if (a instanceof ActionPush) { if (cpool != null) { ((ActionPush) a).constantPool = cpool.constants; - cpool.count++; } } skipped.add(a); @@ -85,7 +82,7 @@ public class ActionWaitForFrame extends Action implements ActionStore { } } skipCount = skipped.size(); - } + }*/ } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionCall.java b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionCall.java index ec3735d4a..94bc14253 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionCall.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionCall.java @@ -29,6 +29,10 @@ public class ActionCall extends Action { super(0x9E, 0); } + public ActionCall(int actionLength) { + super(0x9E, actionLength); + } + @Override public String toString() { return "Call"; diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionGetURL2.java b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionGetURL2.java index 48effecb2..2394fec5c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionGetURL2.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionGetURL2.java @@ -56,8 +56,8 @@ public class ActionGetURL2 extends Action { this.sendVarsMethod = sendVarsMethod; } - public ActionGetURL2(SWFInputStream sis) throws IOException { - super(0x9A, 1); + public ActionGetURL2(int actionLength, SWFInputStream sis) throws IOException { + super(0x9A, actionLength); loadVariablesFlag = sis.readUB(1) == 1; loadTargetFlag = sis.readUB(1) == 1; sis.readUB(4); //reserved diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java index c8bfc0df4..7921667d0 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java @@ -53,8 +53,8 @@ public class ActionIf extends Action { setJumpOffset(offset); } - public ActionIf(SWFInputStream sis) throws IOException { - super(0x9D, 2); + public ActionIf(int actionLength, SWFInputStream sis) throws IOException { + super(0x9D, actionLength); setJumpOffset(sis.readSI16()); } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java index 0601c9fe4..16a97a6b0 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java @@ -52,8 +52,8 @@ public class ActionJump extends Action { setJumpOffset(offset); } - public ActionJump(SWFInputStream sis) throws IOException { - super(0x99, 2); + public ActionJump(int actionLength, SWFInputStream sis) throws IOException { + super(0x99, actionLength); setJumpOffset(sis.readSI16()); } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java index d9d2031bc..52b8d3313 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java @@ -47,6 +47,7 @@ public class ActionWaitForFrame2 extends Action implements ActionStore { public ActionWaitForFrame2(int skipCount) { super(0x8D, 1); this.skipCount = skipCount; + skipped = new ArrayList<>(); } @Override @@ -60,11 +61,11 @@ public class ActionWaitForFrame2 extends Action implements ActionStore { skipCount = store.size(); } - public ActionWaitForFrame2(SWFInputStream sis, ConstantPool cpool) throws IOException { - super(0x8D, 1); + public ActionWaitForFrame2(int actionLength, SWFInputStream sis, ConstantPool cpool) throws IOException { + super(0x8D, actionLength); skipCount = sis.readUI8(); skipped = new ArrayList<>(); - for (int i = 0; i < skipCount; i++) { + /*for (int i = 0; i < skipCount; i++) { Action a = sis.readAction(cpool); if (a instanceof ActionEnd) { skipCount = i; @@ -91,12 +92,13 @@ public class ActionWaitForFrame2 extends Action implements ActionStore { } } skipCount = skipped.size(); - } + }*/ } public ActionWaitForFrame2(FlasmLexer lexer) throws IOException, ParseException { super(0x8D, -1); skipCount = (int) lexLong(lexer); + skipped = new ArrayList<>(); } @Override 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 76beeb76a..7eeaa311d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java @@ -227,6 +227,15 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta return ret; } + @Override + public void setContainerSize(int index, long size) { + if (index == 0) { + codeSize = (int) size; + } else { + throw new IllegalArgumentException("Index must be 0."); + } + } + @Override public String getASMSourceBetween(int pos) { return ""; diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java index 0e85812b2..bed41580d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java @@ -48,8 +48,8 @@ public class ActionStoreRegister extends Action implements StoreTypeAction { this.registerNumber = registerNumber; } - public ActionStoreRegister(SWFInputStream sis) throws IOException { - super(0x87, 1); + public ActionStoreRegister(int actionLength, SWFInputStream sis) throws IOException { + super(0x87, actionLength); registerNumber = sis.readUI8(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java index a55b68c0a..b79aab72d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java @@ -50,8 +50,8 @@ public class ActionWith extends Action implements GraphSourceItemContainer { return false; } - public ActionWith(SWFInputStream sis, ReReadableInputStream rri, int version) throws IOException { - super(0x94, 2); + public ActionWith(int actionLength, SWFInputStream sis, ReReadableInputStream rri, int version) throws IOException { + super(0x94, actionLength); codeSize = sis.readUI16(); this.version = version; } @@ -107,6 +107,16 @@ public class ActionWith extends Action implements GraphSourceItemContainer { return ret; } + @Override + public void setContainerSize(int index, long size) { + if (index == 0) { + codeSize = (int) size; + } + else { + throw new IllegalArgumentException("Index must be 0."); + } + } + @Override public String getASMSourceBetween(int pos) { return ""; 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 70c28a806..da4fbceb8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java @@ -362,6 +362,15 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont return ret; } + @Override + public void setContainerSize(int index, long size) { + if (index == 0) { + codeSize = (int) size; + } else { + throw new IllegalArgumentException("Index must be 0."); + } + } + @Override public boolean parseDivision(long size, FlasmLexer lexer) { codeSize = (int) (size - getHeaderSize()); diff --git a/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java b/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java index 0abb59827..2b0a78efe 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java @@ -206,6 +206,10 @@ public class ActionTry extends Action implements GraphSourceItemContainer { return surroundWithAction(baos.toByteArray(), version).length; } + public long getTrySize() { + return trySize; + } + @Override public List getContainerSizes() { List ret = new ArrayList<>(); @@ -215,6 +219,23 @@ public class ActionTry extends Action implements GraphSourceItemContainer { return ret; } + @Override + public void setContainerSize(int index, long size) { + switch (index) { + case 0: + trySize = size; + break; + case 1: + catchSize = size; + break; + case 2: + finallySize = size; + break; + default: + throw new IllegalArgumentException("Valid indexes are 0, 1, and 2."); + } + } + @Override public boolean parseDivision(long size, FlasmLexer lexer) { try { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index d24c8c72e..3f362e16b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -3236,7 +3236,7 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel + "Push \"start\"\n" + "CallMethod\n" + "Pop\n" - + "Stop", SWF.DEFAULT_VERSION); + + "Stop", SWF.DEFAULT_VERSION, false); doa.setActions(actions, SWF.DEFAULT_VERSION); sos2.writeTag(doa); sos2.writeTag(new ShowFrameTag(null)); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index d8681ea09..96bba5c30 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -49,13 +49,14 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -89,6 +90,7 @@ public class ActionPanel extends JPanel implements ActionListener { public JButton saveDecompiledButton = new JButton(translate("button.save"), View.getIcon("save16")); public JButton cancelDecompiledButton = new JButton(translate("button.cancel"), View.getIcon("cancel16")); public JToggleButton hexButton; + public JToggleButton hexOnlyButton; public JLabel asmLabel = new HeaderLabel(translate("panel.disassembled")); public JLabel decLabel = new HeaderLabel(translate("panel.decompiled")); public List decompiledHilights = new ArrayList<>(); @@ -102,6 +104,7 @@ public class ActionPanel extends JPanel implements ActionListener { public JPanel topButtonsPan; private String srcWithHex; private String srcNoHex; + private String srcHexOnly; private String lastDecompiled = ""; private ASMSource lastASM; public JPanel searchPanel; @@ -272,8 +275,8 @@ public class ActionPanel extends JPanel implements ActionListener { } - public void setHex(boolean hex) { - setText(hex ? srcWithHex : srcNoHex); + public void setHex(boolean hex, boolean hexOnly) { + setText(hex ? srcWithHex : (hexOnly ? srcHexOnly : srcNoHex)); } public void setSource(ASMSource src, final boolean useCache) { @@ -309,7 +312,8 @@ public class ActionPanel extends JPanel implements ActionListener { asm.removeDisassemblyListener(listener); srcWithHex = Helper.hexToComments(lastDisasm); srcNoHex = Helper.stripComments(lastDisasm); - setHex(hexButton.isSelected()); + srcHexOnly = getHexText(srcWithHex); + setHex(hexButton.isSelected(), hexOnlyButton.isSelected()); if (Configuration.getConfig("decompile", true)) { decompiledEditor.setText("//" + translate("work.decompiling") + "..."); String stripped = ""; @@ -378,10 +382,19 @@ public class ActionPanel extends JPanel implements ActionListener { hexButton.setToolTipText(translate("button.viewhex")); hexButton.setMargin(new Insets(3, 3, 3, 3)); + // todo: find icon, and set visible + hexOnlyButton = new JToggleButton(View.getIcon("hex16")); + hexOnlyButton.setActionCommand("HEXONLY"); + hexOnlyButton.addActionListener(this); + hexOnlyButton.setToolTipText(translate("button.viewhex")); + hexOnlyButton.setMargin(new Insets(3, 3, 3, 3)); + hexOnlyButton.setVisible(false); + topButtonsPan = new JPanel(); topButtonsPan.setLayout(new BoxLayout(topButtonsPan, BoxLayout.X_AXIS)); topButtonsPan.add(graphButton); topButtonsPan.add(hexButton); + topButtonsPan.add(hexOnlyButton); JPanel panCode = new JPanel(new BorderLayout()); panCode.add(new JScrollPane(editor), BorderLayout.CENTER); panCode.add(topButtonsPan, BorderLayout.NORTH); @@ -548,8 +561,11 @@ public class ActionPanel extends JPanel implements ActionListener { } public void setEditMode(boolean val) { + // todo: create UI for editing raw data + final boolean rawEdit = false; + if (val) { - setText(srcNoHex); + setText(rawEdit ? srcHexOnly : srcNoHex); editor.setEditable(true); saveButton.setVisible(true); editButton.setVisible(false); @@ -640,13 +656,19 @@ public class ActionPanel extends JPanel implements ActionListener { } else if (e.getActionCommand().equals("EDITACTION")) { setEditMode(true); } else if (e.getActionCommand().equals("HEX")) { - setHex(hexButton.isSelected()); + setHex(hexButton.isSelected(), hexOnlyButton.isSelected()); } else if (e.getActionCommand().equals("CANCELACTION")) { setEditMode(false); - setHex(hexButton.isSelected()); + setHex(hexButton.isSelected(), hexOnlyButton.isSelected()); } else if (e.getActionCommand().equals("SAVEACTION")) { try { - src.setActions(ASMParser.parse(0, src.getPos(), true, editor.getText(), SWF.DEFAULT_VERSION), SWF.DEFAULT_VERSION); + String text = editor.getText(); + if (text.trim().startsWith("#hexdata")) { + src.setActionBytes(getBytesFromHexaText(text)); + } + else { + src.setActions(ASMParser.parse(0, src.getPos(), true, text, SWF.DEFAULT_VERSION, false), SWF.DEFAULT_VERSION); + } setSource(this.src, false); View.showMessageDialog(this, translate("message.action.saved")); saveButton.setVisible(false); @@ -678,6 +700,53 @@ public class ActionPanel extends JPanel implements ActionListener { } } + public byte[] getBytesFromHexaText(String text) { + Scanner scanner = new Scanner(text); + scanner.nextLine(); // ignore first line + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.startsWith(";")) { + continue; + } + line = line.replace(" ", ""); + for (int i = 0; i < line.length() / 2; i++) { + String hexStr = line.substring(i * 2, (i + 1) * 2); + byte b = (byte)Integer.parseInt(hexStr, 16); + baos.write(b); + } + } + byte[] data = baos.toByteArray(); + return data; + } + + private String getHexText(String srcWithHex) { + StringBuilder result = new StringBuilder(); + String nl = System.getProperty("line.separator"); + result.append("#hexdata").append(nl); + + /* // hex data from decompiled actions + Scanner scanner = new Scanner(srcWithHex); + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.startsWith(";")) { + result.append(line.substring(1).trim()).append(nl); + } else { + result.append(";").append(line).append(nl); + } + }*/ + + byte[] data = src.getActionBytes(); + for (int i = 0; i < data.length; i++) { + if (i % 8 == 0) { + result.append(nl); + } + result.append(String.format("%02x ", data[i])); + } + + return result.toString(); + } + public void updateSearchPos() { searchPos.setText((foundPos + 1) + "/" + found.size()); setSource(found.get(foundPos), true); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 026453feb..27dd534f9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.abc.CopyOutputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.BoundedTag; import com.jpexs.decompiler.flash.tags.base.ButtonTag; @@ -169,11 +170,7 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT ReReadableInputStream rri = new ReReadableInputStream(new ByteArrayInputStream(baos.toByteArray())); rri.setPos(prevLength); - boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); - List list = SWFInputStream.readActionList(listeners, getPos() + hdrSize - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); - if (deobfuscate) { - list = Action.removeNops(0, list, version, getPos() + hdrSize, toString()/*FIXME?*/); - } + List list = ActionListReader.readActionList(listeners, getPos() + hdrSize - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); return list; } catch (Exception ex) { Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index 68c847df4..3c7d1d759 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.helpers.ReReadableInputStream; import java.io.ByteArrayInputStream; @@ -115,11 +116,7 @@ public class DoActionTag extends Tag implements ASMSource { baos.write(actionBytes); ReReadableInputStream rri = new ReReadableInputStream(new ByteArrayInputStream(baos.toByteArray())); rri.setPos(prevLength); - boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); - List list = SWFInputStream.readActionList(listeners, getPos() - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); - if (deobfuscate) { - list = Action.removeNops(0, list, version, getPos(), toString()/*FIXME?*/); - } + List list = ActionListReader.readActionList(listeners, getPos() - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); return list; } catch (Exception ex) { Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index 910c33107..88804ef60 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.helpers.Helper; @@ -124,11 +125,7 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource { baos.write(actionBytes); ReReadableInputStream rri = new ReReadableInputStream(new ByteArrayInputStream(baos.toByteArray())); rri.setPos(prevLength); - boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); - List list = SWFInputStream.readActionList(listeners, getPos() + 2 - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); - if (deobfuscate) { - list = Action.removeNops(0, list, version, getPos() + 2, toString()/*FIXME?*/); - } + List list = ActionListReader.readActionList(listeners, getPos() + 2 - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); return list; } catch (Exception ex) { Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); diff --git a/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index 5d85fe9f8..331178ec2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.Configuration; import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.Exportable; @@ -169,11 +170,7 @@ public class BUTTONCONDACTION implements ASMSource, Exportable { @Override public List getActions(int version) { try { - boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); - List list = SWFInputStream.readActionList(listeners, getPos() + 4, new ReReadableInputStream(new ByteArrayInputStream(actionBytes)), version, 0, -1, toString()/*FIXME?*/); - if (deobfuscate) { - list = Action.removeNops(0, list, version, getPos() + 4, toString()/*FIXME?*/); - } + List list = ActionListReader.readActionList(listeners, getPos() + 4, new ReReadableInputStream(new ByteArrayInputStream(actionBytes)), version, 0, -1, toString()/*FIXME?*/); return list; } catch (Exception ex) { diff --git a/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index edcb5071f..0f9ee96a9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.Configuration; import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.Exportable; @@ -167,11 +168,7 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable { @Override public List getActions(int version) { try { - boolean deobfuscate = Configuration.getConfig("autoDeobfuscate", true); - List list = SWFInputStream.readActionList(listeners, getPos() + hdrPos, new ReReadableInputStream(new ByteArrayInputStream(actionBytes)), version, 0, -1, toString()/*FIXME?*/); - if (deobfuscate) { - list = Action.removeNops(0, list, version, getPos() + hdrPos, toString()/*FIXME?*/); - } + List list = ActionListReader.readActionList(listeners, getPos() + hdrPos, new ReReadableInputStream(new ByteArrayInputStream(actionBytes)), version, 0, -1, toString()/*FIXME?*/); return list; } catch (Exception ex) { Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); diff --git a/trunk/src/com/jpexs/decompiler/graph/Graph.java b/trunk/src/com/jpexs/decompiler/graph/Graph.java index 86ce3e7c8..6b4d40c0c 100644 --- a/trunk/src/com/jpexs/decompiler/graph/Graph.java +++ b/trunk/src/com/jpexs/decompiler/graph/Graph.java @@ -2240,7 +2240,7 @@ public class Graph { level--; } ret.append(tabString(level)); - ret.append(parts[p]); + ret.append(parts[p].trim()); ret.append("\r\n"); if (strippedP.equals("{")) { level++; diff --git a/trunk/src/com/jpexs/decompiler/graph/GraphSourceItemContainer.java b/trunk/src/com/jpexs/decompiler/graph/GraphSourceItemContainer.java index 18babfec1..103c83040 100644 --- a/trunk/src/com/jpexs/decompiler/graph/GraphSourceItemContainer.java +++ b/trunk/src/com/jpexs/decompiler/graph/GraphSourceItemContainer.java @@ -31,6 +31,8 @@ public interface GraphSourceItemContainer { public List getContainerSizes(); + public void setContainerSize(int index, long size); + public String getASMSourceBetween(int pos); public boolean parseDivision(long size, FlasmLexer lexer);