feat(plugins): 实现插件系统并优化加载过程

- 新增 BoxClassLoader、IClassTransformer、LoadingCorePlugin 等核心类
- 重构 Main 类,采用多线程和 ProgressBarManager 优化加载过程
- 完善 PluginLoader 类,支持核心插件和字节码转换器的加载- 重定向系统输出到 Log4j2,提高日志管理的灵活性
This commit is contained in:
tzdwindows 7
2025-02-10 15:32:22 +08:00
parent a8f9611db4
commit bf654b7ea3
9 changed files with 349 additions and 119 deletions

View File

@@ -24,11 +24,8 @@ repositories {
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation 'org.commonmark:commonmark:0.24.0'
// https://mvnrepository.com/artifact/org.commonjava.googlecode.markdown4j/markdown4j
implementation 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
@@ -42,16 +39,19 @@ dependencies {
implementation 'org.ow2.asm:asm-tree:7.1'
}
application {
mainClass = 'com.axis.innovators.box.Main'
}
jar {
manifest {
attributes 'Main-Class': 'com.axis.innovators.box.Main'
}
}
application {
mainClass = 'com.axis.innovators.box.Main'
applicationDefaultJvmArgs = [
"-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader"
]
}
test {
useJUnitPlatform()
}

View File

@@ -0,0 +1,33 @@
package com.axis.innovators.box;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.OutputStream;
import java.io.PrintStream;
/**
* 将输出传递给 Log4j2 的日志记录器
* @author tzdwindows 7
*/
public class Log4j2OutputStream extends OutputStream {
private static final Logger logger = LogManager.getLogger(Log4j2OutputStream.class);
@Override
public void write(int b) {
logger.info(String.valueOf((char) b));
}
@Override
public void write(byte[] b, int off, int len) {
logger.info(new String(b, off, len));
}
/**
* 重定向 System.out 和 System.err 到 Log4j2
*/
public static void redirectSystemStreams() {
System.setOut(new PrintStream(new Log4j2OutputStream()));
System.setErr(new PrintStream(new Log4j2OutputStream()));
}
}

View File

@@ -3,10 +3,7 @@ package com.axis.innovators.box;
import com.axis.innovators.box.events.GlobalEventBus;
import com.axis.innovators.box.events.SettingsLoadEvents;
import com.axis.innovators.box.events.SubscribeEvent;
import com.axis.innovators.box.gui.FridaWindow;
import com.axis.innovators.box.gui.LocalWindow;
import com.axis.innovators.box.gui.MainWindow;
import com.axis.innovators.box.gui.LoadIcon;
import com.axis.innovators.box.gui.*;
import com.axis.innovators.box.plugins.PluginLoader;
import com.axis.innovators.box.tools.LibraryLoad;
import com.axis.innovators.box.tools.SystemInfoUtil;
@@ -29,9 +26,10 @@ public class Main {
"tzdwindows 7"
};
/** 我是总任务数 **/
public static int totalTasks = 1;
public static int totalTasks = 0;
/** 我是当前任务数 **/
public static int completedTasks = 0;
public static ProgressBarManager progressBarManager = new ProgressBarManager("加载中...", totalTasks);
static {
try {
@@ -52,6 +50,7 @@ public class Main {
}
public static void main(String[] args) {
Log4j2OutputStream.redirectSystemStreams();
// 输出版本和作者信息
logger.info("Application Version: {}", VERSIONS);
logger.info("Authors: {}", String.join(", ", AUTHOR));
@@ -61,6 +60,11 @@ public class Main {
logger.info("OS Architecture: {}", SystemInfoUtil.getOSArch());
logger.info("CPU: {}", SystemInfoUtil.getCPUInfo());
logger.info("GPU: {}", SystemInfoUtil.getGPUInfo());
logger.info("Java Version: {}", System.getProperty("java.version"));
logger.info("Java Vendor: {}", System.getProperty("java.vendor"));
logger.info("Java Home: {}", System.getProperty("java.home"));
logger.info("Java Class Path: {}", System.getProperty("java.class.path"));
logger.info("ClassLoader.getSystemClassLoader(): {}", ClassLoader.getSystemClassLoader());
// 注册事件
GlobalEventBus.EVENT_BUS.register(new Main());
@@ -71,109 +75,66 @@ public class Main {
} catch (Exception e) {
logger.warn("Failed to load the system facade class", e);
}
Thread thread = new Thread(() -> {
try {
// 主任务1加载插件
progressBarManager.updateMainProgress(++completedTasks);
PluginLoader.loadPlugins();
logger.info("Loaded plugins");
JFrame loadingFrame = new JFrame("加载中...");
loadingFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
loadingFrame.setSize(400, 200);
loadingFrame.setLocationRelativeTo(null);
loadingFrame.setIconImage(LoadIcon.loadIcon("logo.png", 64).getImage());
progressBarManager.close();
JPanel loadingPanel = new JPanel(new BorderLayout());
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
JLabel statusLabel = new JLabel("Initializing...", SwingConstants.CENTER);
JLabel timeLabel = new JLabel("Time elapsed: 0s", SwingConstants.CENTER);
JLabel logoLabel = new JLabel(LoadIcon.loadIcon("logo.png", 64));
SwingUtilities.invokeLater(() -> {
try {
MainWindow ex = new MainWindow();
int id = 0;
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具",
"debug/debug.png",
"用于调试指定Windows工具的一个分类");
loadingPanel.add(logoLabel, BorderLayout.NORTH);
loadingPanel.add(progressBar, BorderLayout.CENTER);
loadingPanel.add(statusLabel, BorderLayout.SOUTH);
loadingPanel.add(timeLabel, BorderLayout.SOUTH);
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
"使用frida注入目标进程的脚本程序 " +
"\n作者tzdwindows 7", ++id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
FridaWindow fridaWindow = new FridaWindow(owner);
fridaWindow.setVisible(true);
}
}));
loadingFrame.add(loadingPanel);
loadingFrame.setVisible(true);
ex.addToolCategory(debugCategory);
new Thread(() -> {
long startTime = System.currentTimeMillis();
int classifyTask = 0;
MainWindow.ToolCategory aICategory = new MainWindow.ToolCategory("AI工具",
"ai/ai.png",
"人工智能/大语言模型");
new Thread(() -> {
while (completedTasks < totalTasks) {
completedTasks++;
final int progress = (int) ((completedTasks / (double) totalTasks) * 100);
aICategory.addTool(new MainWindow.ToolItem("本地AI执行工具", "ai/local/local_main.png",
"在本机对开源大语言模型进行推理" +
"\n作者tzdwindows 7", ++id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
LocalWindow dialog = new LocalWindow(owner);
dialog.setVisible(true);
}
}));
SwingUtilities.invokeLater(() -> {
progressBar.setValue(progress);
statusLabel.setText("Loading: " + progress + "%");
long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
timeLabel.setText("Time elapsed: " + elapsedTime + "s");
});
}
});
while (completedTasks < totalTasks) {
try {
if (classifyTask == 0){
PluginLoader.loadPlugins();
logger.info("Loaded plugins");
classifyTask++;
ex.addToolCategory(aICategory);
ex.initUI();
ex.setVisible(true);
} catch (Exception e) {
logger.error("There was a problem starting the main thread", e);
throw new RuntimeException(e);
}
// 非显示任务请添加在这添加
// ....
} catch (IOException e) {
logger.error("Failed to load plugins", e);
throw new RuntimeException(e);
}
});
} catch (Exception e) {
logger.error("Failed to load plugins", e);
throw new RuntimeException(e);
}
SwingUtilities.invokeLater(() -> {
loadingFrame.dispose();
try {
MainWindow ex = new MainWindow();
int id = 0;
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具",
"debug/debug.png",
"用于调试指定Windows工具的一个分类");
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
"使用frida注入目标进程的脚本程序 " +
"\n作者tzdwindows 7", ++id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
FridaWindow fridaWindow = new FridaWindow(owner);
fridaWindow.setVisible(true);
}
}));
// 在后面注册你自己的项或是添加你自己的分类
// ....
ex.addToolCategory(debugCategory);
MainWindow.ToolCategory aICategory = new MainWindow.ToolCategory("AI工具",
"ai/ai.png",
"人工智能/大语言模型");
aICategory.addTool(new MainWindow.ToolItem("本地AI执行工具", "ai/local/local_main.png",
"在本机对开源大语言模型进行推理" +
"\n作者tzdwindows 7", ++id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
LocalWindow dialog = new LocalWindow(owner);
dialog.setVisible(true);
}
}));
ex.addToolCategory(aICategory);
ex.initUI();
ex.setVisible(true);
} catch (Exception e) {
logger.error("There was a problem starting the main thread", e);
throw new RuntimeException(e);
}
});
}).start();
});
thread.setName("Main Thread");
thread.start();
}
}

View File

@@ -0,0 +1,109 @@
package com.axis.innovators.box.gui;
import javax.swing.*;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
/**
* 启动窗口的任务系统
* @author tzdwindows 7
*/
public class ProgressBarManager {
private JFrame loadingFrame;
private JProgressBar mainProgressBar;
private JProgressBar subProgressBar;
private JLabel statusLabel;
private JLabel timeLabel;
private long startTime;
private int totalTasks;
private int completedTasks;
private Map<String, Integer> subTasks = new HashMap<>();
public ProgressBarManager(String title, int totalTasks) {
this.totalTasks = totalTasks;
this.completedTasks = 0;
this.startTime = System.currentTimeMillis();
loadingFrame = new JFrame(title);
loadingFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
loadingFrame.setSize(400, 250);
loadingFrame.setLocationRelativeTo(null);
loadingFrame.setIconImage(LoadIcon.loadIcon("logo.png", 64).getImage());
JPanel loadingPanel = new JPanel(new BorderLayout());
mainProgressBar = new JProgressBar(0, 100);
mainProgressBar.setStringPainted(true);
subProgressBar = new JProgressBar(0, 100);
subProgressBar.setStringPainted(true);
subProgressBar.setString("Subtask Progress");
statusLabel = new JLabel("Initializing...", SwingConstants.CENTER);
timeLabel = new JLabel("Time elapsed: 0s", SwingConstants.CENTER);
JLabel logoLabel = new JLabel(LoadIcon.loadIcon("logo.png", 64));
JPanel progressPanel = new JPanel(new GridLayout(2, 1));
progressPanel.add(mainProgressBar);
progressPanel.add(subProgressBar);
loadingPanel.add(logoLabel, BorderLayout.NORTH);
loadingPanel.add(progressPanel, BorderLayout.CENTER);
loadingPanel.add(statusLabel, BorderLayout.SOUTH);
loadingPanel.add(timeLabel, BorderLayout.SOUTH);
loadingFrame.add(loadingPanel);
loadingFrame.setVisible(true);
}
/**
* 更新主任务进度
* @param completedTasks 已完成的主任务数量
*/
public void updateMainProgress(int completedTasks) {
this.completedTasks = completedTasks;
int progress = (int) ((completedTasks / (double) totalTasks) * 100);
mainProgressBar.setValue(progress);
statusLabel.setText("Main Progress: " + progress + "%");
updateTimeLabel();
}
/**
* 更新子任务进度
* @param subTaskName 子任务名称
* @param subTaskCompleted 已完成的子任务数量
* @param subTaskTotal 子任务总数
*/
public void updateSubProgress(String subTaskName, int subTaskCompleted, int subTaskTotal) {
subTasks.put(subTaskName, subTaskCompleted);
int progress = (int) ((subTaskCompleted / (double) subTaskTotal) * 100);
subProgressBar.setValue(progress);
subProgressBar.setString(subTaskName + ": " + progress + "%");
updateTimeLabel();
}
/**
* 更新总任务数
*/
public void setTotalTasks(int totalTasks) {
this.totalTasks = totalTasks;
}
/**
* 关闭加载窗口
*/
public void close() {
loadingFrame.dispose();
}
/**
* 更新时间标签
*/
private void updateTimeLabel() {
long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
timeLabel.setText("Time elapsed: " + elapsedTime + "s");
}
}

View File

@@ -0,0 +1,52 @@
package com.axis.innovators.box.plugins;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 自定义加载器
* @author tzdwindows 7
*/
public class BoxClassLoader extends URLClassLoader {
public BoxClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public BoxClassLoader(URL[] sources) {
super(sources);
}
public BoxClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes;
try {
classBytes = getClassBytes(name);
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] transformedClass = transformClass(name, null, classBytes);
return defineClass(name, transformedClass, 0, transformedClass.length);
}
public byte[] getClassBytes(String className) throws IOException, ClassNotFoundException {
String path = className.replace('.', '/') + ".class";
URL classUrl = getResource(path);
if (classUrl == null) {
throw new ClassNotFoundException("Class not found: " + className);
}
try (InputStream is = classUrl.openStream()) {
return is.readAllBytes();
}
}
public byte[] transformClass(String className, String transformedName, byte[] basicClass) {
return PluginLoader.transformClass(className, transformedName, basicClass);
}
}

View File

@@ -0,0 +1,16 @@
package com.axis.innovators.box.plugins;
/**
* 字节码操作接口
* @author tzdwindows 7
*/
public interface IClassTransformer {
/**
* 字节码操作
* @param name 类名
* @param transformedName 当前core插件名
* @param bytes 字节码
* @return 修改后的字节码
*/
byte[] transform(String name, String transformedName, byte[] bytes);
}

View File

@@ -0,0 +1,14 @@
package com.axis.innovators.box.plugins;
import java.util.Map;
/**
* Core插件接口
* @author tzdwindows 7
*/
public interface LoadingCorePlugin {
String[] getLibraryRequestClass();
String[] getASMTransformerClass();
String getSetupClass();
void injectData(Map<String, Object> data);
}

View File

@@ -11,18 +11,15 @@ import java.util.*;
import java.util.jar.*;
/**
* 加载插件的系统
* 插件加载器
* @author tzdwindows 7
*/
public class PluginLoader {
private static final Logger logger = LogManager.getLogger(PluginLoader.class);
public static final String PLUGIN_PATH = FolderCreator.getPluginFolder();
private static final List<PluginDescriptor> loadedPlugins = new ArrayList<>();
private static final List<IClassTransformer> transformers = new ArrayList<>();
/**
* 加载插件
* @throws IOException 插件加载失败
*/
public static void loadPlugins() throws IOException {
File pluginDir = new File(PLUGIN_PATH);
File[] jars = pluginDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".jar"));
@@ -30,10 +27,12 @@ public class PluginLoader {
if (jars == null) {
return;
}
Main.totalTasks = jars.length;
for (int i = 0; i < jars.length; i++) {
processJarFile(jars[i]);
Main.completedTasks = i;
Main.progressBarManager.updateSubProgress(
"Loading Plugin " + i,
i,
jars.length);
}
}
@@ -45,6 +44,45 @@ public class PluginLoader {
} else {
processWithAnnotations(jar, jarFile);
}
// Check for CorePlugin in MANIFEST.MF
Attributes attributes = jar.getManifest().getMainAttributes();
String corePluginClass = attributes.getValue("CorePlugin");
if (corePluginClass != null) {
processCorePlugin(jarFile, corePluginClass);
}
}
}
private static void processCorePlugin(File jarFile, String corePluginClass) {
try (URLClassLoader classLoader = new URLClassLoader(
new URL[]{jarFile.toURI().toURL()},
PluginLoader.class.getClassLoader())
) {
Class<?> coreClass = classLoader.loadClass(corePluginClass);
if (LoadingCorePlugin.class.isAssignableFrom(coreClass)) {
LoadingCorePlugin corePlugin = (LoadingCorePlugin) coreClass.getDeclaredConstructor().newInstance();
registerTransformers(corePlugin);
}
} catch (Exception e) {
logger.error("Failed to load core plugin: {}", corePluginClass, e);
}
}
private static void registerTransformers(LoadingCorePlugin corePlugin) {
String[] transformerClasses = corePlugin.getASMTransformerClass();
if (transformerClasses != null) {
for (String transformerClass : transformerClasses) {
try {
Class<?> transformerClazz = Class.forName(transformerClass);
if (IClassTransformer.class.isAssignableFrom(transformerClazz)) {
IClassTransformer transformer = (IClassTransformer) transformerClazz.getDeclaredConstructor().newInstance();
transformers.add(transformer);
}
} catch (Exception e) {
logger.error("Failed to register transformer: {}", transformerClass, e);
}
}
}
}
@@ -145,4 +183,11 @@ public class PluginLoader {
public static List<PluginDescriptor> getLoadedPlugins() {
return Collections.unmodifiableList(loadedPlugins);
}
public static byte[] transformClass(String name, String transformedName, byte[] basicClass) {
for (IClassTransformer transformer : transformers) {
basicClass = transformer.transform(name, transformedName, basicClass);
}
return basicClass;
}
}

View File

@@ -3,8 +3,8 @@
<Appenders>
<!-- 固定日志文件(按时间和大小滚动) -->
<RollingFile name="MainFileAppender"
fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}-%i.log">
fileName="logs/box.log"
filePattern="logs/box-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
</PatternLayout>
@@ -15,10 +15,10 @@
<DefaultRolloverStrategy max="7"/>
</RollingFile>
<!-- 每次运行独立的日志文件(带时间和PID -->
<!-- 每次运行独立的日志文件(带时间) -->
<RollingFile name="SessionFileAppender"
fileName="logs/app-${date:yyyy-MM-dd_HH-mm-ss}.log"
filePattern="logs/app-%d{yyyy-MM-dd}-%i.log"
fileName="logs/box-${date:yyyy-MM-dd_HH-mm-ss}.log"
filePattern="logs/box-%d{yyyy-MM-dd}-%i.log"
immediateFlush="true"
createOnDemand="false">
<PatternLayout>