From 31f6cbdb70d3c1e574e13dcefbcca65102ac3823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 19 Nov 2023 23:30:08 +0100 Subject: [PATCH] Faster AS3 Debugging - export/import ByteArray variable data --- .../flash/debugger/DebugConnection.as | 153 ++++++++++++++---- libsrc/debugswf/debug/DOMDocument.xml | 14 +- libsrc/debugswf/debug/META-INF/metadata.xml | 12 +- libsrc/debugswf/debug/PublishSettings.xml | 2 +- libsrc/debugswf/debug/bin/SymDepend.cache | Bin 28 -> 28 bytes .../src/com/jpexs/decompiler/flash/SWF.java | 3 + .../decompiler/flash/gui/DebugLogDialog.java | 15 +- .../decompiler/flash/gui/DebugPanel.java | 111 ++++++++++++- src/com/jpexs/decompiler/flash/gui/Main.java | 28 ++-- .../flash/gui/debugger/DebugAdapter.java | 50 ++++++ .../flash/gui/debugger/DebugListener.java | 4 + .../flash/gui/debugger/Debugger.java | 91 +++++++++-- .../flash/gui/debugger/DebuggerTools.java | 45 +++++- .../decompiler/flash/gui/debugger/debug.swf | Bin 2969 -> 5089 bytes 14 files changed, 446 insertions(+), 82 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/debugger/DebugAdapter.java diff --git a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as index 05c9ca248..8cad454b0 100644 --- a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as +++ b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as @@ -4,7 +4,9 @@ import flash.utils.ByteArray; import flash.events.Event; import flash.display.Sprite; - + import flash.utils.getTimer; + import flash.events.ProgressEvent; + public class DebugConnection { private static var s:Socket; @@ -12,21 +14,30 @@ private static var first:Boolean = true; private static var inited:Boolean = false; private static var name:String; + private static var failed:Boolean = false; + private static var fillByteArrays = []; + private static var lenBytes:Array = [ + -1, -1, -1, -1 + ]; + private static var lenBytePos:int = 0; + private static var len:int = 0; + private static var readBa:ByteArray; + + public static const DEBUG_VERSION_MAJOR = 1; - public static const DEBUG_VERSION_MINOR = 2; + public static const DEBUG_VERSION_MINOR = 3; public static const MSG_STRING = 0; public static const MSG_LOADER_URL = 1; public static const MSG_LOADER_BYTES = 2; - public static const MSG_LONGSTRING = 3; - public static const MSG_LOADER_URL_GETBYTES = 4; - public static const MSG_LOADER_BYTES_GETBYTES = 5; - - + public static const MSG_DUMP_BYTEARRAY = 3; + public static const MSG_REQUEST_BYTEARRAY = 4; + private static function sendQueue(){ var qo = q; q = []; + sendHeader(); for each(var m in qo){ writeMsg(m.data,m.type); } @@ -68,8 +79,7 @@ var a3 = s.readUnsignedByte(); var a4 = s.readUnsignedByte(); var len = (a1<<24)+(a2<<16)+(a3<<8)+a4; - - s.readBytes(b,0,len); + s.readBytes(b,0,len); return b; } @@ -85,24 +95,65 @@ private static function readLongString():String { var b:ByteArray = readBytes(); return b.readUTFBytes(b.length); - } - + } + public static function initClient(sname){ if(inited){ return; } name = sname; inited = true; - s = new Socket(); - s.addEventListener(Event.CONNECT, function(){ - sendQueue(); - }); - var port:int = 0; - port = 123456; - s.connect("localhost",port); - inited = true; + try { + s = new Socket(); + s.addEventListener(Event.CONNECT, onSocketConnect); + s.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); + var port:int = 0; + port = 123456; + s.connect("localhost",port); + + inited = true; + } catch (e:SecurityError) { + trace("Debugger helper failed to connect to localhost"); + failed = true; + } } + private static function onSocketConnect(event:Event):void { + sendQueue(); + } + + + private static function onSocketData(event:ProgressEvent):void { + while (s.bytesAvailable > 0) { + if (lenBytePos < 4) { + lenBytes[lenBytePos] = s.readUnsignedByte(); + lenBytePos++; + if (lenBytePos == 4) { + len = (lenBytes[0]<<24)+(lenBytes[1]<<16)+(lenBytes[2]<<8)+lenBytes[3]; + readBa = new ByteArray(); + } + } else { + var readLen:int = s.bytesAvailable <= len ? s.bytesAvailable : len; + s.readBytes(readBa, readBa.length, readLen); + len -= readLen; + + if (len == 0) { + lenBytePos = 0; + var ba:ByteArray = fillByteArrays.pop(); + var pos = ba.position; + ba.position = 0; + ba.length = 0; + ba.writeBytes(readBa); + if (pos > ba.length) { + ba.position = ba.length; + } else { + ba.position = pos; + } + } + } + } + } + public static function writeLoaderURL(url){ writeMsg(url,MSG_LOADER_URL); @@ -113,19 +164,59 @@ writeMsg(data,MSG_LOADER_BYTES); } + public static function writeCommaSeparatedToByteArray(s:String, ba:ByteArray) { + var bytes:Array = s.split(","); + var pos:uint = ba.position; + ba.position = 0; + ba.length = 0; + for (var i:int = 0; i < bytes.length; i++) { + ba.writeByte(bytes[i]); + } + if (pos > ba.length) { + ba.position = ba.length; + } else { + ba.position = pos; + } + } + public static function readCommaSeparatedFromByteArray(ba:ByteArray): String { + var s:String = ""; + var splitter = ""; + for (var i:int = 0; i < ba.length; i++) { + s += splitter; + s += ba[i]; + splitter = ","; + } + return s; + } + + private static function sendHeader() { + if (!first) { + return; + } + if (!s.connected) { + return; + } + writeStringNull("debug.version.major="+DEBUG_VERSION_MAJOR+";debug.version.minor="+DEBUG_VERSION_MINOR); + writeString(name); + first = false; + } public static function writeMsg(msg,msgType=0){ - if(!inited){ + if (failed) { + return; + } + if(!inited) { initClient(""); } - if(s.connected){ - if(first){ - //s.writeByte(0); - writeStringNull("debug.version.major="+DEBUG_VERSION_MAJOR+";debug.version.minor="+DEBUG_VERSION_MINOR); - writeString(name); - first = false; - } + if ((msg is ByteArray) && msgType == MSG_DUMP_BYTEARRAY) { + var ba2:ByteArray = new ByteArray(); + ba2.writeBytes(msg); + msg = ba2; + } + + if(s.connected){ + sendHeader(); s.writeByte(msgType); switch(msgType){ case MSG_STRING: @@ -137,11 +228,11 @@ case MSG_LOADER_BYTES: writeBytes(msg); break; - case MSG_LOADER_URL_GETBYTES: - writeString(msg); + case MSG_DUMP_BYTEARRAY: + writeBytes(msg); break; - case MSG_LOADER_BYTES_GETBYTES: - writeBytes(msg); + case MSG_REQUEST_BYTEARRAY: + fillByteArrays.push(msg); break; } s.flush(); diff --git a/libsrc/debugswf/debug/DOMDocument.xml b/libsrc/debugswf/debug/DOMDocument.xml index 2a2022b3c..dcadb265f 100644 --- a/libsrc/debugswf/debug/DOMDocument.xml +++ b/libsrc/debugswf/debug/DOMDocument.xml @@ -14,6 +14,13 @@ + + + + + + + @@ -27,12 +34,5 @@ - - - - - - - \ No newline at end of file diff --git a/libsrc/debugswf/debug/META-INF/metadata.xml b/libsrc/debugswf/debug/META-INF/metadata.xml index ed05677a5..d682531f2 100644 --- a/libsrc/debugswf/debug/META-INF/metadata.xml +++ b/libsrc/debugswf/debug/META-INF/metadata.xml @@ -5,8 +5,8 @@ xmlns:xmp="http://ns.adobe.com/xap/1.0/"> Adobe Flash Professional CS6 - build 537 2014-10-26T15:59:21+01:00 - 2023-11-18T12:05:50-08:00 - 2023-11-18T12:05:50-08:00 + 2023-11-19T08:32:38-08:00 + 2023-11-19T08:32:38-08:00 @@ -15,7 +15,7 @@ - xmp.iid:CDA31DDF4D86EE11B43B9B2E6D6D7C97 + xmp.iid:D4132A45B886EE11B43B9B2E6D6D7C97 xmp.did:1476A545885CE411B13FACEEFCD7D43C xmp.did:1476A545885CE411B13FACEEFCD7D43C @@ -50,6 +50,12 @@ 2014-10-26T15:59:21+01:00 Adobe Flash Professional CS6 - build 481 + + created + xmp.iid:D4132A45B886EE11B43B9B2E6D6D7C97 + 2014-10-26T15:59:21+01:00 + Adobe Flash Professional CS6 - build 481 + diff --git a/libsrc/debugswf/debug/PublishSettings.xml b/libsrc/debugswf/debug/PublishSettings.xml index e317964e7..618fc2897 100644 --- a/libsrc/debugswf/debug/PublishSettings.xml +++ b/libsrc/debugswf/debug/PublishSettings.xml @@ -79,7 +79,7 @@ . CONFIG::FLASH_AUTHORING="true"; - 0 + 1 1 0 diff --git a/libsrc/debugswf/debug/bin/SymDepend.cache b/libsrc/debugswf/debug/bin/SymDepend.cache index ea8830d752c3eb45b3610f3d021facacb747632e..8ece03aba8945329a120f5d1db7dc7060c7c9016 100644 GIT binary patch literal 28 ecmYdiU|@L8&J84m8Mqik7^HwK5ZJetfdK$3-~$%` literal 28 fcmYdiU|@L8&dtEaAjH7Sz``I0 { + exportByteArrayMenuItem.addActionListener((ActionEvent e1) -> { JFileChooser fc = new JFileChooser(); fc.setCurrentDirectory(new File(Configuration.lastExportDir.get())); if (fc.showSaveDialog(Main.getDefaultMessagesComponent()) == JFileChooser.APPROVE_OPTION) { File file = Helper.fixDialogFile(fc.getSelectedFile()); + + //Variant with direct calling readByte - SLOW + /* try (FileOutputStream fos = new FileOutputStream(file)) { Main.debugExportByteArray(v, fos); + fos.write(data); Configuration.lastExportDir.set(file.getParentFile().getAbsolutePath()); } catch (IOException ex) { Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex); - ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); } - } + */ + //Asynchronous variant + /*DebuggerTools.initDebugger().addMessageListener(new DebugAdapter() { + + @Override + public void onDumpByteArray(String clientId, byte[] data) { + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(data); + Configuration.lastExportDir.set(file.getParentFile().getAbsolutePath()); + } catch (IOException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex); + ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + DebuggerTools.initDebugger().removeMessageListener(this); + } + }); + Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent; + try { + Main.getDebugHandler().callMethod(debugConnectionClass, "writeMsg", Arrays.asList(v, (Double) (double) Debugger.MSG_DUMP_BYTEARRAY)); + } catch (DebuggerHandler.ActionScriptException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex); + }*/ + + //Variant using comma separated bytes, pretty fast + try { + Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent; + String dataStr = (String) Main.getDebugHandler().callMethod(debugConnectionClass, "readCommaSeparatedFromByteArray", Arrays.asList(v)).variables.get(0).value; + String[] parts = dataStr.split(","); + byte[] data = new byte[parts.length]; + for (int i = 0; i < parts.length; i++) { + data[i] = (byte) Integer.parseInt(parts[i]); + } + + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(data); + Configuration.lastExportDir.set(file.getParentFile().getAbsolutePath()); + } catch (IOException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex); + ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + } catch (DebuggerHandler.ActionScriptException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex); + } + } }); exportMenu.add(exportByteArrayMenuItem); @@ -227,11 +280,57 @@ public class DebugPanel extends JPanel { if (fc.showOpenDialog(Main.getDefaultMessagesComponent()) == JFileChooser.APPROVE_OPTION) { File file = Helper.fixDialogFile(fc.getSelectedFile()); Configuration.lastOpenDir.set(file.getParentFile().getAbsolutePath()); - try (FileInputStream fis = new FileInputStream(file)) { + + //Variant with asynchronous connection + /*DebuggerTools.initDebugger().addMessageListener(new DebugAdapter() { + @Override + public byte[] onRequestBytes(String clientId) { + byte[] data = null; + try { + data = Helper.readFileEx(file.getAbsolutePath()); + } catch (IOException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex); + ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + DebuggerTools.initDebugger().removeMessageListener(this); + return data; + } + }); + Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent; + try { + Main.getDebugHandler().callMethod(debugConnectionClass, "writeMsg", Arrays.asList(v, (Double) (double) Debugger.MSG_REQUEST_BYTEARRAY)); + } catch (DebuggerHandler.ActionScriptException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex); + }*/ + + //Variant with direct writeByte calls - SLOW + /*try (FileInputStream fis = new FileInputStream(file)) { Main.debugImportByteArray(v, fis); } catch (IOException ex) { Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex); ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + }*/ + + //Variant with using comma separated bytes, pretty fast + try { + byte[] data = Helper.readFileEx(file.getAbsolutePath()); + String splitter = ""; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length; i++) { + sb.append(splitter); + sb.append(data[i] & 0xff); + splitter = ","; + } + String dataStr = sb.toString(); + Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent; + try { + Main.getDebugHandler().callMethod(debugConnectionClass, "writeCommaSeparatedToByteArray", Arrays.asList(dataStr, v)); + } catch (DebuggerHandler.ActionScriptException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex); + } + } catch (IOException ex) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex); + ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); } } }); @@ -264,8 +363,8 @@ public class DebugPanel extends JPanel { addWatchMenu.add(watchReadMenuItem); addWatchMenu.add(watchWriteMenuItem); addWatchMenu.add(watchReadWriteMenuItem); - pm.add(addWatchMenu); - pm.show(e.getComponent(), e.getX(), e.getY()); + pm.add(addWatchMenu); + pm.show(e.getComponent(), e.getX(), e.getY()); } }; diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 5d2c0b5fd..19c01a215 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -46,6 +46,7 @@ import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; import com.jpexs.decompiler.flash.console.ContextMenuTools; import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; import com.jpexs.decompiler.flash.gfx.GfxConvertor; +import com.jpexs.decompiler.flash.gui.debugger.DebugAdapter; import com.jpexs.decompiler.flash.gui.debugger.DebugListener; import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; @@ -217,6 +218,8 @@ public class Main { public static CancellableWorker importWorker = null; public static CancellableWorker deobfuscatePCodeWorker = null; public static CancellableWorker swfPrepareWorker = null; + + public static String currentDebuggerPackage = null; public static boolean isSwfAir(Openable openable) { SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(openable.getShortPathTitle()); @@ -524,7 +527,13 @@ public class Main { } instrSWF.removeEventListener(prepEventListner); - instrSWF = super.prepare(instrSWF); + //instrSWF = super.prepare(instrSWF); + + if (!DebuggerTools.hasDebugger(instrSWF)) { + DebuggerTools.switchDebugger(instrSWF); + } + DebuggerTools.injectDebugLoader(instrSWF); + currentDebuggerPackage = instrSWF.debuggerPackage; return instrSWF; } @@ -2399,15 +2408,8 @@ public class Main { watcherWorker.execute(); } - DebuggerTools.initDebugger().addMessageListener(new DebugListener() { - @Override - public void onMessage(String clientId, String msg) { - } - - @Override - public void onLoaderURL(String clientId, String url) { - } - + DebuggerTools.initDebugger().addMessageListener(new DebugAdapter() { + @Override public void onLoaderBytes(String clientId, byte[] data) { String hash = md5(data); @@ -2438,11 +2440,7 @@ public class Main { } catch (IOException ex) { logger.log(Level.SEVERE, "Cannot create tempfile"); } - } - - @Override - public void onFinish(String clientId) { - } + } }); try { diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebugAdapter.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebugAdapter.java new file mode 100644 index 000000000..90d418a4b --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebugAdapter.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2023 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.gui.debugger; + +/** + * + * @author JPEXS + */ +public class DebugAdapter implements DebugListener { + + @Override + public void onMessage(String clientId, String msg) { + } + + @Override + public void onLoaderURL(String clientId, String url) { + } + + @Override + public void onLoaderBytes(String clientId, byte[] data) { + } + + @Override + public void onDumpByteArray(String clientId, byte[] data) { + } + + @Override + public void onFinish(String clientId) { + } + + @Override + public byte[] onRequestBytes(String clientId) { + return null; + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebugListener.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebugListener.java index 6bfa1729f..2818da1fa 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/DebugListener.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebugListener.java @@ -27,6 +27,10 @@ public interface DebugListener { public void onLoaderURL(String clientId, String url); public void onLoaderBytes(String clientId, byte[] data); + + public void onDumpByteArray(String clientId, byte[] data); public void onFinish(String clientId); + + public byte[] onRequestBytes(String clientId); } diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java b/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java index 8e7bc3185..ca03f383a 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java @@ -30,6 +30,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -37,8 +39,29 @@ import java.util.WeakHashMap; */ public class Debugger { + public static final int MSG_STRING = 0; + + public static final int MSG_LOADER_URL = 1; + + public static final int MSG_LOADER_BYTES = 2; + + public static final int MSG_DUMP_BYTEARRAY = 3; + + public static final int MSG_REQUEST_BYTEARRAY = 4; + + private static final Set listeners = new HashSet<>(); + private static Logger logger = Logger.getLogger(Debugger.class.getName()); + + private static boolean active = false; + + public static boolean isActive() { + return active; + } + + + public synchronized void addMessageListener(DebugListener l) { listeners.add(l); } @@ -61,12 +84,7 @@ public class Debugger { private final Map parameters = new HashMap<>(); - public static final int MSG_STRING = 0; - - public static final int MSG_LOADER_URL = 1; - - public static final int MSG_LOADER_BYTES = 2; - + public String getParameter(String name, String defValue) { if (parameters.containsKey(name)) { return parameters.get(name); @@ -108,6 +126,14 @@ public class Debugger { return type; } + private void writeBytes(OutputStream os, byte[] data) throws IOException { + os.write((data.length >> 24) & 0xff); + os.write((data.length >> 16) & 0xff); + os.write((data.length >> 8) & 0xff); + os.write(data.length & 0xff); + os.write(data); + } + private byte[] readBytes(InputStream is) throws IOException { int len = is.read(); if (len == -1) { @@ -161,7 +187,8 @@ public class Debugger { @Override public void run() { String clientName = Integer.toString(id); - try (InputStream is = s.getInputStream()) { + try (InputStream is = s.getInputStream(); + OutputStream os = s.getOutputStream()) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int c; @@ -175,11 +202,10 @@ public class Debugger { String ret = baos.toString("UTF-8"); if (ret.equals("")) { - try (OutputStream os = s.getOutputStream()) { - os.write(("").getBytes("UTF-8")); - } + os.write(("").getBytes("UTF-8")); } else { if (!ret.isEmpty()) { + logger.log(Level.FINER, "Connected. Header: {0}", ret); String[] param = (ret.contains(";") ? ret.split(";") : new String[]{ret}); for (String p : param) { if (p.contains("=")) { @@ -190,35 +216,79 @@ public class Debugger { parameters.put(p, "true"); } } + active = true; } boolean hasType = hasMsgType(); + logger.log(Level.FINER, "reading name..."); String name = readString(is); + logger.log(Level.FINER, "name = {0}", name); if (!name.isEmpty()) { clientName = name; } while (true) { int type = 0; + logger.finer("reading type..."); if (hasType) { type = readType(is); } + logger.log(Level.FINE, "received type {0}", type); switch (type) { case MSG_STRING: + logger.finer("reading string..."); ret = readString(is); + logger.finer("informing listeners..."); for (DebugListener l : listeners) { l.onMessage(clientName, ret); } + logger.finer("listeners informed"); break; case MSG_LOADER_URL: + logger.finer("reading string..."); ret = readString(is); + logger.finer("informing listeners..."); for (DebugListener l : listeners) { l.onLoaderURL(clientName, ret); } + logger.finer("listeners informed"); break; case MSG_LOADER_BYTES: + logger.finer("reading bytes..."); byte[] retB = readBytes(is); + logger.finer("informing listeners..."); for (DebugListener l : listeners) { l.onLoaderBytes(clientName, retB); } + logger.finer("listeners informed"); + break; + case MSG_DUMP_BYTEARRAY: + logger.finer("reading bytes..."); + byte[] retBa = readBytes(is); + logger.finer("informing listeners..."); + for (DebugListener l : listeners) { + l.onDumpByteArray(clientName, retBa); + } + logger.finer("listeners informed"); + break; + case MSG_REQUEST_BYTEARRAY: + logger.finer("checking listeners for data..."); + boolean dataFound = false; + for (DebugListener l : listeners) { + byte[] data = l.onRequestBytes(clientName); + if (data != null) { + logger.finer("found listener with data"); + logger.log(Level.FINER, "writing data.length = {0}", data.length); + writeBytes(os, data); + logger.finer("data written"); + dataFound = true; + break; + } + } + if (!dataFound) { + logger.finer("listener not found, writing empty array"); + writeBytes(os, new byte[0]); + } + os.flush(); + logger.finer("listeners checked"); break; } } @@ -233,6 +303,7 @@ public class Debugger { //ignore } finished = true; + active = false; for (DebugListener l : listeners) { l.onFinish(clientName); } diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java index d92078b54..a19f0c5ff 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java @@ -46,8 +46,8 @@ public class DebuggerTools { public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger"; - private static volatile Debugger debugger; - + private static volatile Debugger debugger; + private static ScriptPack getDebuggerScriptPack(SWF swf) { List allAbcList = new ArrayList<>(); for (ABCContainerTag ac : swf.getAbcList()) { @@ -234,6 +234,7 @@ public class DebuggerTools { if (Configuration.randomDebuggerPackage.get()) { newdebuggerpkg += ".pkg" + rhex; } + swf.debuggerPackage = newdebuggerpkg; //add debug ABC tags to main SWF for (ABCContainerTag ds : debugSWF.getAbcList()) { @@ -261,6 +262,46 @@ public class DebuggerTools { ft.useNetwork = true; ft.setModified(true); } + + + + //Add call to DebugConnection.initClient("") to the document class + /*String documentClass = swf.getDocumentClass(); + if (documentClass != null) { + List searchClassNames = new ArrayList<>(); + searchClassNames.add(documentClass); + List documentClassPacks = swf.getScriptPacksByClassNames(searchClassNames); + if (!documentClassPacks.isEmpty()) { + ScriptPack documentClassPack = documentClassPacks.get(0); + Trait publicTrait = documentClassPack.getPublicTrait(); + if (publicTrait != null) { + if (publicTrait instanceof TraitClass) { + TraitClass classTrait = (TraitClass) publicTrait; + int classIndex = classTrait.class_info; + ABC a = documentClassPack.abc; + int cinitMethodInfo = a.class_info.get(classIndex).cinit_index; + MethodBody body = a.findBody(cinitMethodInfo); + AVM2Code code = body.getCode(); + int debugConnectionMultiname = a.constants.getMultinameId( + Multiname.createQName(false, a.constants.getStringId("DebugConnection", true), + a.constants.getNamespaceId(Namespace.KIND_PACKAGE, newdebuggerpkg, 0, true) + ), true); + int initClientMultiname = a.constants.getMultinameId( + Multiname.createQName(false, a.constants.getStringId("initClient", true), + a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true) + ), true); + code.insertInstruction(0, new AVM2Instruction(0, AVM2Instructions.GetLex, new int[] {debugConnectionMultiname}), true, body); + code.insertInstruction(1, new AVM2Instruction(0, AVM2Instructions.PushString, new int[] {a.constants.getStringId("", true)}), true, body); + code.insertInstruction(2, new AVM2Instruction(0, AVM2Instructions.CallPropVoid, new int[] {initClientMultiname, 1}), true, body); + if (body.max_stack < 2) { + body.max_stack = 2; + } + body.setModified(); + } + } + } + } + */ } catch (Exception ex) { logger.log(Level.SEVERE, "Error while attaching debugger", ex); diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/debug.swf b/src/com/jpexs/decompiler/flash/gui/debugger/debug.swf index bc35da0026e19284d0058dc5aa7d3080445d7777..cc36bc0ef5454f0fa1f6dbfe2c079fc057021db1 100644 GIT binary patch literal 5089 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 DE=caZ literal 2969 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 PLI1WQNH6*?S3=32-yXbC