Skip to content

Commit

Permalink
support single class files
Browse files Browse the repository at this point in the history
  • Loading branch information
GraxCode committed May 7, 2020
1 parent 00ba587 commit 9e150ee
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 59 deletions.
2 changes: 1 addition & 1 deletion src/me/nov/threadtear/Threadtear.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private static void configureEnvironment() throws Exception {
}

public void run(boolean verbose, boolean disableSecurity) {
ArrayList<Clazz> classes = listPanel.classList.classes;
List<Clazz> classes = listPanel.classList.classes;
ArrayList<Execution> executions = listPanel.executionList.getExecutions();
if (classes == null || classes.isEmpty()) {
JOptionPane.showMessageDialog(this, "You have to load a jar file first.");
Expand Down
13 changes: 8 additions & 5 deletions src/me/nov/threadtear/io/Clazz.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ public class Clazz {
public boolean transform = true;
public final ClassNode node;
public final JarEntry oldEntry;
public final JarFile jar;
public final Object inputFile;

public Clazz(ClassNode node, JarEntry oldEntry, JarFile jar) {
public Clazz(ClassNode node, JarEntry oldEntry, Object inputFile) {
super();
this.node = node;
this.oldEntry = oldEntry;
this.jar = jar;
this.inputFile = inputFile;
}

public InputStream streamOriginal() throws IOException {
JarFile jf = new JarFile(jar.getName());
return jf.getInputStream(jf.getEntry(oldEntry.getName()));
if (inputFile instanceof JarFile) {
JarFile jf = new JarFile(((JarFile) inputFile).getName());
return jf.getInputStream(jf.getEntry(oldEntry.getName()));
}
return new FileInputStream((File) inputFile);
}
}
71 changes: 40 additions & 31 deletions src/me/nov/threadtear/io/JarIO.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package me.nov.threadtear.io;

import java.io.*;
import java.util.ArrayList;
import java.util.*;
import java.util.jar.*;
import java.util.stream.Stream;
import java.util.zip.*;

import org.apache.commons.io.IOUtils;
import org.objectweb.asm.tree.ClassNode;
Expand Down Expand Up @@ -44,43 +45,52 @@ private static ArrayList<Clazz> readEntry(JarFile jar, JarEntry en, ArrayList<Cl

public static final String CERT_REGEX = "META-INF\\/.+(\\.SF|\\.RSA|\\.DSA)";

public static void saveAsJar(File original, File output, ArrayList<Clazz> classes, boolean noSignature, boolean watermark) {
public static void saveAsJar(File original, File output, List<Clazz> classes, boolean noSignature, boolean watermark) {
try {
JarOutputStream out = new JarOutputStream(new FileOutputStream(output));
JarFile jar = new JarFile(original);
Stream<JarEntry> str = jar.stream();
str.forEach(z -> {
Rewriting: {
JarFile jar;
try {
if (classes.stream().anyMatch(c -> c.oldEntry.getName().equals(z.getName()))) {
// ignore old class files
return;
}
String name = z.getName();
if (noSignature && name.matches(CERT_REGEX)) {
// export no certificates
return;
}
if (name.equals("META-INF/MANIFEST.MF")) {
byte[] manifest = IOUtils.toByteArray(jar.getInputStream(z));
if (noSignature) {
manifest = Manifest.patchManifest(manifest);
jar = new JarFile(original);
} catch (ZipException e) {
// not a zip file, has to be a class
break Rewriting;
}
Stream<JarEntry> str = jar.stream();
str.forEach(z -> {
try {
if (classes.stream().anyMatch(c -> c.oldEntry.getName().equals(z.getName()))) {
// ignore old class files
return;
}
String name = z.getName();
if (noSignature && name.matches(CERT_REGEX)) {
// export no certificates
return;
}
if (watermark) {
manifest = Manifest.watermark(manifest);
if (name.equals("META-INF/MANIFEST.MF")) {
byte[] manifest = IOUtils.toByteArray(jar.getInputStream(z));
if (noSignature) {
manifest = Manifest.patchManifest(manifest);
}
if (watermark) {
manifest = Manifest.watermark(manifest);
}
out.putNextEntry(cloneOldEntry(z, z.getName()));
out.write(manifest);
out.closeEntry();
return;
}
// export resources
out.putNextEntry(cloneOldEntry(z, z.getName()));
out.write(manifest);
out.write(IOUtils.toByteArray(jar.getInputStream(z)));
out.closeEntry();
return;
} catch (Exception e) {
Threadtear.logger.severe("Failed at entry " + z.getName() + " " + e.getClass().getName() + " " + e.getMessage());
}
// export resources
out.putNextEntry(cloneOldEntry(z, z.getName()));
out.write(IOUtils.toByteArray(jar.getInputStream(z)));
out.closeEntry();
} catch (Exception e) {
Threadtear.logger.severe("Failed at entry " + z.getName() + " " + e.getClass().getName() + " " + e.getMessage());
}
});
});
jar.close();
}
for (Clazz c : classes) {
try {
// add updated classes
Expand All @@ -91,7 +101,6 @@ public static void saveAsJar(File original, File output, ArrayList<Clazz> classe
Threadtear.logger.severe("Failed at class entry " + c.node.name + " " + e.getClass().getName() + " " + e.getMessage());
}
}
jar.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
Expand Down
4 changes: 2 additions & 2 deletions src/me/nov/threadtear/swing/dialog/JarAnalysis.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class JarAnalysis extends JDialog implements Opcodes {
private static final long serialVersionUID = 1L;
private JTextArea area;

public JarAnalysis(ArrayList<Clazz> classes) {
public JarAnalysis(List<Clazz> classes) {
setModalityType(ModalityType.APPLICATION_MODAL);
setTitle("Obfuscation analysis");
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
Expand Down Expand Up @@ -51,7 +51,7 @@ public JarAnalysis(ArrayList<Clazz> classes) {
});
}

private void analyze(ArrayList<Clazz> classes) {
private void analyze(List<Clazz> classes) {
if (classes == null) {
print("Open a jar file first.");
return;
Expand Down
2 changes: 1 addition & 1 deletion src/me/nov/threadtear/swing/handler/ILoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import java.io.File;

public interface ILoader {
public void onJarLoad(File input);
public void onFileDrop(File input);
}
4 changes: 2 additions & 2 deletions src/me/nov/threadtear/swing/handler/JarDropHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public boolean importData(TransferHandler.TransferSupport info) {
return false;
}
for (File jar : data) {
if (jar.getName().toLowerCase().endsWith(".jar")) {
loader.onJarLoad(jar);
if (jar.getName().toLowerCase().matches(".*(\\.jar|\\.class)")) {
loader.onFileDrop(jar);
return true;
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/me/nov/threadtear/swing/panel/ConfigurationPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.apache.commons.configuration2.*;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.io.FilenameUtils;

import com.github.weisj.darklaf.icons.IconLoader;

Expand Down Expand Up @@ -91,7 +92,7 @@ private JPanel createBottomButtons() {
}
JFileChooser jfc = new JFileChooser(inputFile.getParentFile());
jfc.setAcceptAllFileFilterUsed(false);
jfc.setSelectedFile(inputFile);
jfc.setSelectedFile(new File(FilenameUtils.removeExtension(inputFile.getAbsolutePath()) + ".jar"));
jfc.setDialogTitle("Save transformed jar archive");
jfc.setFileFilter(new FileNameExtensionFilter("Java Package (*.jar)", "jar"));
int result = jfc.showSaveDialog(this);
Expand Down Expand Up @@ -150,7 +151,7 @@ private void loadConfig(File input) {
if (config.containsKey("file")) {
File file = new File(config.getString("file"));
if (file.exists()) {
main.listPanel.classList.onJarLoad(file);
main.listPanel.classList.onFileDrop(file);
if (config.containsKey("ignored")) {
String[] ignored = config.getStringArray("ignored");
for (String ignore : ignored) {
Expand Down
49 changes: 34 additions & 15 deletions src/me/nov/threadtear/swing/tree/ClassTreePanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.List;
import java.util.jar.*;

import javax.swing.*;
import javax.swing.tree.*;

import org.apache.commons.io.FilenameUtils;
import org.objectweb.asm.tree.ClassNode;

import com.github.weisj.darklaf.components.loading.LoadingIndicator;
import com.github.weisj.darklaf.icons.IconLoader;

Expand All @@ -24,7 +30,7 @@ public class ClassTreePanel extends JPanel implements ILoader {
private static final long serialVersionUID = 1L;
private Threadtear threadtear;
public File inputFile;
public ArrayList<Clazz> classes;
public List<Clazz> classes;
public DefaultTreeModel model;
private ClassTree tree;
private JPanel outerPanel;
Expand Down Expand Up @@ -128,7 +134,7 @@ public class ClassTree extends JTreeWithHint {
private static final long serialVersionUID = 1L;

public ClassTree() {
super("Drag a java archive file here");
super("Drag a jar or class file here");
this.setRootVisible(false);
this.setShowsRootHandles(true);
this.setFocusable(true);
Expand Down Expand Up @@ -158,9 +164,10 @@ public void mouseClicked(MouseEvent e) {
}

@Override
public void onJarLoad(File input) {
public void onFileDrop(File input) {
this.remove(outerPanel);
LoadingIndicator loadingLabel = new LoadingIndicator("Loading class files... ", JLabel.CENTER);
String type = FilenameUtils.getExtension(input.getAbsolutePath());
LoadingIndicator loadingLabel = new LoadingIndicator("Loading class file(s)... ", JLabel.CENTER);
loadingLabel.setRunning(true);
this.add(loadingLabel, BorderLayout.CENTER);
this.invalidate();
Expand All @@ -170,16 +177,7 @@ public void onJarLoad(File input) {
SwingUtilities.invokeLater(() -> {
new Thread(() -> {
this.inputFile = input;
try {
this.classes = JarIO.loadClasses(input);
if (classes.stream().anyMatch(c -> c.oldEntry.getCertificates() != null)) {
JOptionPane.showMessageDialog(this,
"<html>Warning: File is signed and may not load correctly if already modified, remove the signature<br>(<tt>META-INF\\MANIFEST.MF</tt>) and certificates (<tt>META-INF\\*.SF/.RSA</tt>) first!",
"Signature warning", JOptionPane.WARNING_MESSAGE);
}
} catch (IOException e) {
e.printStackTrace();
}
this.loadFile(type);
loadTree(classes);
refreshIgnored();
model.reload();
Expand All @@ -198,8 +196,29 @@ public void onJarLoad(File input) {
}
}

private void loadFile(String type) {
try {
switch (type) {
case "jar":
this.classes = JarIO.loadClasses(inputFile);
if (classes.stream().anyMatch(c -> c.oldEntry.getCertificates() != null)) {
JOptionPane.showMessageDialog(this,
"<html>Warning: File is signed and may not load correctly if already modified, remove the signature<br>(<tt>META-INF\\MANIFEST.MF</tt>) and certificates (<tt>META-INF\\*.SF/.RSA</tt>) first!",
"Signature warning", JOptionPane.WARNING_MESSAGE);
}
break;
case "class":
ClassNode node = Conversion.toNode(Files.readAllBytes(inputFile.toPath()));
this.classes = Collections.singletonList(new Clazz(node, new JarEntry(node.name), inputFile));
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}

@SuppressWarnings("unchecked")
public void loadTree(ArrayList<Clazz> classes) {
public void loadTree(List<Clazz> classes) {
SortedTreeClassNode root = new SortedTreeClassNode("");
model = new DefaultTreeModel(root);
classes.forEach(c -> {
Expand Down

0 comments on commit 9e150ee

Please sign in to comment.