commit 292551fbc7c5fcdd9699ffdc639b53ea60eeeafb Author: ZUNMX-WORKSPACE\36083 Date: Sat Jun 22 01:36:29 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/artifacts/jetbrains_cracker_jar.xml b/.idea/artifacts/jetbrains_cracker_jar.xml new file mode 100644 index 0000000..f34aaa2 --- /dev/null +++ b/.idea/artifacts/jetbrains_cracker_jar.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/out/artifacts/jetbrains_cracker_jar + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..40b8994 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..4b661a5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..424419c --- /dev/null +++ b/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + org.example + jetbrains_cracker + 1.0-SNAPSHOT + jar + + 1.8 + 1.8 + UTF-8 + + + + org.javassist + javassist + 3.28.0-GA + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + + top.zunmx.Main + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c9717f2 --- /dev/null +++ b/readme.md @@ -0,0 +1,185 @@ +# 引言 + +tb上买过教育版,结果用了一年多就封了,我记得之前用过ja-netfilter,用的是激活码,但是看起来直接替换的这种解决方案不太靠谱,现在是使用jetbra.in激活服务器激活,但是如果断网条件下,或者某一天这个网站down了,就没办法了,尝试过通过fiddler拦截激活服务器的响应,发现相同请求所获得的响应是不同的,加密算法也搞不懂,power.conf也看不懂,用着就不放心,还是社区版搞起吧,毕竟社区版也够用了。这里也是从52大神那里作为参考,想到的另一种方案,此方法仅供技术研究和讨论,切勿用于商业用途,否则后果自负。 + +经测试idea,datagrip,clion均过许可 + +# 版本 + +idea 2024.1.3 + +# 准备工作 + +jadx +idea +arthas +jdk + +# 思路 + +## 找到窗口 + +![image.png](https://www.zunmx.top/usr/uploads/2024/06/799549738.png) +![image.png](https://www.zunmx.top/usr/uploads/2024/06/3547910640.png) + +通过arthas获取awt的窗口`$ ognl '@java.awt.Window@getWindows()'`, +尝试关闭窗口`ognl '@javax.swing.JDialog@getWindows()[3].dispose()'` + +发现idea并没有完全退出,如果直接手动点退出的话,程序会结束 + +## 找到类名 + +上文中找到窗口了,类名其实也就出来了,但是如何判断`com.intellij.openapi.ui.impl.DialogWrapperPeerImpl$MyDialogWrapperImpl`具体在哪个包里,还是有些困难,通过`sc -d -f com.intellij.openapi.ui.impl.DialogWrapperPeerImpl`并没有找到,这里就是猜,猜到app-client.jar文件 + +## 分析代码 + +找到注入位置这里也踩了很多坑,比如说关闭了不该关闭的窗口,但是开发过java的应该都熟悉get/set方法,那么在setTitle时检查名称是不是就可以判断出这个窗口是不是应该关闭了? + +## 写代码 + +### 依赖 + +```xml + + + org.javassist + javassist + 3.28.0-GA + + +``` + +### 代码 + +```java +package org.example; + + +import javassist.*; + +import java.io.*; +import java.util.Enumeration; +import java.util.jar.*; + +public class ModifyDialogWrapperPeerImpl { + public static void main(String[] args) { + try { + ClassPool pool = ClassPool.getDefault(); + String jarPath = "G:\\test\\app-client-old.jar"; // jar文件路径 + String exportPath = "G:\\test\\app-client.jar"; // jar文件路径 + + pool.insertClassPath(jarPath); + CtClass ctClass = pool.get("com.intellij.openapi.ui.impl.DialogWrapperPeerImpl"); // 类名 + CtMethod setTitleMethod = ctClass.getDeclaredMethod("setTitle", + new CtClass[]{pool.get("java.lang.String")}); // 方法名 + setTitleMethod.insertBefore("{ System.out.println(\"即将打开窗口-->\"+$1);" + + "boolean isLicenseWindow = false;" + + "if($1.indexOf(\"许可证\")>-1) {isLicenseWindow=true; this.dispose();}" + + "if($1.indexOf(\"Licenses\")>-1) {isLicenseWindow=true; this.dispose();}" + + "if(isLicenseWindow)System.out.println(\"发现激活窗口,即将关闭\");" + + " }"); // 注入的代码 + String tempDir = "G:\\test\\modified_classes"; + ctClass.writeFile(tempDir); + ctClass.detach(); + + updateJarFile("G:\\test\\app-client-old.jar", tempDir, exportPath); + System.out.println("Method modified successfully."); + } catch (NotFoundException | CannotCompileException | IOException e) { + e.printStackTrace(); + } + } + + public static void updateJarFile(String originalJarPath, String modifiedClassesPath, String outputJarPath) throws IOException { + JarFile originalJar = new JarFile(originalJarPath); + FileOutputStream fos = new FileOutputStream(outputJarPath); + JarOutputStream jos = new JarOutputStream(fos); + + // 复制原始JAR中的所有文件到新的JAR中,跳过要修改的类文件 + Enumeration entries = originalJar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.getName().equals("com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.class")) { // 跳过要修改的类文件 + InputStream is = originalJar.getInputStream(entry); + jos.putNextEntry(new JarEntry(entry.getName())); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + jos.write(buffer, 0, bytesRead); + } + is.close(); + jos.closeEntry(); + } + } + originalJar.close(); + + // 添加修改后的类文件到新的JAR中 + File modifiedClassesDir = new File(modifiedClassesPath); + addModifiedFilesToJar(modifiedClassesDir, "", jos); + + jos.close(); + } + + private static void addModifiedFilesToJar(File source, String parent, JarOutputStream jos) throws IOException { + File[] files = source.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + addModifiedFilesToJar(file, parent + file.getName() + "/", jos); + } else { + FileInputStream fis = new FileInputStream(file); + jos.putNextEntry(new JarEntry(parent + file.getName())); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + jos.write(buffer, 0, bytesRead); + } + fis.close(); + jos.closeEntry(); + } + } + } +} +``` + +执行完成后,需要把生成的文件替换回去。 + +# 测试 + +![image.png](https://www.zunmx.top/usr/uploads/2024/06/2847269302.png) +![image.png](https://www.zunmx.top/usr/uploads/2024/06/709101366.png) + +# 注意 + +这里需要注意的是,上文代码中的Licenses和许可证是写死的,如果使用其他语言的语言包,需要手动修改一下。 + +jar文件路径 +idea == > app-client.jar +clion ==> app-client.jar +datagrip ==> app.jar +操作前需要先备份一下 + +经过测试,MyDialog是所有子窗口的类,如果一股脑全部关闭,设置,新建,打开,关于,你都将无法看到。 +但是用了这个方法,注册窗口也是无法显示出来的 + + +这种方法不能绕过插件的激活 + + +# 工程实例 + +![image.png](https://www.zunmx.top/usr/uploads/2024/06/3344638210.png) +![image.png](https://www.zunmx.top/usr/uploads/2024/06/92482229.png) + +[jc.zip](https://www.zunmx.top/usr/uploads/2024/06/1200008417.zip) + +运行: + +```bash +java -jar jetbrains_cracker-1.0-SNAPSHOT-jar-with-dependencies.jar +``` + +⚠ 仅以开源和学习精神,切勿用于商业用途。 + +# 结语 + + +请支持正版,仅供研究学习使用,请勿用于非法用途。 diff --git a/src/main/java/top/zunmx/MWindow.java b/src/main/java/top/zunmx/MWindow.java new file mode 100644 index 0000000..7f6c29f --- /dev/null +++ b/src/main/java/top/zunmx/MWindow.java @@ -0,0 +1,219 @@ +package top.zunmx; + +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.util.ArrayList; +import javax.swing.JFileChooser; + +public class MWindow extends Frame { + private static MyList logList; + private final MyButton selectDirButton; + private final MyButton clearListButton; + private final MyButton creckButton; + private MyTextArea textLabel; + private Thread searchThread; + public static boolean stop = false; + private static ArrayList file_paths = new ArrayList(); + + public static String findAppJar(File dir) { + File[] files = dir.listFiles(); + if (files != null) { + lab1: + for (File file : files) { + if (stop) break; + if (file.isDirectory()) { + if (file.getName().equalsIgnoreCase("lib")) { + File[] libFiles = file.listFiles(); + if (libFiles != null) { + for (File libFile : libFiles) { + if (stop) break; + if (libFile.isFile() && libFile.getName().equalsIgnoreCase("app.jar")) { + file_paths.add(file.getAbsolutePath()); + break lab1; + } + } + } + } else { + // 递归检查子目录 + if (stop) break; + if (findAppJar(file) != null) { + file_paths.add(file.getAbsolutePath()); + break lab1; + } + } + } + } + } + return null; + } + + private void buttonEnabled(boolean flag) { +// clearListButton.setEnabled(flag); + creckButton.setEnabled(flag); + selectDirButton.setEnabled(flag); + } + + + public MWindow() { + // 设置窗口标题 + super("JetBrains License Cracker V1.0"); + + // 设置窗口布局 + setLayout(new BorderLayout()); + + // 创建目录选择器按钮 + selectDirButton = new MyButton("Select Jetbrains Path"); + selectDirButton.addActionListener(e -> { + File directory = selectDirectory(); + if (directory != null && directory.isDirectory()) { + logList.removeAll(); + file_paths.clear(); + if (directory.exists() && directory.isDirectory()) { + logList.add("=============================[SEARCHING]================================="); + stop = false; + logList.add("[i] Searching for jetbrains product in " + directory.getAbsolutePath()); + new Thread(() -> { + this.buttonEnabled(false); + Thread searchThread = new Thread(() -> findAppJar(directory)); + searchThread.start(); + try { + searchThread.join(); + logList.add("=============================[FOUND " + file_paths.size() + " ITEM]================================="); + for (String filePath : file_paths) { + logList.add("[#] Found Product: " + filePath); + } + + if (stop) { + logList.add("[!] STOPPED"); + } + this.buttonEnabled(true); + } catch (InterruptedException ex) { + logList.add("[x] Exception: " + ex.getMessage()); + throw new RuntimeException(ex); + } + }).start(); + + + } else { + logList.add("[!] The specified path is not a directory or does not exist."); + } + } + }); + + // 创建清除列表按钮 + clearListButton = new MyButton("Reset / Clear List"); + clearListButton.addActionListener(e -> { + stop = true; + logList.removeAll(); + file_paths.clear(); + }); + + // 创建破解按钮 + creckButton = new MyButton("Crack!"); + creckButton.addActionListener(e -> { + if (file_paths.isEmpty()) { + logList.removeAll(); + logList.add("[!] No product found"); + logList.add("[!] Please select the Jetbrains software installation directory."); + return; + } + buttonEnabled(false); + new Thread(() -> { + Thread thread = new Thread(() -> { + for (String filePath : file_paths) { + try { + String[] split = filePath.split("\\\\"); + String product = split[filePath.split("\\\\").length - 2]; + logList.add("=============================[CRACKING " + product + "]================================="); + logList.add("[i] Cracking " + filePath); + ModifyDialogWrapperPeerImpl modifyDialogWrapperPeer = new ModifyDialogWrapperPeerImpl(); + modifyDialogWrapperPeer.crack_it(filePath, logList); + modifyDialogWrapperPeer = null; + System.gc(); + if (stop) { + logList.add("[!] STOPPED"); + break; + } + } catch (Exception except) { + logList.add("[!] Crack Failed: " + except.getMessage()); + } + } + }); + thread.start(); + try { + thread.join(); + } catch (InterruptedException ex) { + logList.add("[x] Exception: " + ex.getMessage()); + throw new RuntimeException(ex); + } + buttonEnabled(true); + }).start(); + + }); + textLabel = new MyTextArea("[i] Usage\n" + + " 1. Click [Select Jetbrains Path] Button\n" + + " 2. Select Jetbrains software installation directory\n" + + " eg. C:\\program files\\Jetbrains\n" + + " 3. Click [crack!] Button\n" + + " 4. Just a moment. \n\n" + + "[!] Recovery\n" + + " If you want to restore the original version,\n" + + " go to the lib\\JZC-backup directory under the product installation directory,\n" + + " find the backup files, and replace the files that in the lib directory.\n" + + "[!] Crack by Bypass the license window for trial\n" + + "[x] For code academic exchange only, do not use for commercial purposes\n" + + "[x] please delete the sample within 24 hours\n" + + "[i] Detection to prevent repeated cracking\n" + + "[i] Patch by ZunMX\n", 9, 30, TextArea.SCROLLBARS_VERTICAL_ONLY); + + // 创建文件列表 + logList = new MyList(); + + // 创建按钮面板 + Panel buttonPanel = new Panel(); + buttonPanel.setLayout(new FlowLayout()); + buttonPanel.add(selectDirButton); + buttonPanel.add(clearListButton); + buttonPanel.add(creckButton); + + // 添加组件到窗口 + add(buttonPanel, BorderLayout.NORTH); + add(logList, BorderLayout.CENTER); + add(textLabel, BorderLayout.SOUTH); + + // 设置窗口关闭行为 + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + this.setSize(750, 550); + Dimension windowSize = this.getSize(); + int x = (screenSize.width - windowSize.width) / 2; + int y = (screenSize.height - windowSize.height) / 2; + this.setLocation(x, y); + setVisible(true); + validate(); + repaint(); + } + + private File selectDirectory() { + File directory = null; + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle("Choose Jetbrains path"); +// fileChooser.setCurrentDirectory(new File("d:\\software\\jetbrains")); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int option = fileChooser.showOpenDialog(this); + if (option == JFileChooser.APPROVE_OPTION) { + directory = fileChooser.getSelectedFile(); + } + return directory; + } + + public static void main(String[] args) { + new MWindow(); + } +} diff --git a/src/main/java/top/zunmx/Main.java b/src/main/java/top/zunmx/Main.java new file mode 100644 index 0000000..bd1d73b --- /dev/null +++ b/src/main/java/top/zunmx/Main.java @@ -0,0 +1,13 @@ +package top.zunmx; + +import javax.swing.*; + +public class Main { + public static void main(String[] args) { +// ModifyDialogWrapperPeerImpl.crack_it("D:\\software\\JetBrains\\DataGrip 2024.1.2\\lib"); +// ModifyDialogWrapperPeerImpl.crack_it("D:\\software\\JetBrains\\CLion 2024.1.4\\lib"); + JOptionPane.showMessageDialog(null, + "此程序仅用于学术交流,切勿用于商业用途,您若花钱购买此产品,请立即举报商家!","注意",2); + new MWindow(); + } +} diff --git a/src/main/java/top/zunmx/ModifyDialogWrapperPeerImpl.java b/src/main/java/top/zunmx/ModifyDialogWrapperPeerImpl.java new file mode 100644 index 0000000..73b669d --- /dev/null +++ b/src/main/java/top/zunmx/ModifyDialogWrapperPeerImpl.java @@ -0,0 +1,190 @@ +package top.zunmx; + + +import javassist.*; + +import java.awt.*; +import java.io.*; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.*; +import java.util.Enumeration; +import java.util.jar.*; + +public class ModifyDialogWrapperPeerImpl { + public String filename = ""; + public String backupPath = ""; + public String middlePath = ""; + public String bkPath = ""; + public MyList logList; + private CtClass ctClass; + private URLClassLoader urlClassLoader; + private ClassPool pool = null; + private LoaderClassPath jarClassPath = null; + + public boolean deleteDirectory(String dirPath) { + File directory = new File(dirPath); + + // 检查目录是否存在 + if (!directory.exists()) { + logList.add("[#] Directory " + dirPath + " does not exist."); + return false; + } + + // 递归删除目录中的所有文件和子目录 + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file.getAbsolutePath()); + } else { + file.delete(); + logList.add(" |-[#] Deleted file: " + file.getAbsolutePath()); + } + } + } + + // 最后尝试删除空目录 + logList.add(" |-[#] Deleting directory: " + directory.getAbsolutePath()); + return directory.delete(); + } + + public void moveFile(String sourcePath, String targetPath) throws IOException { + Path src = Paths.get(sourcePath); + Path dest = Paths.get(targetPath); + Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING); + logList.add("[#] Moved backup file from " + sourcePath + " to " + targetPath); + } + + public String checker(String dirPath) { + File file = new File(dirPath + "\\" + "JCZ-backup"); + boolean directory = file.exists(); + if (!directory) { + logList.add("[#] JCZ-backup folder not found, creating..."); + file.mkdir(); + } + file = new File(dirPath + "\\" + "JCZ-middle"); + directory = file.exists(); + if (!directory) { + logList.add("[#] JCZ-middle folder not found, creating..."); + file.mkdir(); + } + backupPath = dirPath + "\\" + "JCZ-backup"; + middlePath = dirPath + "\\" + "JCZ-middle"; + if (new File(dirPath + "\\" + "app-client.jar").exists()) { + filename = "app-client.jar"; + logList.add("[#] app-client.jar found, using it."); + return dirPath + "\\" + "app-client.jar"; + } else { + filename = "app.jar"; + logList.add("[#] app.jar found, using it."); + return dirPath + "\\" + "app.jar"; + } + } + + public void crack_it(String dirPath, MyList log_list) throws Exception { + logList = log_list; + pool = ClassPool.getDefault(); + String d_jarPath = checker(dirPath); + File file = new File(backupPath + "\\" + filename); + if (file.exists()) { + bkPath = backupPath + "\\" + System.currentTimeMillis() + filename; + moveFile(backupPath + "\\" + filename, bkPath); // 将原来的文件备份起来 + } + moveFile(d_jarPath, backupPath + "\\" + filename); // 将原来的文件备份起来 + bkPath = backupPath + "\\" + filename; + String jarPath = backupPath + "\\" + filename; + File jar_url = new File(jarPath); + urlClassLoader = new URLClassLoader( + new URL[]{jar_url.toURI().toURL()} + ); + jarClassPath = new LoaderClassPath(urlClassLoader); + + pool.insertClassPath(jarClassPath); + logList.add("[#] Loading jar file: " + jarPath); + ctClass = pool.get("com.intellij.openapi.ui.impl.DialogWrapperPeerImpl"); // 类名 + CtMethod setTitleMethod = ctClass.getDeclaredMethod("setTitle", + new CtClass[]{pool.get("java.lang.String")}); // 方法名 + if (setTitleMethod.getMethodInfo().getCodeAttribute().getCode().length > 20) { + ctClass.detach(); + urlClassLoader.close(); + pool.removeClassPath(jarClassPath); + logList.add("[!] The file seems to have been cracked. EXIT"); + moveFile(bkPath, dirPath + "\\" + filename); + throw new RuntimeException("PRODUCT HAS BEEN CRACKED!"); + } + setTitleMethod.insertBefore("{ System.out.println(\"即将打开窗口-->\"+$1);" + + "boolean isLicenseWindow = false;" + + "if($1.indexOf(\"许可证\")>-1) {isLicenseWindow=true; this.dispose();}" + + "if($1.indexOf(\"Licenses\")>-1) {isLicenseWindow=true; this.dispose();}" + + "if(isLicenseWindow)System.out.println(\"发现激活窗口,即将关闭\");" + + "Exception e = new Exception(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n" + + "System.out.println(\"调用栈信息-->\");" + + "e.printStackTrace();" + + " }"); // 注入的代码 + logList.add("[#] Modifying class: " + ctClass.getName()); + ctClass.writeFile(middlePath); + ctClass.detach(); + updateJarFile(jarPath, middlePath, d_jarPath); + logList.add("[#] Exporting modified jar file: " + d_jarPath); + moveFile(jarPath, backupPath + "\\" + filename); + new File(d_jarPath).renameTo(new File(jarPath)); + logList.add("[#] Deleting middle folder: " + middlePath); + + + urlClassLoader.close(); + pool.removeClassPath(jarClassPath); + deleteDirectory(middlePath); + logList.add("[√] Cracked ^_^"); + + } + + public void updateJarFile(String originalJarPath, String modifiedClassesPath, String outputJarPath) throws IOException { + JarFile originalJar = new JarFile(originalJarPath); + FileOutputStream fos = new FileOutputStream(outputJarPath); + JarOutputStream jos = new JarOutputStream(fos); + + // 复制原始JAR中的所有文件到新的JAR中,跳过要修改的类文件 + Enumeration entries = originalJar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.getName().equals("com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.class")) { // 跳过要修改的类文件 + InputStream is = originalJar.getInputStream(entry); + jos.putNextEntry(new JarEntry(entry.getName())); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + jos.write(buffer, 0, bytesRead); + } + is.close(); + jos.closeEntry(); + } + } + originalJar.close(); + + // 添加修改后的类文件到新的JAR中 + File modifiedClassesDir = new File(modifiedClassesPath); + addModifiedFilesToJar(modifiedClassesDir, "", jos); + jos.close(); + fos.close(); + } + + private void addModifiedFilesToJar(File source, String parent, JarOutputStream jos) throws IOException { + File[] files = source.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + addModifiedFilesToJar(file, parent + file.getName() + "/", jos); + } else { + FileInputStream fis = new FileInputStream(file); + jos.putNextEntry(new JarEntry(parent + file.getName())); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + jos.write(buffer, 0, bytesRead); + } + fis.close(); + jos.closeEntry(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/top/zunmx/MyButton.java b/src/main/java/top/zunmx/MyButton.java new file mode 100644 index 0000000..925272e --- /dev/null +++ b/src/main/java/top/zunmx/MyButton.java @@ -0,0 +1,11 @@ +package top.zunmx; + +import javax.swing.*; +import java.awt.*; + +public class MyButton extends JButton { + public MyButton(String label) + { + super(label); + } +} diff --git a/src/main/java/top/zunmx/MyList.java b/src/main/java/top/zunmx/MyList.java new file mode 100644 index 0000000..67c301f --- /dev/null +++ b/src/main/java/top/zunmx/MyList.java @@ -0,0 +1,18 @@ +package top.zunmx; + +import java.awt.*; + +public class MyList extends List { + public MyList() { + super(); + this.setBackground(new Color(125,125,125)); + this.setForeground(new Color(255, 255, 255, 255)); + Font font = new Font("Monospaced", Font.PLAIN, 14); + this.setFont(font); + } + @Override + public void add(String item) { + super.add(item); + this.select(this.getItemCount() - 1); + } +} diff --git a/src/main/java/top/zunmx/MyTextArea.java b/src/main/java/top/zunmx/MyTextArea.java new file mode 100644 index 0000000..71410e9 --- /dev/null +++ b/src/main/java/top/zunmx/MyTextArea.java @@ -0,0 +1,15 @@ +package top.zunmx; + +import java.awt.*; + +public class MyTextArea extends TextArea { + public MyTextArea(String text, int rows, int columns, int style) { + super(text, rows, columns, style); + this.setEditable(false); + this.setBackground(new Color(125,125,125)); + this.setForeground(new Color(255, 251, 0, 255)); + this.setEditable(false); + this.setFont(new Font("Monospaced", Font.BOLD, 14)); + this.setFocusable(false); // 禁止获取焦点 + } +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..2f7a113 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: top.zunmx.Main +