diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..ea2f10417
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,24 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+tab_width = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.java]
+max_line_length = 120
+ij_java_class_brace_style = next_line
+ij_java_method_brace_style = next_line
+ij_java_block_brace_style = next_line
+
+[*.xml]
+indent_size = 4
+tab_width = 4
+
+[*.properties]
+indent_size = 4
+tab_width = 4
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 415e2ae47..3f794d727 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -14,24 +14,25 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- java: [ '8', '11', '17' ] # LTS versions
+ java: [ '8', '11', '17', '21' ] # LTS versions
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.java }}
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
+ cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Extract Maven project version
- run: echo ::set-output name=version::$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
+ run: echo "bcv_version=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)" >> $GITHUB_ENV
id: project
- name: 'Upload Artifact'
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
if: ${{ matrix.java == '8' }}
with:
- name: Bytecode-Viewer-${{ steps.project.outputs.version }}-SNAPSHOT
- path: target/Bytecode-Viewer-${{ steps.project.outputs.version }}.jar
+ name: Bytecode-Viewer-${{ env.bcv_version }}-SNAPSHOT
+ path: target/Bytecode-Viewer-${{ env.bcv_version }}.jar
retention-days: 90
diff --git a/LICENSE b/LICENSE
index b966d6748..5a43ba9f1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -645,30 +645,4 @@ the "copyright" line and a pointer to where the full notice is found.
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 .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Bytecode Viewer(BCV) Copyright (C) 2014 Kalen "Konloch" Kinloch - http://bytecodeviewer.com - http://the.bytecode.club
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
\ No newline at end of file
+ along with this program. If not, see .
\ No newline at end of file
diff --git a/README.md b/README.md
index 143b7e2df..2bbe99b59 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,10 @@
Bytecode Viewer - a lightweight user-friendly Java/Android Bytecode Viewer, Decompiler & More.
#### New Features
+* Jump to Declaration
+* Draggable tabs
* Patched [CVE-2022-21675](https://github.com/Konloch/bytecode-viewer/security/advisories/GHSA-3wq9-j4fc-4wmc) (Make sure to upgrade to v2.11.X)
-* Dark mode with multiple themes
+* Dark mode by default with multiple themes
* Translated into over 30 languages including: Arabic, German, Japanese, Mandarin, Russian, Spanish
* Plugin Writer - create and edit external plugins from within BCV
* Fixed Java & Bytecode editing/compiling
@@ -12,8 +14,7 @@ Bytecode Viewer - a lightweight user-friendly Java/Android Bytecode Viewer, Deco
* Right-click menus on the resource and search panels
* Javap disassembler
* XAPK support
-* Updated nearly all dependencies (incl. decompilers like CFR, JD-GUI etc.)
-* Updated ASM library to version 9.1
+* Latest dependencies (incl. decompilers like CFR, JD-GUI etc.)
* Added support to Java files compiled using JDK > 13
* Migrated to Maven
@@ -95,6 +96,9 @@ Just clone this repo and run ``mvn package``. It's that simple!
Open the Maven project (e.g. in IntelliJ, open the ``pom.xml`` as a project file).
+## UI Is Lagging
+Change the theme to your systems. Go into `View->Visual Settings->Window Theme` and select `System Theme`.
+
## Java Heap Space Issues (java.lang.OutOfMemoryError)
Start BCV with more RAM, e.g. `java -Xmx3G -jar BCV.jar`
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 000000000..a61ca81d5
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/checkstyle_suppression.xml b/checkstyle_suppression.xml
new file mode 100644
index 000000000..6c2c53ce0
--- /dev/null
+++ b/checkstyle_suppression.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/plugins/groovy/ExampleStringDecrypter.gy b/plugins/groovy/ExampleStringDecrypter.gy
index 72608f97c..bc4815299 100644
--- a/plugins/groovy/ExampleStringDecrypter.gy
+++ b/plugins/groovy/ExampleStringDecrypter.gy
@@ -7,7 +7,7 @@ import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialog
import java.lang.reflect.Field
-import static the.bytecode.club.bytecodeviewer.Constants.nl
+import static the.bytecode.club.bytecodeviewer.Constants.NL
/**
** This is an example of a String Decrypter Groovy Plugin for BCV.
@@ -22,7 +22,7 @@ class ExampleStringDecrypter extends Plugin {
MultipleChoiceDialog dialog = new MultipleChoiceDialog("Bytecode Viewer - WARNING",
"WARNING: This will load the classes into the JVM and execute the initialize function"
- + nl + "for each class. IF THE FILE YOU'RE LOADING IS MALICIOUS, DO NOT CONTINUE.",
+ + NL + "for each class. IF THE FILE YOU'RE LOADING IS MALICIOUS, DO NOT CONTINUE.",
new String[]{"Continue", "Cancel"})
if (dialog.promptChoice() == 0) {
diff --git a/plugins/java/ClassParser.java b/plugins/java/ClassParser.java
new file mode 100644
index 000000000..f5aefe2de
--- /dev/null
+++ b/plugins/java/ClassParser.java
@@ -0,0 +1,347 @@
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.tree.ClassNode;
+import the.bytecode.club.bytecodeviewer.BytecodeViewer;
+import the.bytecode.club.bytecodeviewer.api.Plugin;
+import the.bytecode.club.bytecodeviewer.api.PluginConsole;
+import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Class Parser
+ *
+ * @author Damir37
+ */
+public class ClassParser extends Plugin
+{
+
+ private PluginConsole pluginConsole;
+
+ @Override
+ public void execute(List list)
+ {
+ if(!BytecodeViewer.isActiveClassActive())
+ {
+ BytecodeViewer.showMessage("Open A Classfile First");
+ return;
+ }
+
+ ClassNode c = BytecodeViewer.getCurrentlyOpenedClassNode();
+
+ ClassFileParser classFileParser = new ClassFileParser(classNodeToByte(c));
+
+ pluginConsole = new PluginConsole("ClassParser");
+ pluginConsole.setVisible(true);
+
+ print("Parsing class: " + c.name + ".class");
+ print("MAGIC VALUE: " + classFileParser.parseMagicValue());
+ print("Class version: " + classFileParser.parseVersionClass());
+ print("Constant pool count: " + classFileParser.parseConstantPoolCount()
+ + " If not all constants were parsed, most likely the constant is not used in the bytecode.");
+ print("Then use the javap utility to view the constant pool of a class file.");
+ print("Last modified class: " + classFileParser.parseClassModificationDate());
+ print("Hash sum class md5: " + classFileParser.getHash("MD5"));
+ print("Hash sum class sha1: " + classFileParser.getHash("SHA-1"));
+ print("Hash sum class sha256: " + classFileParser.getHash("SHA-256"));
+ print("Hash sum class sha512: " + classFileParser.getHash("SHA-512"));
+ print("Constant pool ->");
+
+ classFileParser.getConstantPool().parseConstantPool();
+
+ if (classFileParser.getConstantPool().getCpList() != null && !classFileParser.getConstantPool().getCpList().isEmpty())
+ {
+ for (String s : classFileParser.getConstantPool().getCpList())
+ {
+ print(s);
+ }
+ }
+ }
+
+ private byte[] classNodeToByte(ClassNode classNode)
+ {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ classNode.accept(cw);
+ return cw.toByteArray();
+ }
+
+ public void print(String text)
+ {
+ pluginConsole.appendText(text);
+ pluginConsole.repaint();
+ }
+
+ private static class ClassFileParser
+ {
+ private final ByteBuffer buffer;
+ private final ConstantParser cpParser;
+
+ public ClassFileParser(byte[] classBytes)
+ {
+ this.buffer = ByteBuffer.wrap(classBytes);
+ cpParser = new ConstantParser(buffer, parseConstantPoolCount());
+ }
+
+ public String parseMagicValue()
+ {
+ buffer.position(0);
+ int magicValue = buffer.getInt();
+ return "0x" + Integer.toHexString(magicValue).toUpperCase();
+ }
+
+ public ClassVersion parseVersionClass()
+ {
+ buffer.position(4);
+ int minor = buffer.getShort() & 0xFFFF;
+ int major = buffer.getShort() & 0xFFFF;
+ return ClassVersion.check(major, minor);
+ }
+
+ public Date parseClassModificationDate()
+ {
+ buffer.position(8);
+ long modificationTime = buffer.getInt() & 0xFFFFFFFFL;
+ return new Date(modificationTime * 1000L);
+ }
+
+ public int parseConstantPoolCount()
+ {
+ buffer.position(8);
+ return buffer.getShort() & 0xFFFF;
+ }
+
+ public String getHash(String algorithm)
+ {
+ try
+ {
+ MessageDigest md = MessageDigest.getInstance(algorithm);
+ md.update(buffer.array());
+ byte[] digest = md.digest();
+ return convertToHex(digest);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ e.printStackTrace();
+ }
+
+ return "null";
+ }
+
+ private String convertToHex(byte[] bytes)
+ {
+ StringBuilder hexString = new StringBuilder();
+
+ for (byte b : bytes)
+ {
+ hexString.append(String.format("%02X", b));
+ }
+
+ return hexString.toString();
+ }
+
+ public ConstantParser getConstantPool()
+ {
+ return cpParser;
+ }
+ }
+
+ private enum ClassVersion
+ {
+ UNKNOWN(0, 0),
+ JAVA_1_1(45, 3),
+ JAVA_1_2(46, 0),
+ JAVA_1_3(47, 0),
+ JAVA_1_4(48, 0),
+ JAVA_5(49, 0),
+ JAVA_6(50, 0),
+ JAVA_7(51, 0),
+ JAVA_8(52, 0),
+ JAVA_9(53, 0),
+ JAVA_10(54, 0),
+ AVA_11(55, 0),
+ JAVA_12(56, 0),
+ JAVA_13(57, 0),
+ JAVA_14(58, 0),
+ JAVA_15(59, 0),
+ JAVA_16(60, 0),
+ JAVA_17(61, 0),
+ JAVA_18(62, 0),
+ JAVA_19(63, 0),
+ JAVA_20(64, 0),
+ JAVA_21(65, 0),
+ JAVA_22(66, 0),
+ JAVA_23(67, 0),
+ JAVA_24(68, 0),
+ JAVA_25(69, 0),
+ JAVA_26(70, 0),
+ JAVA_27(71, 0),
+ JAVA_28(72, 0),
+ JAVA_29(73, 0),
+ JAVA_30(74, 0);
+
+ public final int major;
+ public final int minor;
+
+ ClassVersion(int major, int minor)
+ {
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public static ClassVersion check(int major, int minor)
+ {
+ for (ClassVersion v : ClassVersion.values())
+ {
+ if (v.major == major && v.minor == minor)
+ return v;
+ }
+
+ return UNKNOWN;
+ }
+ }
+
+ private static class ConstantParser
+ {
+ private final ByteBuffer buffer;
+ private final int constantPoolCount;
+ private final List cpList = new ArrayList();
+
+ public ConstantParser(ByteBuffer buffer, int constantPoolCount)
+ {
+ this.buffer = buffer;
+ this.constantPoolCount = constantPoolCount;
+ }
+
+ public void parseConstantPool()
+ {
+ buffer.position(10);
+
+ for (int i = 1; i < constantPoolCount; i++)
+ {
+ int tag = buffer.get() & 0xFF;
+ switch (tag)
+ {
+ case ConstantType.CONSTANT_Utf8:
+ int length = buffer.getShort() & 0xFFFF;
+ byte[] bytes = new byte[length];
+ buffer.get(bytes);
+ String string = new String(bytes);
+ cpList.add("[" + i + "] CONSTANT_Utf8: " + string);
+ break;
+
+ case ConstantType.CONSTANT_Integer:
+ int value = buffer.getInt();
+ cpList.add("[" + i + "] CONSTANT_Integer: " + value);
+ break;
+
+ case ConstantType.CONSTANT_Float:
+ float floatValue = buffer.getFloat();
+ cpList.add("[" + i + "] CONSTANT_Float: " + floatValue);
+ break;
+
+ case ConstantType.CONSTANT_Long:
+ long longValue = buffer.getLong();
+ cpList.add("[" + i + "] CONSTANT_Long: " + longValue);
+ i++;
+ break;
+
+ case ConstantType.CONSTANT_Double:
+ double doubleValue = buffer.getDouble();
+ cpList.add("[" + i + "] CONSTANT_Double: " + doubleValue);
+ i++;
+ break;
+
+ case ConstantType.CONSTANT_Class:
+ int nameIndex = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_Class: #" + nameIndex);
+ break;
+
+ case ConstantType.CONSTANT_String:
+ int stringIndex = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_String: #" + stringIndex);
+ break;
+
+ case ConstantType.CONSTANT_Fieldref:
+ case ConstantType.CONSTANT_Methodref:
+ case ConstantType.CONSTANT_InterfaceMethodref:
+ int classIndex = buffer.getShort() & 0xFFFF;
+ int nameAndTypeIndex = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_" + getRefTypeName(tag) + ": #" + classIndex + ".#" + nameAndTypeIndex);
+ break;
+
+ case ConstantType.CONSTANT_NameAndType:
+ int nameIndex1 = buffer.getShort() & 0xFFFF;
+ int descriptorIndex = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_NameAndType: #" + nameIndex1 + ":#" + descriptorIndex);
+ break;
+
+ case ConstantType.CONSTANT_MethodHandle:
+ int referenceKind = buffer.get() & 0xFF;
+ int referenceIndex = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_MethodHandle: " + referenceKind + ":#" + referenceIndex);
+ break;
+
+ case ConstantType.CONSTANT_MethodType:
+ int descriptorIndex1 = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_MethodType: #" + descriptorIndex1);
+ break;
+
+ case ConstantType.CONSTANT_InvokeDynamic:
+ int bootstrapMethodAttrIndex = buffer.getShort() & 0xFFFF;
+ int nameAndTypeIndex3 = buffer.getShort() & 0xFFFF;
+ cpList.add("[" + i + "] CONSTANT_InvokeDynamic: #" + bootstrapMethodAttrIndex + ":#" + nameAndTypeIndex3);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown constant pool tag " + tag);
+ }
+ }
+ }
+
+ private String getRefTypeName(int tag)
+ {
+ switch (tag)
+ {
+ case ConstantType.CONSTANT_Fieldref:
+ return "Fieldref";
+ case ConstantType.CONSTANT_Methodref:
+ return "Methodref";
+ case ConstantType.CONSTANT_InterfaceMethodref:
+ return "InterfaceMethodref";
+ default:
+ return "Unknown";
+ }
+ }
+
+ public List getCpList()
+ {
+ return cpList;
+ }
+ }
+
+ private interface ConstantType
+ {
+ public static final byte CONSTANT_Utf8 = 1;
+ public static final byte CONSTANT_Class = 7;
+ public static final byte CONSTANT_Fieldref = 9;
+ public static final byte CONSTANT_Methodref = 10;
+ public static final byte CONSTANT_InterfaceMethodref = 11;
+ public static final byte CONSTANT_String = 8;
+ public static final byte CONSTANT_Integer = 3;
+ public static final byte CONSTANT_Float = 4;
+ public static final byte CONSTANT_Long = 5;
+ public static final byte CONSTANT_Double = 6;
+ public static final byte CONSTANT_NameAndType = 12;
+ public static final byte CONSTANT_MethodHandle = 15;
+ public static final byte CONSTANT_MethodType = 16;
+ public static final byte CONSTANT_InvokeDynamic = 18;
+ }
+}
diff --git a/plugins/java/ExampleStringDecrypter.java b/plugins/java/ExampleStringDecrypter.java
index cc7d3dab1..8e71a3b43 100644
--- a/plugins/java/ExampleStringDecrypter.java
+++ b/plugins/java/ExampleStringDecrypter.java
@@ -1,68 +1,82 @@
import java.lang.reflect.Field;
import java.util.List;
+
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
+import the.bytecode.club.bytecodeviewer.*;
import the.bytecode.club.bytecodeviewer.api.*;
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialog;
-import static the.bytecode.club.bytecodeviewer.Constants.nl;
+import static the.bytecode.club.bytecodeviewer.Constants.NL;
/**
- ** This is an example of a String Decrypter Java Plugin for BCV.
- **
- ** @author [Your-Name-Goes-Here]
+ * * This is an example of a String Decrypter Java Plugin for BCV.
+ * *
+ * * @author [Your-Name-Goes-Here]
**/
-public class ExampleStringDecrypter extends Plugin {
+public class ExampleStringDecrypter extends Plugin
+{
@Override
- public void execute(List classNodesList) {
+ public void execute(List classNodesList)
+ {
PluginConsole gui = new PluginConsole("Example String Decrypter Java Edition");
- MultipleChoiceDialog dialog = new MultipleChoiceDialog("Bytecode Viewer - WARNING",
- "WARNING: This will load the classes into the JVM and execute the initialize function"
- + nl + "for each class. IF THE FILE YOU'RE LOADING IS MALICIOUS, DO NOT CONTINUE.",
- new String[]{"Continue", "Cancel"});
+ MultipleChoiceDialog dialog = new MultipleChoiceDialog("Bytecode Viewer - WARNING", "WARNING: This will load the classes into the JVM and execute the initialize function" + NL +
+ "for each class. IF THE FILE YOU'RE LOADING IS MALICIOUS, DO NOT CONTINUE.", new String[]{"Continue", "Cancel"});
- if (dialog.promptChoice() == 0) {
+ if (dialog.promptChoice() == 0)
+ {
boolean needsWarning = false;
-
- for (ClassNode cn : classNodesList) {
- try {
+
+ for (ClassNode cn : classNodesList)
+ {
+ try
+ {
//load the class node into the classloader
BCV.getClassNodeLoader().addClass(cn);
-
- for (Object o : cn.fields.toArray()) {
+
+ for (Object o : cn.fields.toArray())
+ {
FieldNode f = (FieldNode) o;
-
+
//if the class contains the field z, get the class object from the class node
//then print out the value of the fields inside the class
//if the strings get decrypted on init, this allows you to dump the current values
-
- if (f.name.equals("z")) {
- try {
- for (Field f2 : BCV.getClassNodeLoader().nodeToClass(cn).getFields()) {
+ if (f.name.equals("z"))
+ {
+ try
+ {
+ for (Field f2 : BCV.getClassNodeLoader().nodeToClass(cn).getFields())
+ {
String s = (String) f2.get(null);
if (s != null && !s.isEmpty())
gui.appendText(cn + ":" + s);
}
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored)
+ {
}
}
}
- } catch (Exception e) {
+ }
+ catch (Exception e)
+ {
gui.appendText("Failed loading class " + cn.name);
e.printStackTrace();
needsWarning = true;
}
}
-
- if (needsWarning) {
- BytecodeViewer.showMessage("Some classes failed to decrypt, if you'd like to decrypt all of them\n"
- + "makes sure you include ALL the libraries it requires.");
+
+ if (needsWarning)
+ {
+ BytecodeViewer.showMessage("Some classes failed to decrypt, if you'd like to decrypt all of them" + NL +
+ "makes sure you include ALL the libraries it requires.");
}
gui.setVisible(true);
}
}
+
}
diff --git a/plugins/java/Skeleton.java b/plugins/java/Skeleton.java
index 4177905b3..324444102 100644
--- a/plugins/java/Skeleton.java
+++ b/plugins/java/Skeleton.java
@@ -8,12 +8,15 @@
** @author [Your Name Goes Here]
**/
-public class Skeleton extends Plugin {
+public class Skeleton extends Plugin
+{
@Override
- public void execute(List classNodesList) {
+ public void execute(List classNodesList)
+ {
PluginConsole gui = new PluginConsole("Skeleton Title");
gui.setVisible(true);
gui.appendText("executed skeleton example");
}
-}
\ No newline at end of file
+
+}
diff --git a/plugins/java/XposedGenerator.java b/plugins/java/XposedGenerator.java
index a291dafed..70c44eed5 100644
--- a/plugins/java/XposedGenerator.java
+++ b/plugins/java/XposedGenerator.java
@@ -11,6 +11,7 @@
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
+
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.*;
import the.bytecode.club.bytecodeviewer.api.*;
@@ -18,39 +19,47 @@
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
/**
- ** This is an Xposed Generator Plugin, used to aid Reverse-Engineering.
- **
- ** @author jowasp
+ * * This is an Xposed Generator Plugin, used to aid Reverse-Engineering.
+ * *
+ * * @author jowasp
**/
-public class XposedGenerator extends Plugin {
-
+public class XposedGenerator extends Plugin
+{
+
private static final List methodsNames = new ArrayList<>();
private static final List cleanMethodsNames = new ArrayList<>();
private static String foundPckg;
- public XposedGenerator() {
- }
-
@Override
- public void execute(List classNodeList) {
+ public void execute(List classNodeList)
+ {
//Get actual file class content
ResourceViewer viewer = BytecodeViewer.getActiveResource();
- if (viewer == null) {
- BytecodeViewer.showMessage("Open A Class First");
+ if (viewer == null)
+ {
+ BytecodeViewer.showMessage("Open A Classfile First");
return;
}
String className = viewer.getName();
ClassNode classnode = BytecodeViewer.getCurrentlyOpenedClassNode();
+ if (classnode == null)
+ {
+ BytecodeViewer.showMessage("Open A Classfile First");
+ return;
+ }
+
//Call XposedGenerator class
- ParseChosenFileContent(className, classnode);
+ parseChosenFileContent(className, classnode);
}
- public static void ParseChosenFileContent(String classname, ClassNode classNode) {
- try {
+ public static void parseChosenFileContent(String classname, ClassNode classNode)
+ {
+ try
+ {
//Parse content - Extract methods after APK /JAR has been extracted
byte[] cont = ASMUtil.nodeToBytes(classNode);
@@ -63,6 +72,7 @@ public static void ParseChosenFileContent(String classname, ClassNode classNode)
String[] xposedTemplateTypes = {"Empty", "Parameters", "Helper"};
@SuppressWarnings({"unchecked", "rawtypes"})
JComboBox xposedTemplateList = new JComboBox(xposedTemplateTypes);
+
//Set results of parsed methods into a list
List methodsExtracted = ProcessContentExtractedClass(decomp);
String packgExtracted = ProcessContentExtractedPackage(decomp);
@@ -71,7 +81,8 @@ public static void ParseChosenFileContent(String classname, ClassNode classNode)
List cleanMethods;
//clear list
cleanMethods = ProcessCleanMethodsAll(methodsExtracted);
- if (!cleanMethods.isEmpty()) {
+ if (!cleanMethods.isEmpty())
+ {
JComboBox cb = new JComboBox<>(cleanMethods.toArray(new String[0]));
//Add Panel elements
@@ -82,49 +93,51 @@ public static void ParseChosenFileContent(String classname, ClassNode classNode)
myPanel.add(cb);
//output methods to pane box
- int result = JOptionPane.showConfirmDialog(null, myPanel,
- "Choose Template and Method for Xposed Module", JOptionPane.OK_CANCEL_OPTION);
+ int result = JOptionPane.showConfirmDialog(null, myPanel, "Choose Template and Method for Xposed Module", JOptionPane.OK_CANCEL_OPTION);
+ myPanel.remove();
- if (result == JOptionPane.OK_OPTION) {
+ if (result == JOptionPane.OK_OPTION)
+ {
//Read Chosen Class
Object cbItem = cb.getSelectedItem();
Object xPosedItem = xposedTemplateList.getSelectedItem();
System.out.println("SELECTED CLASS is" + cbItem);
- if (cbItem != null && xPosedItem != null) {
+ if (cbItem != null && xPosedItem != null)
+ {
String selectedClass = cbItem.toString();
String selectedXposedTemplate = xPosedItem.toString();
//WriteXposed Class with extracted data
- try {
+ try
+ {
WriteXposedModule(selectedClass, packgExtracted, classname, selectedXposedTemplate);
- } catch (IllegalArgumentException e) {
+ }
+ catch (IllegalArgumentException e)
+ {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Error" + e);
}
}
}
- } else {
+ }
+ else
+ {
JOptionPane.showMessageDialog(null, "Class Not Suitable");
}
- } catch (IllegalArgumentException e) {
+ }
+ catch (IllegalArgumentException e)
+ {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Error" + e);
}
-
}
- public static void WriteXposedModule(String functionToHook, String packageName, String classToHook,
- String template) {
- if (template != null && !template.equals("Empty")) {
- try {
-
- //TODO: Prompt save dialog
- File file = new File("./XposedClassTest.java");
-
- // if file doesn't exist, then create it
- if (!file.exists())
- file.createNewFile();
-
+ public static void WriteXposedModule(String functionToHook, String packageName, String classToHook, String template)
+ {
+ if (template != null && !template.equals("Empty"))
+ {
+ try
+ {
//Extract the package name only
String packageNameOnly = packageName.substring(8, packageName.length() - 2).trim();
String classToHookNameOnly = classToHook;
@@ -133,110 +146,119 @@ public static void WriteXposedModule(String functionToHook, String packageName,
String[] classClean = classToHookNameOnly.split("/");
String[] functionSplitValues = functionToHook.split("\\s+");
-
+
//select
String onlyClass = classClean[classClean.length - 1];
String onlyFunction = CleanUpFunction(functionSplitValues);
//Write Xposed Class
- String XposedClassText = "package androidpentesting.com.xposedmodule;" + "\r\n" +
- "import de.robv.android.xposed.IXposedHookLoadPackage;" + "\r\n" +
- "\r\n" +
- "import de.robv.android.xposed.XC_MethodHook;" + "\r\n" +
- "import de.robv.android.xposed.XposedBridge;" + "\r\n" +
- "import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;" + "\r\n" +
- "import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;" + "\r\n" +
- "\r\n" +
- "public class XposedClassTest implements IXposedHookLoadPackage {" + "\r\n" + "\r\n" +
- " public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {" + "\r\n" + "\r\n" +
- " String classToHook = " + "\"" + packageNameOnly + "." + onlyClass + "\";" +
- "\r\n" +
- " String functionToHook = " + "\"" + onlyFunction + "\";" + "\r\n" +
- " if (lpparam.packageName.equals(" + "\"" + packageNameOnly + "\"" + ")){" + "\r"
- + "\n" +
- " XposedBridge.log(" + "\" Loaded app: \" " + " + lpparam.packageName);" +
- "\r\n" + "\r\n" +
- " findAndHookMethod(" + "\"" + onlyClass + "\"" + ", lpparam.classLoader, " + " \"" + onlyFunction + "\"" + ", int.class," + "\r\n" +
- " new XC_MethodHook() {" + "\r\n" +
- " @Override" + "\r\n" +
- " protected void beforeHookedMethod(MethodHookParam param) throws "
- + "Throwable {" + "\r\n" +
- " //TO BE FILLED BY ANALYST" + "\r\n" +
- " }" + "\r\n" +
- " });" + "\r\n" +
- " }" + "\r\n" +
- " }" + "\r\n" +
- "}" + "\r\n";
- FileWriter fw = new FileWriter(file.getAbsoluteFile());
- BufferedWriter bw = new BufferedWriter(fw);
- bw.write(XposedClassText);
- bw.write("\r\n");
- bw.close();
-
- JOptionPane.showMessageDialog(null, "Xposed Module Generated");
- } catch (IOException e) {
- JOptionPane.showMessageDialog(null, "Error" + e);
+ String XposedClassText = "package androidpentesting.com.xposedmodule;" + "\r\n"
+ + "import de.robv.android.xposed.IXposedHookLoadPackage;" + "\r\n" + "\r\n"
+ + "import de.robv.android.xposed.XC_MethodHook;" + "\r\n"
+ + "import de.robv.android.xposed.XposedBridge;" + "\r\n"
+ + "import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;" + "\r\n"
+ + "import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;" + "\r\n" + "\r\n"
+ + "public class XposedClassTest implements IXposedHookLoadPackage {" + "\r\n" + "\r\n"
+ + " public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {" + "\r\n" + "\r\n"
+ + " String classToHook = " + "\"" + packageNameOnly + "." + onlyClass + "\";" + "\r\n"
+ + " String functionToHook = " + "\"" + onlyFunction + "\";" + "\r\n" + "\r\n"
+ + " if (lpparam.packageName.equals(" + "\"" + packageNameOnly + "\"" + ")){" + "\r\n"
+ + " XposedBridge.log(" + "\" Loaded app: \" " + " + lpparam.packageName);" + "\r\n" + "\r\n"
+ + " findAndHookMethod(" + "\"" + onlyClass + "\"" + ", lpparam.classLoader, " + " \"" + onlyFunction + "\"" + ", int.class," + "\r\n"
+ + " new XC_MethodHook() {" + "\r\n"
+ + " @Override" + "\r\n"
+ + " protected void beforeHookedMethod(MethodHookParam param) throws Throwable {" + "\r\n"
+ + " //TO BE FILLED BY ANALYST" + "\r\n"
+ + " }" + "\r\n"
+ + " });" + "\r\n"
+ + " }" + "\r\n"
+ + " }" + "\r\n"
+ + "}" + "\r\n";
+
+ PluginConsole gui = new PluginConsole("Xposed Code Generation");
+ gui.appendText(XposedClassText);
+ gui.setVisible(true);
+ }
+ catch (Exception e)
+ {
e.printStackTrace();
}
- } else {
+ }
+ else
+ {
JOptionPane.showMessageDialog(null, "Empty Template Chosen, Did Not Generate");
}
}
- private static List ProcessContentExtractedClass(String contentFile) {
+ private static List ProcessContentExtractedClass(String contentFile)
+ {
Scanner scanner = null;
- try {
+ try
+ {
scanner = new Scanner(contentFile);
//@TODO : Improve patterns to match other excepts 'public'
-
+
String regexclass = "public";
Pattern pattern = Pattern.compile(regexclass, Pattern.CASE_INSENSITIVE);
-
- while (scanner.hasNextLine()) {
+
+ while (scanner.hasNextLine())
+ {
String line = scanner.nextLine();
-
+
// process the line
Matcher matcher = pattern.matcher(line);
- while (matcher.find()) {
- if (matcher.group() != null) {
+ while (matcher.find())
+ {
+ if (matcher.group() != null)
+ {
System.out.println("find() found the pattern \"" + quote(line.trim()));
System.out.println("Function: " + CleanUpFunction(line.trim().split("\\s+")));
methodsNames.add(quote(line.trim()));
- } else {
+ }
+ else
+ {
methodsNames.add("No methods found");
}
}
}
- if (methodsNames.isEmpty()) {
+ if (methodsNames.isEmpty())
methodsNames.add("No methods found");
- } else {
+ else
return methodsNames;
- }
+
return methodsNames;
- } finally {
+ }
+ finally
+ {
if (scanner != null)
scanner.close();
}
}
- private static List ProcessCleanMethodsAll(List rawMethods) {
- for (String m : rawMethods) {
+ private static List ProcessCleanMethodsAll(List rawMethods)
+ {
+ for (String m : rawMethods)
+ {
//Exclude class declaration
//TODO:add a list containing all possible types
- if (!m.contains("extends") && (!m.contains("implements") && (m.contains("(")))) {
+ if (!m.contains("extends") && (!m.contains("implements") && (m.contains("("))))
+ {
cleanMethodsNames.add(m);
}
}
-
+
return cleanMethodsNames;
}
- private static String CleanUpFunction(String[] rawFunction) {
+ private static String CleanUpFunction(String[] rawFunction)
+ {
String onlyFunc = "functiondummy";
- for (String m : rawFunction) {
- if (m.contains("(")) {
+ for (String m : rawFunction)
+ {
+ if (m.contains("("))
+ {
String[] split = m.split("\\(")[0].split(" ");
return split[split.length - 1];
}
@@ -245,50 +267,65 @@ private static String CleanUpFunction(String[] rawFunction) {
return onlyFunc;
}
- private static String ProcessContentExtractedPackage(String contentFile) {
+ private static String ProcessContentExtractedPackage(String contentFile)
+ {
Scanner scanner = null;
- try {
+ try
+ {
scanner = new Scanner(contentFile);
String regexPkg = "package";
Pattern patternPkg = Pattern.compile(regexPkg, Pattern.CASE_INSENSITIVE);
String line = scanner.nextLine();
-
+
// process the line
Matcher matcher = patternPkg.matcher(line);
- while (matcher.find()) {
- if (matcher.group() != null) {
+ while (matcher.find())
+ {
+ if (matcher.group() != null)
+ {
System.out.println("find() found the pattern \"" + quote(line.trim()));
foundPckg = quote(line.trim());
- } else {
+ }
+ else
+ {
foundPckg = "";
}
}
-
+
try
{
if (foundPckg == null || foundPckg.isEmpty())
foundPckg = "No Package Found";
- } catch (NullPointerException e) {
- JOptionPane.showMessageDialog(null,
- "Error - no package was found in the selected class: " + e);
- } finally {
+ }
+ catch (NullPointerException e)
+ {
+ JOptionPane.showMessageDialog(null, "Error - no package was found in the selected class: " + e);
+ }
+ finally
+ {
scanner.close();
}
- } catch (IllegalArgumentException e) {
+ }
+ catch (IllegalArgumentException e)
+ {
JOptionPane.showMessageDialog(null, "Error" + e);
if (scanner != null)
scanner.close();
- } finally {
+ }
+ finally
+ {
if (scanner != null)
scanner.close();
}
-
+
return foundPckg;
}
- private static String quote(String aText) {
+ private static String quote(String aText)
+ {
String QUOTE = "'";
return QUOTE + aText + QUOTE;
}
+
}
diff --git a/pom.xml b/pom.xml
index de7b3cbec..1f6ece39b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
the.bytecode.clubBytecode-Viewer
- 2.11.2
+ 2.13.1
@@ -13,45 +13,48 @@
UTF-8
- 23.0.0
- 4.9.3
- 2.6.1
- 9.3
- 0.2.0
+ 24.1.0
+ 2.11.0
+ 9.7
+ 0.2.21.0bcv0.1521.9.12
- 1.5.0
- 1.15
- 3.1.6
- 1.21
- 2.11.0
- 3.12.0
- 1.9
- 2.7.3
- 0.3.4
- 5.2.1.Final
- v49
- 4281855
- 2.9.0
- 31.1-jre
+ 1.9.0
+ 1.17.1
+ 3.1.12
+ 1.27.1
+ 2.16.1
+ 3.17.0
+ 1.12.0
+ 3.0.2
+ 0.4.1
+ 6.3.9.Final
+ 2.4.22
+ e0d44f4
+ 2.11.0
+ 33.3.0-jre
+ 2.2.04.2
- 1.3.4
- 3.1.6
+ 1.4.71.6.6bcv3.4.1.321.2.0
- 3.2
- 0.2.0
+ 3.4
+ 0.2.20.6.0
- 3.2.0
+ 3.5.22.1.1
- 1.7.36
- 2.5.2
- 1.30
+ 2.0.16
+ 3.0.8
+ 1.34.11.0.3
- 0.2.2
+ a8f700b1.1.4c
+ 3.26.2
+ 1.0.1
+ 1.7
+ 1.2.0
@@ -67,9 +70,18 @@
jitpack.iohttps://jitpack.io
+
+ sonatype-snapshots
+ https://oss.sonatype.org/content/repositories/snapshots/
+
+
+ com.konloch
+ HTTPRequest
+ ${httprequest.version}
+ org.jetbrainsannotations
@@ -210,7 +222,7 @@
org.codehaus.janinojanino
- ${janino.version}
+ ${commons-compiler.version}org.jd
@@ -234,8 +246,13 @@
org.exbin.auxiliary
- paged_data
- ${paged-data.version}
+ binary_data
+ ${binary-data.version}
+
+
+ org.exbin.auxiliary
+ binary_data-array
+ ${binary-data.version}org.bitbucket.mstrobel
@@ -268,19 +285,19 @@
${slf4j.version}
- org.smali
+ com.android.tools.smalismali${smali.version}
- org.smali
- baksmali
+ com.android.tools.smali
+ smali-baksmali${smali.version}
- org.yaml
- snakeyaml
- ${snakeyaml.version}
+ com.konloch
+ safeyaml
+ ${safeyaml.version}xpp3
@@ -311,14 +328,26 @@
io.github.skylotjadx-dex-input${jadx.version}
+
+
+ org.smali
+ baksmali
+
+ io.github.skylotjadx-smali-input${jadx.version}
+
+
+ org.smali
+ smali
+
+
- com.github.ThexXTURBOXx
+ de.femtopedia.dex2jardex2jar${dex2jar.version}
@@ -333,7 +362,7 @@
${darklaf-extensions-rsta.version}
- com.github.gotson
+ com.github.ThexXTURBOXxwebp-imageio${webp-imageio.version}
@@ -348,15 +377,29 @@
${treelayout.version}
- org.antlr
- antlr4
- ${antlr4.version}
-
-
- com.ibm.icu
- icu4j
-
-
+ com.github.javaparser
+ javaparser-core
+ ${java-parser.version}
+
+
+ com.github.javaparser
+ javaparser-symbol-solver-core
+ ${java-parser.version}
+
+
+ com.konloch
+ TaskManager
+ ${taskmanager.version}
+
+
+ com.google.googlejavaformat
+ google-java-format
+ ${google-java-format.version}
+
+
+ com.konloch
+ DiskLib
+ ${disk-lib.version}
@@ -374,10 +417,18 @@
-
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 3.5.0
+
+ checkstyle.xml
+
+
+ org.apache.maven.pluginsmaven-compiler-plugin
- 3.9.0
+ 3.13.0${maven.compiler.source}${maven.compiler.target}
@@ -387,7 +438,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.3.1
+ 3.10.0${maven.compiler.source}
@@ -395,7 +446,7 @@
org.apache.maven.pluginsmaven-shade-plugin
- 3.2.4
+ 3.6.0package
@@ -416,12 +467,34 @@
META-INF/*LICENSE*META-INF/*NOTICE*META-INF/MANIFEST.MF
+ LICENSE
+ license.txt
+ NOTICE
+
+
+
+
+ de.femtopedia.dex2jar:d2j-external
+ true
+
+ com/android/**
+ api_database/**
+ META-INF/services/**
+ LICENSE
+ r8-version.properties
+ org/objectweb/asm/MethodWriter.class
+
+
+
+
+ org.ow2.asm:asm
+
+ org/objectweb/asm/MethodWriter.class
-
+ the.bytecode.club.bytecodeviewer.BytecodeViewer${project.version}
@@ -429,8 +502,7 @@
${maven.compiler.target}
-
+
diff --git a/src/main/java/me/konloch/kontainer/io/DiskReader.java b/src/main/java/me/konloch/kontainer/io/DiskReader.java
deleted file mode 100644
index 7d676f1fc..000000000
--- a/src/main/java/me/konloch/kontainer/io/DiskReader.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package me.konloch.kontainer.io;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import the.bytecode.club.bytecodeviewer.util.EncodeUtils;
-
-/**
- * Used to load from the disk, optional caching
- *
- * @author Konloch
- */
-
-public class DiskReader {
-
- public static Random random = new Random();
- public static Map> map = new HashMap<>();
-
- /**
- * Used to load from file, allows caching
- */
- public synchronized static List loadArrayList(String fileName,
- boolean cache) {
- List array = new ArrayList<>();
- if (!map.containsKey(fileName)) {
- try {
- File file = new File(fileName);
- if (!file.exists()) // doesn't exist, return empty
- return array;
-
- try (FileReader fr = new FileReader(file);
- BufferedReader reader = new BufferedReader(fr)) {
- String add;
-
- while ((add = reader.readLine()) != null)
- array.add(add);
-
- }
-
- if (cache)
- map.put(fileName, array);
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- array = map.get(fileName);
- }
-
- return array;
-
- }
-
- /**
- * Used to load from file
- */
- public synchronized static String loadAsString(String fileName) throws Exception {
- StringBuilder s = new StringBuilder();
-
- try (FileReader fr = new FileReader(fileName);
- BufferedReader reader = new BufferedReader(fr)) {
- for (String add = reader.readLine(); add != null; add = reader.readLine()) {
- s.append(EncodeUtils.unicodeToString(add)).append(System.lineSeparator());
- }
- }
-
- return s.toString();
- }
-
- /**
- * Used to load a string via line number lineNumber = -1 means random.
- */
- public static String loadString(String fileName, int lineNumber,
- boolean cache) throws Exception {
-
- List array;
- if (!map.containsKey(fileName)) {
- array = new ArrayList<>();
- File file = new File(fileName);
-
- try (FileReader fr = new FileReader(file);
- BufferedReader reader = new BufferedReader(fr)) {
- String add;
-
- while ((add = reader.readLine()) != null)
- array.add(add);
- }
-
- if (cache)
- map.put(fileName, array);
- } else {
- array = map.get(fileName);
- }
-
- if (lineNumber == -1) {
- int size = array.size();
- return array.get(random.nextInt(size));
- } else
- return array.get(lineNumber);
- }
-
-}
diff --git a/src/main/java/me/konloch/kontainer/io/DiskWriter.java b/src/main/java/me/konloch/kontainer/io/DiskWriter.java
deleted file mode 100644
index be0b33f78..000000000
--- a/src/main/java/me/konloch/kontainer/io/DiskWriter.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package me.konloch.kontainer.io;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.PrintWriter;
-import java.util.Arrays;
-
-/**
- * This method will save to disk
- *
- * @author Konloch
- */
-
-public class DiskWriter {
-
- /**
- * Used to insert a difference string with preserving the file extension
- *
- * @param fileName The file name
- * @param difference Normally an integer
- * @return The filename with the difference inserted and the file extension
- * preserved
- */
- public static String insertFileName(String fileName, String difference) {
- String[] babe = fileName.split("\\.");
- int count = 0;
- int math = babe.length;
- StringBuilder m = new StringBuilder();
-
- for (String s2 : babe) {
- m.append(s2);
- if (math - 2 == count)
- m.append(difference).append(".");
- else if (math - 1 != count)
- m.append(".");
- count++;
- }
-
- return m.toString();
- }
-
- /**
- * Writes a new line to the file, if it doesn't exist it will automatically
- * create it.
- *
- * @param filename
- * @param fileContents
- * @param debug
- */
- public static synchronized void writeNewLine(String filename,
- byte[] fileContents, boolean debug) {
- new File(filename).getParentFile().mkdirs();
- String original = filename;
- int counter = 0;
-
- boolean saved = false;
- int failSafe = 0;
- while (!saved && failSafe++ <= 42069)
- {
- try (FileWriter fr = new FileWriter(filename, true);
- BufferedWriter bw = new BufferedWriter(fr);
- PrintWriter writer = new PrintWriter(bw)) {
- writer.println(Arrays.toString(fileContents));
- if (debug)
- System.out.println("Saved " + filename + " to disk");
- saved = true;
- } catch (Exception e) {
- if (debug)
- System.out.println("Failed saving, trying to save as "
- + filename);
- if (original.contains(".")) {
- filename = insertFileName(original, "" + counter);
- } else
- filename = original + counter;
- counter++;
- }
- }
- }
-
- /**
- * Writes a string to the file
- */
- public static void writeNewLine(String filename, String lineToWrite)
- {
- writeNewLine(filename, lineToWrite, false);
- }
-
- /**
- * Writes a string to the file
- */
- public static synchronized void writeNewLine(String filename,
- String lineToWrite, boolean debug) {
- new File(filename).getParentFile().mkdirs();
- String original = filename;
- int counter = 0;
-
- boolean saved = false;
- int failSafe = 0;
- while (!saved && failSafe++ <= 42069)
- {
- try (FileWriter fr = new FileWriter(filename, true);
- BufferedWriter bw = new BufferedWriter(fr);
- PrintWriter writer = new PrintWriter(bw)) {
- writer.println(lineToWrite);
- if (debug)
- System.out.println("Saved " + filename + ">" + lineToWrite
- + " to disk");
- saved = true;
- } catch (Exception e) {
- if (debug)
- System.out.println("Failed saving, trying to save as "
- + filename);
- if (original.contains(".")) {
- filename = insertFileName(original, "" + counter);
- } else
- filename = original + counter;
- counter++;
- }
- }
- }
-
- /**
- * Deletes the original file if it exists, then writes the fileContents[] to
- * the file.
- *
- * @param filename
- * @param fileContents
- * @param debug
- */
- public static synchronized void replaceFileBytes(String filename,
- byte[] fileContents, boolean debug) {
- new File(filename).getParentFile().mkdirs();
- File f = new File(filename);
- if (f.exists())
- f.delete();
-
- String original = filename;
- int counter = 0;
-
- boolean saved = false;
- int failSafe = 0;
- while (!saved && failSafe++ <= 42069)
- {
- try (FileOutputStream stream = new FileOutputStream(filename))
- {
- stream.write(fileContents);
- stream.flush();
- if (debug)
- System.out.println("Saved " + filename + " to disk");
- saved = true;
- } catch (Exception e) {
- if (debug)
- System.out.println("Failed saving, trying to save as "
- + filename);
- if (original.contains(".")) {
- filename = insertFileName(original, "" + counter);
- } else
- filename = original + counter;
- counter++;
- }
- }
- }
-
- /**
- * Deletes the original file if it exists, then writes the lineToWrite to
- * the file.
- *
- * @param filename
- * @param lineToWrite
- * @param debug
- */
- public static synchronized void replaceFile(String filename,
- String lineToWrite, boolean debug) {
- new File(filename).getParentFile().mkdirs();
- File f = new File(filename);
- if (f.exists())
- f.delete();
- String original = filename;
- int counter = 0;
-
- boolean saved = false;
- int failSafe = 0;
- while (!saved && failSafe++ <= 42069)
- {
- try (FileWriter fr = new FileWriter(filename, true);
- BufferedWriter bw = new BufferedWriter(fr);
- PrintWriter writer = new PrintWriter(bw)) {
- writer.println(lineToWrite);
- if (debug)
- System.out.println("Saved " + filename + ">" + lineToWrite
- + " to disk");
- saved = true;
- } catch (Exception e) {
- if (debug)
- System.out.println("Failed saving, trying to save as "
- + filename + "_");
- if (original.contains(".")) {
- filename = insertFileName(original, "" + counter);
- } else
- filename = original + counter;
- counter++;
- }
- }
- }
-
-}
diff --git a/src/main/java/me/konloch/kontainer/io/HTTPRequest.java b/src/main/java/me/konloch/kontainer/io/HTTPRequest.java
deleted file mode 100644
index c3b1798ba..000000000
--- a/src/main/java/me/konloch/kontainer/io/HTTPRequest.java
+++ /dev/null
@@ -1,276 +0,0 @@
-package me.konloch.kontainer.io;
-
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.Proxy;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * A wrapper for Java SE classes to write/read an HTTP Request
- *
- * @author Konloch
- */
-
-public class HTTPRequest {
-
- public URL url;
- private int timeout = 30000;
- private String cookie;
- private String referer;
- private String postData;
- private String useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0";
- private Proxy proxy;
- private boolean setFollowRedirects = true;
- private BufferedReader reader;
- private DataOutputStream writer;
- private HttpURLConnection connection;
- private Set>> lastConnectionHeaders;
- private int statusCode;
-
- /**
- * Creates a new HTTPRequest object
- *
- * @param url
- */
- public HTTPRequest(URL url) {
- this.url = url;
- }
-
- /**
- * Sets a referer to send to the web server
- */
- public void setReferer(String referer) {
- this.referer = referer;
- }
-
- /**
- * Set a cookie string to send to the web server
- */
- public void setCookie(String cookie) {
- this.cookie = cookie;
- }
-
- /**
- * Sets post data to send to the web server
- */
- public void setPostData(String postData) {
- this.postData = postData;
- }
-
- /**
- * Sets a custom useragent, default 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0'
- */
- public void setUseragent(String useragent) {
- this.useragent = useragent;
- }
-
- /**
- * Sets the seconds till timeout, default 30,000 milliseconds
- */
- public void setTimeout(int timeout) {
- this.timeout = timeout;
- }
-
- /**
- * Sets a proxy to connect through
- */
- public void setProxy(Proxy proxy) {
- this.proxy = proxy;
- }
-
- /**
- * Used to get the headers the webserver sent on our last connection
- */
- public Set>> getLastConnectionHeaders() {
- return lastConnectionHeaders;
- }
-
- public int getStatusCode()
- {
- return statusCode;
- }
-
- /**
- * By default, follow redirects are enabled
- */
- public void setFollowRedirects(boolean setFollowRedirects) {
- this.setFollowRedirects = setFollowRedirects;
- }
-
- /**
- * Used to set up the connection to read the content.
- */
- private void setup() throws Exception {
- if (proxy != null)
- connection = (HttpURLConnection) url.openConnection(proxy);
- else
- connection = (HttpURLConnection) url.openConnection();
-
- if (cookie != null)
- connection.setRequestProperty("Cookie", cookie);
- if (referer != null)
- connection.addRequestProperty("Referer", referer);
-
- connection.setRequestProperty("User-Agent", useragent);
- connection.setReadTimeout(timeout);
- connection.setConnectTimeout(timeout);
- connection.setUseCaches(false);
- HttpURLConnection.setFollowRedirects(setFollowRedirects);
-
- if (postData != null) {
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- connection.setDoInput(true);
- writer = new DataOutputStream(connection.getOutputStream());
- writer.writeBytes(postData);
- writer.flush();
- }
-
- reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- }
-
- /**
- * Reads the entire page and returns a string array
- *
- * @return
- * @throws Exception
- */
- public String[] read() throws Exception {
- List st;
-
- try {
- setup();
-
- st = new ArrayList<>();
- String s;
- while ((s = reader.readLine()) != null)
- st.add(s);
-
- lastConnectionHeaders = connection.getHeaderFields().entrySet();
- statusCode = connection.getResponseCode();
- } catch (Exception e) {
- cleanup();
- throw e;
- } finally {
- cleanup();
- }
-
- return st.toArray(new String[0]);
- }
-
- /**
- * Reads as many lines as expected unless it reaches the end.
- *
- * @param linesToRead
- * @return
- * @throws Exception
- */
- public String[] read(int linesToRead) throws Exception {
- List st;
-
- try {
- setup();
-
- st = new ArrayList<>();
- for (int i = 0; i < linesToRead; i++) {
- String s = reader.readLine();
- if (s != null)
- st.add(s);
- }
-
- lastConnectionHeaders = connection.getHeaderFields().entrySet();
- statusCode = connection.getResponseCode();
- } catch (Exception e) {
- cleanup();
- throw e;
- } finally {
- cleanup();
- }
-
- return st.toArray(new String[0]);
- }
-
- /**
- * Only reads the first line
- *
- * @return
- * @throws Exception
- */
- public String readSingle() throws Exception {
- String s;
-
- try {
- setup();
-
- s = reader.readLine();
-
- lastConnectionHeaders = connection.getHeaderFields().entrySet();
- statusCode = connection.getResponseCode();
- } catch (Exception e) {
- cleanup();
- throw e;
- } finally {
- cleanup();
- }
-
- return s;
- }
-
- /**
- * Reads until it reaches the expected line then it returns it.
- *
- * @param linesToRead
- * @return
- * @throws Exception
- */
- public String readSingle(int linesToRead) throws Exception {
- String s;
-
- try {
- setup();
-
- for (int i = 0; i < linesToRead - 1; i++)
- reader.readLine();
-
- s = reader.readLine();
-
- lastConnectionHeaders = connection.getHeaderFields().entrySet();
- statusCode = connection.getResponseCode();
- } catch (Exception e) {
- cleanup();
- throw e;
- } finally {
- cleanup();
- }
-
- return s;
- }
-
- /**
- * Used to clean up the connection, closes the connections and nulls the objects
- */
- private void cleanup() {
- try {
- reader.close();
- } catch (Exception ignored) {
- }
- try {
- writer.close();
- } catch (Exception ignored) {
- }
- try {
- connection.disconnect();
- } catch (Exception ignored) {
- }
- reader = null;
- writer = null;
- connection = null;
- }
-
-}
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java b/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java
index afbebc35d..17181440b 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java
@@ -1,16 +1,27 @@
+/***************************************************************************
+ * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
+ * Copyright (C) 2014 Konloch - Konloch.com / BytecodeViewer.com *
+ * *
+ * 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 the.bytecode.club.bytecodeviewer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import javax.swing.SwingUtilities;
-import me.konloch.kontainer.io.DiskReader;
+import com.konloch.disklib.DiskReader;
+import com.konloch.taskmanager.TaskManager;
import org.apache.commons.io.FileUtils;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.api.BCV;
@@ -19,89 +30,69 @@
import the.bytecode.club.bytecodeviewer.bootloader.BootState;
import the.bytecode.club.bytecodeviewer.bootloader.InstallFatJar;
import the.bytecode.club.bytecodeviewer.bootloader.UpdateCheck;
+import the.bytecode.club.bytecodeviewer.cli.BCVCommandLine;
+import the.bytecode.club.bytecodeviewer.cli.CLICommand;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.components.ExtendedJOptionPane;
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialog;
+import the.bytecode.club.bytecodeviewer.gui.components.SearchableJTextArea;
+import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListIconRenderer;
-import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
-import the.bytecode.club.bytecodeviewer.obfuscators.mapping.Refactorer;
import the.bytecode.club.bytecodeviewer.plugin.PluginWriter;
import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
import the.bytecode.club.bytecodeviewer.resources.importing.ImportResource;
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
-import the.bytecode.club.bytecodeviewer.util.BootCheck;
-import the.bytecode.club.bytecodeviewer.util.ClassFileUtils;
-import the.bytecode.club.bytecodeviewer.util.LazyNameUtil;
-import the.bytecode.club.bytecodeviewer.util.MiscUtils;
-import the.bytecode.club.bytecodeviewer.util.PingBack;
-import the.bytecode.club.bytecodeviewer.util.SecurityMan;
+import the.bytecode.club.bytecodeviewer.util.*;
-import static javax.swing.JOptionPane.QUESTION_MESSAGE;
-import static the.bytecode.club.bytecodeviewer.Constants.DEV_MODE;
-import static the.bytecode.club.bytecodeviewer.Constants.FAT_JAR;
-import static the.bytecode.club.bytecodeviewer.Constants.VERSION;
-import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.*;
-/***************************************************************************
- * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
- * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
- * *
- * 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 . *
- ***************************************************************************/
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static the.bytecode.club.bytecodeviewer.Constants.*;
/**
* A lightweight Java Reverse Engineering suite, developed by Konloch - http://konloch.me
- *
+ *
* All you have to do is add a jar or class file into the workspace,
* select the file you want then it will start decompiling the class in the background.
* When it's done it will show the Source code, Bytecode and Hexcode of the class file you chose.
- *
+ *
* There is also a plugin system that will allow you to interact with the loaded classfiles.
* For example you can write a String deobfuscator, a malicious code searcher,
* or anything else you can think of.
- *
+ *
* You can either use one of the pre-written plugins, or write your own. It supports java scripting.
* Once a plugin is activated, it will send a ClassNode ArrayList of every single class loaded in the
* file system to the execute function, this allows the user to handle it completely using ASM.
- *
+ *
* Are you a Java Reverse Engineer? Or maybe you want to learn Java Reverse Engineering?
* Join The Bytecode Club, we're noob friendly, and censorship free.
* http://the.bytecode.club
- *
+ *
* TODO BUGS:
* + View>Visual Settings>Show Class Methods
* + Spam-clicking the refresh button will cause the swing thread to deadlock (Quickly opening resources used to also do this)
* This is caused by the ctrlMouseWheelZoom code, a temporary patch is just removing it worst case
- *
+ *
* TODO API BUGS:
* + All of the plugins that modify code need to include BytecodeViewer.updateAllClassNodeByteArrays();
* + All of the plugins that do any code changes should also include BytecodeViewer.refreshAllTabs();
* + Anything using getLoadedClasses() needs to be replaced with the new API
* + Anything using blindlySearchForClassNode() should instead search through the resource container search function
* + BCV's classLoader should be destroyed each time a resource is added or removed
- *
- * TODO DarkLAF Specific Bugs:
- * + JMenuBar can only be displayed on a JFrame, a work around is needed for this (Partially solved)
- *
+ *
* TODO IN-PROGRESS:
* + Resource Exporter/Save/Decompile As Zip needs to be rewritten
* + Finish dragging code
* + Finish right-click tab menu detection
* + Fix hook inject for EZ-Injection
- *
+ *
* TODO FEATURES:
* + On refresh save scroll position
* + Option to only compile currently viewed class (true by default)
@@ -113,7 +104,7 @@
* + Add decompile all as zip for CLI
* + Console on the Main Viewer UI
* + Font settings
- *
+ *
* TODO IDEAS:
* + App Bundle Support
* + Add JEB decompiler optionally, requires them to add jeb library jar
@@ -132,36 +123,35 @@
public class BytecodeViewer
{
- //TODO fix this for tab dragging & better tab controls
- public static boolean EXPERIMENTAL_TAB_CODE = false;
-
+
//the launch args called on BCV
public static String[] launchArgs;
-
+
//the GUI reference
public static MainViewerGUI viewer;
-
+
//All of the opened resources (Files/Classes/Etc)
- public static Map resourceContainers = new LinkedHashMap<>();
-
+ public static Map resourceContainers = new LinkedHashMap<>();
+
//All of the created processes (Decompilers/etc)
public static List createdProcesses = new ArrayList<>();
-
+
//Security Manager for dynamic analysis debugging
public static SecurityMan sm = new SecurityMan();
-
- //Refactorer
- public static Refactorer refactorer = new Refactorer();
-
+
//GSON Reference
- public static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
-
+ public static Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ //BCV CLI
+ public static final BCVCommandLine CLI = new BCVCommandLine();
+
//Threads
- private static final Thread versionChecker = new Thread(new UpdateCheck(), "Version Checker");
- private static final Thread pingBack = new Thread(new PingBack(), "Pingback");
- private static final Thread installFatJar = new Thread(new InstallFatJar(), "Install Fat-Jar");
- private static final Thread bootCheck = new Thread(new BootCheck(), "Boot Check");
-
+ private static final Thread VERSION_CHECKER = new Thread(new UpdateCheck(), "Version Checker");
+ private static final Thread PING_BACK = new Thread(new PingBack(), "Pingback");
+ private static final Thread INSTALL_FAT_JAR = new Thread(new InstallFatJar(), "Install Fat-Jar");
+ private static final Thread BOOT_CHECK = new Thread(new BootCheck(), "Boot Check");
+ private static final TaskManager TASK_MANAGER = new TaskManager();
+
/**
* Main startup
*
@@ -170,136 +160,156 @@ public class BytecodeViewer
public static void main(String[] args)
{
launchArgs = args;
-
- //welcome message
+
+ //CLI startup banner
System.out.print("Bytecode Viewer " + VERSION);
+
if (FAT_JAR)
System.out.print(" [Fat Jar]");
-
- System.out.println(" - Created by @Konloch");
- System.out.println("https://bytecodeviewer.com - https://the.bytecode.club");
+
+ System.out.println(" - https://bytecodeviewer.com\r\nCreated by @Konloch - https://konloch.com\r\nPresented by https://the.bytecode.club");
// Set the security manager
- try {
+ try
+ {
System.setSecurityManager(sm);
- } catch (Throwable t) {
+ }
+ catch (Throwable t)
+ {
System.err.println("Cannot set security manager! Are you on Java 18+ and have not enabled support for it?");
System.err.println("Because of this, you may be susceptible to some exploits!");
System.err.println("Either deal with it or allow it using the -Djava.security.manager=allow parameter.");
}
+ //init the CLI
+ CLI.init(launchArgs);
+
try
{
//precache settings file
SettingsSerializer.preloadSettingsFile();
-
+
//setup look and feel
- Configuration.lafTheme.setLAF();
-
+ if(!CLI.isCLI())
+ Configuration.lafTheme.setLAF();
+
//set swing specific system properties
System.setProperty("swing.aatext", "true");
-
+
//setup swing components
- viewer = new MainViewerGUI();
- SwingUtilities.updateComponentTreeUI(viewer);
-
+ if(!CLI.isCLI())
+ {
+ // Enable the native menu bar on macOS
+ System.setProperty("apple.laf.useScreenMenuBar", "true");
+
+ viewer = new MainViewerGUI();
+ //SwingUtilities.updateComponentTreeUI(viewer);
+ }
+
//load settings and set swing components state
SettingsSerializer.loadSettings();
Configuration.bootState = BootState.SETTINGS_LOADED;
-
+
//set translation language
if (!Settings.hasSetLanguageAsSystemLanguage)
MiscUtils.setLanguage(MiscUtils.guessLanguage());
-
- //handle CLI
- int CLI = CommandLineInput.parseCommandLine(args);
- if (CLI == CommandLineInput.STOP)
- return;
-
+
//load with shaded libraries
if (FAT_JAR)
{
- installFatJar.start();
+ INSTALL_FAT_JAR.start();
}
else //load through bootloader
{
- bootCheck.start();
- Boot.boot(args, CLI != CommandLineInput.GUI);
+ BOOT_CHECK.start();
+ Boot.boot(args);
}
-
+
//CLI arguments say spawn the GUI
- if (CLI == CommandLineInput.GUI)
+ if(!CLI.isCLI())
{
- BytecodeViewer.boot(false);
+ BytecodeViewer.boot();
Configuration.bootState = BootState.GUI_SHOWING;
}
- else //CLI arguments say keep it CLI
- {
- BytecodeViewer.boot(true);
- CommandLineInput.executeCommandLine(args);
- }
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
}
-
+
/**
* Boot after all of the libraries have been loaded
- *
- * @param cli is it running CLI mode or not
*/
- public static void boot(boolean cli)
+ public static void boot()
{
//delete files in the temp folder
cleanupAsync();
-
+
//shutdown hooks
Runtime.getRuntime().addShutdownHook(new Thread(() ->
{
for (Process proc : createdProcesses)
proc.destroy();
-
+
SettingsSerializer.saveSettings();
cleanup();
}, "Shutdown Hook"));
-
+
+ //start the background task manager
+ TASK_MANAGER.start();
+
//setup the viewer
viewer.calledAfterLoad();
-
+
//setup the recent files
Settings.resetRecentFilesMenu();
-
+
//ping back once on first boot to add to global user count
if (!Configuration.pingback)
{
- pingBack.start();
+ PING_BACK.start();
Configuration.pingback = true;
}
-
+
//version checking
if (viewer.updateCheck.isSelected() && !DEV_MODE)
- versionChecker.start();
-
+ VERSION_CHECKER.start();
+
//show the main UI
- if (!cli)
- viewer.setVisible(true);
-
+ viewer.setVisible(true);
+
//print startup time
- System.out.println("Start up took " + ((System.currentTimeMillis() - Configuration.start) / 1000) + " seconds");
-
+ System.out.println("Start up took " + ((System.currentTimeMillis() - Configuration.BOOT_TIMESTAMP) / 1000) + " seconds");
+
//request focus on GUI for hotkeys on start
- if (!cli)
- viewer.requestFocus();
-
+ viewer.requestFocus();
+
//open files from launch args
- if (!cli)
- if (launchArgs.length >= 1)
- for (String s : launchArgs)
- openFiles(new File[]{new File(s)}, true);
+ openFilesFromLaunchArguments();
}
-
+
+ private static void openFilesFromLaunchArguments()
+ {
+ if(launchArgs.length < 1)
+ return;
+
+ //parse input for commands
+ for (int i = 0; i < launchArgs.length; i++)
+ {
+ String fileInput = launchArgs[i];
+ CLICommand command = CLI.getCommand(fileInput);
+
+ if (command != null)
+ {
+ if(command.hasArgs)
+ i++;
+ }
+ else
+ openFiles(new File[]{new File(fileInput)}, true);
+ }
+ }
+
/**
* Adds a resource container to BCVs resource container list
*/
@@ -308,14 +318,17 @@ public static void addResourceContainer(ResourceContainer container)
resourceContainers.put(container.name, container);
SwingUtilities.invokeLater(() ->
{
- try {
+ try
+ {
viewer.resourcePane.addResourceContainer(container);
- } catch (Exception e) {
+ }
+ catch (Exception e)
+ {
e.printStackTrace();
}
});
}
-
+
/**
* Returns true if there is at least one file resource loaded
*/
@@ -323,7 +336,7 @@ public static boolean hasResources()
{
return !resourceContainers.isEmpty();
}
-
+
/**
* Returns true if there is currently a tab open with a resource inside of it
*/
@@ -331,7 +344,7 @@ public static boolean hasActiveResource()
{
return getActiveResource() != null;
}
-
+
/**
* Returns true if there is currently a tab open with a resource inside of it
*/
@@ -340,7 +353,7 @@ public static boolean isActiveResourceClass()
ResourceViewer resource = getActiveResource();
return resource instanceof ClassViewer;
}
-
+
/**
* Returns the currently opened & viewed resource
*/
@@ -348,7 +361,7 @@ public static ResourceViewer getActiveResource()
{
return BytecodeViewer.viewer.workPane.getActiveResource();
}
-
+
/**
* Returns the currently opened ClassNode
*
@@ -356,9 +369,25 @@ public static ResourceViewer getActiveResource()
*/
public static ClassNode getCurrentlyOpenedClassNode()
{
- return getActiveResource().resource.getResourceClassNode();
+ return getActiveClass().resource.getResourceClassNode();
+ }
+
+ /**
+ * Returns the last opened resource class
+ */
+ public static ResourceViewer getActiveClass()
+ {
+ return BytecodeViewer.viewer.workPane.getLastActiveClass();
}
-
+
+ /**
+ * Returns true if the active class is not null
+ */
+ public static boolean isActiveClassActive()
+ {
+ return getActiveClass() != null;
+ }
+
/**
* Returns the ClassNode by the specified name
*
@@ -373,14 +402,14 @@ public static ClassNode blindlySearchForClassNode(String name)
for (ResourceContainer container : resourceContainers.values())
{
ClassNode node = container.getClassNode(name);
-
+
if (node != null)
return node;
}
-
+
return null;
}
-
+
/**
* Returns the resource container by the specific name
*/
@@ -389,10 +418,10 @@ public static ResourceContainer getFileContainer(String name)
for (ResourceContainer container : resourceContainers.values())
if (container.name.equals(name))
return container;
-
+
return null;
}
-
+
/**
* Returns all of the loaded resource containers
*/
@@ -400,7 +429,7 @@ public static Collection getResourceContainers()
{
return resourceContainers.values();
}
-
+
/**
* Grabs the file contents of the loaded resources.
*
@@ -415,10 +444,10 @@ public static byte[] getFileContents(String name)
for (ResourceContainer container : resourceContainers.values())
if (container.resourceFiles.containsKey(name))
return container.resourceFiles.get(name);
-
+
return null;
}
-
+
/**
* Grab the byte array from the loaded Class object by getting the resource from the classloader
*/
@@ -426,10 +455,10 @@ public static byte[] getClassFileBytes(Class> clazz) throws IOException
{
return ClassFileUtils.getClassFileBytes(clazz);
}
-
+
/**
* Gets all of the loaded classes as an array list
- *
+ *
* TODO: remove this and replace it with:
* BytecodeViewer.getResourceContainers().forEach(container -> {
* execute(new ArrayList<>(container.resourceClasses.values()));
@@ -441,15 +470,15 @@ public static byte[] getClassFileBytes(Class> clazz) throws IOException
public static List getLoadedClasses()
{
List a = new ArrayList<>();
-
+
for (ResourceContainer container : resourceContainers.values())
for (ClassNode c : container.resourceClasses.values())
if (!a.contains(c))
a.add(c);
-
+
return a;
}
-
+
/**
* Called any time refresh is called to automatically compile all of the compilable panes that're opened.
*/
@@ -457,7 +486,7 @@ public static boolean autoCompileSuccessful()
{
if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected())
return true;
-
+
try
{
return compile(false, false);
@@ -467,7 +496,7 @@ public static boolean autoCompileSuccessful()
return false;
}
}
-
+
/**
* Compile all of the compilable panes that're opened.
*
@@ -479,20 +508,20 @@ public static boolean compile(boolean message, boolean successAlert)
BytecodeViewer.updateBusyStatus(true);
boolean noErrors = true;
boolean actuallyTried = false;
-
+
for (java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers())
{
if (c instanceof ClassViewer)
{
ClassViewer cv = (ClassViewer) c;
-
+
if (noErrors && !cv.bytecodeViewPanel1.compile())
noErrors = false;
if (noErrors && !cv.bytecodeViewPanel2.compile())
noErrors = false;
if (noErrors && !cv.bytecodeViewPanel3.compile())
noErrors = false;
-
+
if (cv.bytecodeViewPanel1.textArea != null && cv.bytecodeViewPanel1.textArea.isEditable())
actuallyTried = true;
if (cv.bytecodeViewPanel2.textArea != null && cv.bytecodeViewPanel2.textArea.isEditable())
@@ -501,7 +530,7 @@ public static boolean compile(boolean message, boolean successAlert)
actuallyTried = true;
}
}
-
+
if (message)
{
if (actuallyTried)
@@ -514,34 +543,34 @@ public static boolean compile(boolean message, boolean successAlert)
BytecodeViewer.showMessage("You have no editable panes opened, make one editable and try again.");
}
}
-
+
BytecodeViewer.updateBusyStatus(false);
return true;
}
-
+
/**
* Opens a file, optional if it should append to the recent files menu
*
* @param files the file(s) you wish to open
* @param recentFiles if it should append to the recent files menu
*/
- public static void openFiles(final File[] files, boolean recentFiles)
+ public static void openFiles(File[] files, boolean recentFiles)
{
if (recentFiles)
{
for (File f : files)
if (f.exists())
Settings.addRecentFile(f);
-
+
SettingsSerializer.saveSettingsAsync();
}
-
+
BytecodeViewer.updateBusyStatus(true);
Configuration.needsReDump = true;
Thread t = new Thread(new ImportResource(files), "Import Resource");
t.start();
}
-
+
/**
* Starts the specified plugin
*
@@ -555,10 +584,10 @@ public static void startPlugin(File file)
Settings.removeRecentPlugin(file);
return;
}
-
+
try
{
- PluginWriter writer = new PluginWriter(DiskReader.loadAsString(file.getAbsolutePath()), file.getName());
+ PluginWriter writer = new PluginWriter(DiskReader.readString(file.getAbsolutePath()), file.getName());
writer.setSourceFile(file);
writer.setVisible(true);
}
@@ -566,10 +595,20 @@ public static void startPlugin(File file)
{
BytecodeViewer.handleException(e);
}
-
+
Settings.addRecentPlugin(file);
}
-
+
+ /**
+ * Returns the Task Manager
+ *
+ * @return the global task manager object
+ */
+ public static TaskManager getTaskManager()
+ {
+ return TASK_MANAGER;
+ }
+
/**
* Send a message to alert the user
*
@@ -579,7 +618,7 @@ public static void showMessage(String message)
{
ExtendedJOptionPane.showMessageDialog(viewer, message);
}
-
+
/**
* Send a message to alert the user
*/
@@ -587,16 +626,15 @@ public static String showInput(String message)
{
return ExtendedJOptionPane.showInputDialog(viewer, message);
}
-
+
/**
* Send a message to alert the user
*/
public static String showInput(String message, String title, String initialMessage)
{
- return (String) ExtendedJOptionPane.showInputDialog(viewer, message, title,
- QUESTION_MESSAGE, null, null, initialMessage);
+ return (String) ExtendedJOptionPane.showInputDialog(viewer, message, title, QUESTION_MESSAGE, null, null, initialMessage);
}
-
+
/**
* Alerts the user the program is running something in the background
*/
@@ -604,7 +642,7 @@ public static void updateBusyStatus(boolean busyStatus)
{
viewer.updateBusyStatus(busyStatus);
}
-
+
/**
* Clears all active busy status icons
*/
@@ -612,7 +650,7 @@ public static void clearBusyStatus()
{
viewer.clearBusyStatus();
}
-
+
/**
* Returns true if there are no loaded resource classes
*/
@@ -623,10 +661,10 @@ public static boolean promptIfNoLoadedClasses()
BytecodeViewer.showMessage(TranslatedStrings.FIRST_OPEN_A_CLASS.toString());
return true;
}
-
+
return false;
}
-
+
/**
* Returns true if there are no loaded resource classes
*/
@@ -637,10 +675,10 @@ public static boolean promptIfNoLoadedResources()
BytecodeViewer.showMessage(TranslatedStrings.FIRST_OPEN_A_RESOURCE.toString());
return true;
}
-
+
return false;
}
-
+
/**
* Handle the exception by creating a new window for bug reporting
*/
@@ -648,15 +686,18 @@ public static void handleException(Throwable t)
{
handleException(t, ExceptionUI.KONLOCH);
}
-
+
/**
* Handle the exception by creating a new window for bug reporting
*/
public static void handleException(Throwable t, String author)
{
- new ExceptionUI(t, author);
+ if(CLI.isCLI())
+ t.printStackTrace();
+ else
+ new ExceptionUI(t, author);
}
-
+
/**
* Refreshes the title on all of the opened tabs
*/
@@ -664,32 +705,35 @@ public static void updateAllClassNodeByteArrays()
{
resourceContainers.values().forEach(ResourceContainer::updateClassNodeBytes);
}
-
+
/**
* Refreshes the title on all of the opened tabs
*/
public static void refreshAllTabTitles()
{
- for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
+ for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
- ResourceViewer viewer = ((TabbedPane) BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i)).resource;
- viewer.refreshTitle();
+ //ResourceViewer viewer = ((TabbedPane) BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i)).resource;
+ //viewer.refreshTitle();
+ //TODO
}
}
-
+
/**
- * Refreshes the title on all of the opened tabs
+ * Refreshes all the opened tabs
*/
public static void refreshAllTabs()
{
- new Thread(()->
+ new Thread(() ->
{
updateBusyStatus(true);
+
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
- ResourceViewer viewer = ((TabbedPane) BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i)).resource;
+ ResourceViewer viewer = (ResourceViewer) BytecodeViewer.viewer.workPane.tabs.getComponentAt(i);
viewer.refresh(null);
}
+
updateBusyStatus(false);
}, "Refresh All Tabs").start();
}
@@ -703,17 +747,16 @@ public static void resetWorkspace(boolean ask)
{
if (ask)
{
- MultipleChoiceDialog dialog = new MultipleChoiceDialog(TranslatedStrings.RESET_TITLE.toString(),
- TranslatedStrings.RESET_CONFIRM.toString(),
- new String[]{TranslatedStrings.YES.toString(), TranslatedStrings.NO.toString()});
-
+ MultipleChoiceDialog dialog = new MultipleChoiceDialog(TranslatedStrings.RESET_TITLE.toString(), TranslatedStrings.RESET_CONFIRM.toString(),
+ new String[]{TranslatedStrings.YES.toString(), TranslatedStrings.NO.toString()});
+
if (dialog.promptChoice() != 0)
return;
}
-
+
resetWorkspace();
}
-
+
/**
* Resets the workspace
*/
@@ -727,7 +770,7 @@ public static void resetWorkspace()
BCV.getClassNodeLoader().clear();
ResourceListIconRenderer.iconCache.clear();
}
-
+
/**
* Clears the temp directory
*/
@@ -742,18 +785,55 @@ public static void cleanupAsync()
*/
public static void cleanup()
{
- File tempF = new File(tempDirectory);
+ File tempF = new File(TEMP_DIRECTORY);
- try {
+ try
+ {
FileUtils.deleteDirectory(tempF);
- } catch (Exception ignored) { }
+ }
+ catch (Exception ignored)
+ {
+ }
while (!tempF.exists()) // keep making dirs
tempF.mkdir();
}
-
+
/**
* because Smali and Baksmali System.exit if it failed
*/
- public static void exit(int i) { }
+ public static void exit(int i)
+ {
+ }
+
+ /**
+ * Updates all UI components fonts.
+ *
+ * @param font The font to change everything to.
+ * @implNote {@link SearchableRSyntaxTextArea} and {@link SearchableJTextArea}
+ * do not update until "Refresh" button is clicked.
+ */
+ public static void updateAllFonts(Font font)
+ {
+ Enumeration