diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index d53f5d252..b60b6b01e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.action.Action; @@ -2859,4 +2860,19 @@ public final class SWF implements SWFContainerItem, Timelined { public String toString() { return getShortFileName(); } + + public void deobfuscate(DeobfuscationLevel level) throws InterruptedException { + List atags = getAbcList(); + + for (ABCContainerTag tag : atags) { + if (level == DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE) { + tag.getABC().removeDeadCode(); + } else if (level == DeobfuscationLevel.LEVEL_REMOVE_TRAPS) { + tag.getABC().removeTraps(); + } else if (level == DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW) { + tag.getABC().removeTraps(); + tag.getABC().restoreControlFlow(); + } + } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/DeobfuscationLevel.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/DeobfuscationLevel.java new file mode 100644 index 000000000..d55ccde43 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/DeobfuscationLevel.java @@ -0,0 +1,22 @@ +package com.jpexs.decompiler.flash.abc.avm2.deobfuscation; + +/** + * + * @author JPEXS + */ +public enum DeobfuscationLevel { + + LEVEL_REMOVE_DEAD_CODE(1), + LEVEL_REMOVE_TRAPS(2), + LEVEL_RESTORE_CONTROL_FLOW(3); + + private final int level; + + public int getLevel() { + return level; + } + + DeobfuscationLevel(int level) { + this.level = level; + } +} diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index ecca43947..69ce22c27 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler; @@ -314,6 +315,10 @@ public class CommandLineArgumentParser { out.println(" " + (cnt++) + ") -replace (|) [methodBodyIndex1] [(|) [methodBodyIndex2]]..."); out.println(" ...replaces the data of the specified BinaryData, Image, DefineSound tag or Script"); out.println(" ...methodBodyIndexN parameter should be specified if and only if the imported entity is an AS3 P-Code"); + out.println(" " + (cnt++) + ") -deobfuscate "); + out.println(" ...Deobfuscates AS3 P-code in and saves result to "); + out.println(" ... can be one of: controlflow/3/max, traps/2, deadcode/1"); + out.println(" ...WARNING: The deobfuscation result is still probably far enough to be openable by other decompilers."); printCmdLineUsageExamples(out); } @@ -329,11 +334,12 @@ public class CommandLineArgumentParser { out.println("java -jar ffdec.jar -format script:pcode,text:plain -export script,text,image \"C:\\decompiled\" myfile.swf"); out.println("java -jar ffdec.jar -format fla:cs5.5 -export fla \"C:\\sources\\myfile.fla\" myfile.swf"); out.println("java -jar ffdec.jar -dumpSWF myfile.swf"); - out.println("java -jar ffdec.jar -compress myfile.swf myfiledec.swf"); - out.println("java -jar ffdec.jar -decompress myfiledec.swf myfile.swf"); + out.println("java -jar ffdec.jar -compress myfile.swf myfilecomp.swf"); + out.println("java -jar ffdec.jar -decompress myfile.swf myfiledec.swf"); out.println("java -jar ffdec.jar -onerror ignore -export script \"C:\\decompiled\" myfile.swf"); out.println("java -jar ffdec.jar -onerror retry 5 -export script \"C:\\decompiled\" myfile.swf"); out.println("java -jar ffdec.jar -config autoDeobfuscate=1,parallelSpeedUp=0 -export script \"C:\\decompiled\" myfile.swf"); + out.println("java -jar ffdec.jar -deobfuscate max myas3file_secure.swf myas3file.swf"); out.println(""); out.println("Instead of \"java -jar ffdec.jar\" you can use ffdec.bat on Windows, ffdec.sh on Linux/MacOs"); } @@ -470,6 +476,8 @@ public class CommandLineArgumentParser { parseXml2Swf(args); } else if (command.equals("extract")) { parseExtract(args); + } else if (command.equals("deobfuscate")) { + parseDeobfuscate(args); } else if (command.equals("renameinvalididentifiers")) { parseRenameInvalidIdentifiers(args); } else if (command.equals("dumpswf")) { @@ -1317,6 +1325,75 @@ public class CommandLineArgumentParser { } } + private static void parseDeobfuscate(Stack args) { + if (args.size() < 3) { + badArguments(); + } + String mode = args.pop(); + DeobfuscationLevel lev = null; + switch (mode) { + case "controlflow": + case "max": + case "3": + lev = DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW; + break; + case "traps": + case "2": + lev = DeobfuscationLevel.LEVEL_REMOVE_TRAPS; + break; + case "deadcode": + case "1": + lev = DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE; + break; + default: + System.err.println("Invalid level, must be one of: controlflow,traps,deadcode or 1,2, 3/max"); + System.exit(1); + break; + } + File inFile = new File(args.pop()); + File outFile = new File(args.pop()); + File tmpFile = null; + if (inFile.equals(outFile)) { + try { + tmpFile = File.createTempFile("ffdec_deobf_", ".swf"); + outFile = tmpFile; + } catch (IOException ex) { + System.err.println("Unable to create temp file"); + System.exit(1); + } + } + try (FileInputStream is = new FileInputStream(inFile); + FileOutputStream fos = new FileOutputStream(outFile)) { + SWF swf = new SWF(is, Configuration.parallelSpeedUp.get()); + if (!swf.isAS3()) { + System.out.println("Warning: The file is not AS3. Only AS3 deobfuscation from commandline is available."); + System.exit(0); + } + swf.deobfuscate(lev); + swf.saveTo(fos); + if (tmpFile != null) { + inFile.delete(); + tmpFile.renameTo(inFile); + tmpFile = null; + System.out.println("" + inFile + " overwritten."); + } + System.out.println("OK"); + } catch (FileNotFoundException ex) { + System.err.println("File not found."); + System.exit(1); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + System.exit(1); + } catch (IOException ex) { + Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, "Error", ex); + System.exit(1); + } finally { + if (tmpFile != null && tmpFile.exists()) { + tmpFile.delete(); + } + } + } + private static void parseCompress(Stack args) { if (args.size() < 2) { badArguments(); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index f7e10830a..715eb41d9 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.configuration.ConfigurationItem; @@ -2222,11 +2223,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se ABCPanel aBCPanel = getABCPanel(); if (deobfuscationDialog.processAllCheckbox.isSelected()) { for (ABCContainerTag tag : abcPanel.getAbcList()) { - if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { + if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE.getLevel()) { tag.getABC().removeDeadCode(); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationLevel.LEVEL_REMOVE_TRAPS.getLevel()) { tag.getABC().removeTraps(); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW.getLevel()) { tag.getABC().removeTraps(); tag.getABC().restoreControlFlow(); } @@ -2237,11 +2238,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); ABC abc = abcPanel.abc; if (bi != -1) { - if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { + if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE.getLevel()) { abc.bodies.get(bi).removeDeadCode(abc.constants, t, abc.method_info.get(abc.bodies.get(bi).method_info)); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationLevel.LEVEL_REMOVE_TRAPS.getLevel()) { abc.bodies.get(bi).removeTraps(abc.constants, abc, t, decompiledTextArea.getScriptLeaf().scriptIndex, decompiledTextArea.getClassIndex(), decompiledTextArea.getIsStatic(), ""/*FIXME*/); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW.getLevel()) { abc.bodies.get(bi).removeTraps(abc.constants, abc, t, decompiledTextArea.getScriptLeaf().scriptIndex, decompiledTextArea.getClassIndex(), decompiledTextArea.getIsStatic(), ""/*FIXME*/); abc.bodies.get(bi).restoreControlFlow(abc.constants, t, abc.method_info.get(abc.bodies.get(bi).method_info)); } diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DeobfuscationDialog.java b/src/com/jpexs/decompiler/flash/gui/abc/DeobfuscationDialog.java index e66a1d4b2..93f617fc0 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DeobfuscationDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DeobfuscationDialog.java @@ -16,6 +16,8 @@ */ package com.jpexs.decompiler.flash.gui.abc; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; import com.jpexs.decompiler.flash.gui.AppDialog; import com.jpexs.decompiler.flash.gui.View; import java.awt.Component; @@ -43,12 +45,6 @@ public class DeobfuscationDialog extends AppDialog { private int result = ERROR_OPTION; - public static final int LEVEL_REMOVE_DEAD_CODE = 1; - - public static final int LEVEL_REMOVE_TRAPS = 2; - - public static final int LEVEL_RESTORE_CONTROL_FLOW = 3; - @SuppressWarnings("unchecked") public DeobfuscationDialog() { setDefaultCloseOperation(HIDE_ON_CLOSE); @@ -68,9 +64,9 @@ public class DeobfuscationDialog extends AppDialog { Hashtable labelTable = new Hashtable<>(); //labelTable.put(LEVEL_NONE, new JLabel("None")); - labelTable.put(LEVEL_REMOVE_DEAD_CODE, new JLabel(translate("deobfuscation.removedeadcode"))); - labelTable.put(LEVEL_REMOVE_TRAPS, new JLabel(translate("deobfuscation.removetraps"))); - labelTable.put(LEVEL_RESTORE_CONTROL_FLOW, new JLabel(translate("deobfuscation.restorecontrolflow"))); + labelTable.put(DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE.getLevel(), new JLabel(translate("deobfuscation.removedeadcode"))); + labelTable.put(DeobfuscationLevel.LEVEL_REMOVE_TRAPS.getLevel(), new JLabel(translate("deobfuscation.removetraps"))); + labelTable.put(DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW.getLevel(), new JLabel(translate("deobfuscation.restorecontrolflow"))); codeProcessingLevel.setLabelTable(labelTable); codeProcessingLevel.setPaintLabels(true);