feat(gui): 重构 FridaWindow 类并添加新功能
-重新设计了窗口布局和样式,增加了更多控制按钮 -集成了自动补全功能,支持 Frida 相关的关键字和 API - 优化了代码编辑器的配色方案,支持语法高亮 - 改进了日志输出区域的可读性- 移除了未使用的 LanguageManager 导入
This commit is contained in:
66
build.gradle
66
build.gradle
@@ -3,6 +3,12 @@ plugins {
|
||||
id 'application'
|
||||
id 'edu.sc.seis.launch4j' version '2.5.4'
|
||||
id 'org.openjfx.javafxplugin' version '0.1.0'
|
||||
id 'org.springframework.boot' version '3.2.0'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
}
|
||||
|
||||
configurations {
|
||||
all*.exclude group: 'org.openjfx', module: 'javafx'
|
||||
}
|
||||
|
||||
// JDK 版本检查
|
||||
@@ -16,17 +22,16 @@ group = 'com.axis.innovators.box'
|
||||
version = '0.0.1'
|
||||
|
||||
repositories {
|
||||
maven { setUrl("https://maven.aliyun.com/repository/central") }
|
||||
maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
|
||||
maven { setUrl("https://maven.aliyun.com/repository/google") }
|
||||
maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
|
||||
maven { setUrl("https://maven.aliyun.com/repository/public") }
|
||||
maven { setUrl("https://jitpack.io") }
|
||||
maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/") }
|
||||
maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/jcenter") }
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://maven.aliyun.com/repository/public'
|
||||
metadataSources {
|
||||
mavenPom()
|
||||
artifact()
|
||||
ignoreGradleMetadataRedirection()
|
||||
}
|
||||
}
|
||||
maven { url 'https://jitpack.io' }
|
||||
// mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -40,11 +45,11 @@ dependencies {
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.20.0'
|
||||
|
||||
implementation 'org.ow2.asm:asm:7.1'
|
||||
implementation 'org.ow2.asm:asm-commons:7.1'
|
||||
implementation 'org.ow2.asm:asm-analysis:7.1'
|
||||
implementation 'org.ow2.asm:asm-util:7.0'
|
||||
implementation 'org.ow2.asm:asm-tree:7.1'
|
||||
implementation 'org.ow2.asm:asm:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-commons:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-analysis:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-util:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-tree:9.7.1'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.17.2'
|
||||
|
||||
@@ -63,7 +68,13 @@ dependencies {
|
||||
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本
|
||||
implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.5.36'
|
||||
|
||||
implementation 'com.fifesoft:rsyntaxtextarea:3.3.0'
|
||||
// 必须的核心依赖
|
||||
implementation 'com.fifesoft:rsyntaxtextarea:3.3.1'
|
||||
implementation 'com.fifesoft:autocomplete:3.3.1'
|
||||
|
||||
// 可选UI增强
|
||||
implementation 'com.fifesoft:rstaui:3.3.1'
|
||||
|
||||
implementation 'org.apache.commons:commons-compress:1.23.0'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
|
||||
implementation 'org.controlsfx:controlsfx:11.1.2' // 现代化UI组件
|
||||
@@ -76,6 +87,27 @@ dependencies {
|
||||
implementation 'org.benf:cfr:0.152'
|
||||
|
||||
implementation 'com.github.javaparser:javaparser-core:3.25.1'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web' // Web支持
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JPA数据库支持
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation' // 参数校验
|
||||
|
||||
// 安全相关依赖
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security' // Spring Security
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.12.3' // JWT API
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3', // JWT实现
|
||||
'io.jsonwebtoken:jjwt-jackson:0.12.3' // JWT序列化
|
||||
|
||||
|
||||
runtimeOnly 'com.mysql:mysql-connector-j'
|
||||
|
||||
// 开发工具
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
|
||||
// 测试依赖
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.security:spring-security-test'
|
||||
|
||||
}
|
||||
|
||||
// 分离依赖项到 libs 目录
|
||||
|
||||
Binary file not shown.
@@ -1,90 +0,0 @@
|
||||
"""
|
||||
工具模块初始化脚本
|
||||
功能:向Axis Innovators Box注册自定义工具类别和工具项
|
||||
作者:tzdwindows 7
|
||||
版本:1.1
|
||||
"""
|
||||
from com.axis.innovators.box.python import PyLocalSide
|
||||
|
||||
from javax.swing import AbstractAction, JFrame,JDialog, JLabel, JButton, JPanel, JOptionPane
|
||||
from java.awt import BorderLayout, Dimension
|
||||
from java.awt.event import ActionListener
|
||||
|
||||
class MyAction(AbstractAction):
|
||||
def actionPerformed(self, event):
|
||||
"""工具项点击事件处理"""
|
||||
print("[DEBUG] Tool item clicked! Event source:", event.getSource())
|
||||
|
||||
# 创建 JDialog 作为子窗口
|
||||
parent_frame = event.getSource().getTopLevelAncestor() # 获取父窗口
|
||||
dialog = JDialog(parent_frame, u"工具面板", True) # True 表示模态对话框
|
||||
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE) # 关闭时释放资源
|
||||
dialog.setSize(300, 200)
|
||||
dialog.setLocationRelativeTo(parent_frame) # 居中于父窗口
|
||||
|
||||
# 创建面板并设置布局
|
||||
panel = JPanel()
|
||||
panel.layout = BorderLayout() # 使用边界布局
|
||||
|
||||
# 添加标签
|
||||
label = JLabel(u"这是工具项的子面板")
|
||||
panel.add(label, BorderLayout.CENTER)
|
||||
|
||||
# 添加关闭按钮
|
||||
close_btn = JButton(u"关闭")
|
||||
close_btn.addActionListener(lambda e: dialog.dispose()) # 点击关闭窗口
|
||||
panel.add(close_btn, BorderLayout.SOUTH)
|
||||
|
||||
# 将面板添加到窗口
|
||||
dialog.add(panel)
|
||||
dialog.visible = True # 显示窗口
|
||||
|
||||
|
||||
def onStartup():
|
||||
"""
|
||||
系统启动时自动执行的初始化逻辑
|
||||
功能:
|
||||
1. 创建工具类别
|
||||
2. 创建工具项并绑定动作
|
||||
3. 注册到系统全局工具集
|
||||
"""
|
||||
print('[INFO] 正在初始化自定义工具...')
|
||||
|
||||
# --------------------------
|
||||
# 创建工具类别(参数顺序:显示名称,图标资源名,描述)
|
||||
# --------------------------
|
||||
tool_category = PyLocalSide.getToolCategory(
|
||||
u"数据分析工具", # 显示名称(GUI可见)
|
||||
u"analytics_icon.png", # 图标文件名(需存在于资源目录)
|
||||
u"高级数据分析功能集合" # 悬停提示描述
|
||||
)
|
||||
|
||||
# --------------------------
|
||||
# 创建工具项(参数顺序:显示名称,图标,描述,ID,动作对象)
|
||||
# --------------------------
|
||||
tool_action = MyAction()
|
||||
tool_item = PyLocalSide.getToolItem(
|
||||
u"数据可视化", # 工具项显示名称
|
||||
u"chart_icon.png", # 工具项图标
|
||||
u"生成交互式数据图表", # 工具项描述
|
||||
1001, # 工具项唯一ID(需在配置中统一管理)
|
||||
tool_action # 点击触发的动作
|
||||
)
|
||||
tool_category.addTool(tool_item)
|
||||
|
||||
# --------------------------
|
||||
# 注册工具类别到系统(参数:类别对象,全局唯一注册名称)
|
||||
# --------------------------
|
||||
PyLocalSide.addToolCategory(
|
||||
tool_category,
|
||||
u"custom_module::data_analysis_tools" # 推荐命名规则:模块名::功能名
|
||||
)
|
||||
print('[SUCCESS] 工具类别注册成功')
|
||||
|
||||
if __name__ == '__main__':
|
||||
result = 0
|
||||
errorResult = ""
|
||||
|
||||
# 确保Jython运行时可以访问onStartup函数
|
||||
# 原理:将函数显式绑定到全局字典
|
||||
globals()['onStartup'] = onStartup
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"id": "testing",
|
||||
"name": "测试",
|
||||
"version": "0.0.1",
|
||||
"description": "测试插件",
|
||||
"author": "tzdwindows 7",
|
||||
"dependencies": [],
|
||||
|
||||
"_comment": {
|
||||
"warning": "本文件为插件元数据配置,修改后需重启应用生效",
|
||||
"path": "插件资源应放置在plugins/{id}/目录下"
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,6 @@ public class AxisInnovatorsBox {
|
||||
try {
|
||||
LibraryLoad.loadLibrary("FridaNative");
|
||||
LibraryLoad.loadLibrary("ThrowSafely");
|
||||
LM.loadLibrary(LM.CUDA);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to load the 'FridaNative' library", e);
|
||||
throw new RuntimeException(e);
|
||||
@@ -231,7 +230,6 @@ public class AxisInnovatorsBox {
|
||||
* 初始化Log4j2
|
||||
*/
|
||||
public void initLog4j2() {
|
||||
Log4j2OutputStream.redirectSystemStreams();
|
||||
|
||||
logger.info("Application Version: {}", VERSIONS);
|
||||
logger.info("Authors: {}", String.join(", ", AUTHOR));
|
||||
@@ -245,6 +243,8 @@ public class AxisInnovatorsBox {
|
||||
logger.info("Java Home: {}", System.getProperty("java.home"));
|
||||
logger.info("Java Class Path: {}", System.getProperty("java.class.path"));
|
||||
logger.info("ClassLoader.getSystemClassLoader(): {}", ClassLoader.getSystemClassLoader());
|
||||
|
||||
Log4j2OutputStream.redirectSystemStreams();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,6 +277,12 @@ public class AxisInnovatorsBox {
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatLightLaf_theme");
|
||||
|
||||
main.registrationTopic.addTopic(new com.formdev.flatlaf.FlatDarculaLaf(),
|
||||
"Darcula主题",
|
||||
"Darcula主题",
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:darcula_theme");
|
||||
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to load the system facade class", e);
|
||||
@@ -334,7 +340,7 @@ public class AxisInnovatorsBox {
|
||||
public static void run(String[] args) {
|
||||
main = new AxisInnovatorsBox(args);
|
||||
try {
|
||||
main.initLog4j2();
|
||||
//main.initLog4j2();
|
||||
main.setTopic();
|
||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||
for (Map<String, String> fileInfo : validFiles) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
@@ -26,6 +27,7 @@ public class Log4j2OutputStream extends OutputStream {
|
||||
systemOutContent.write(b, off, len);
|
||||
String message = new String(b, off, len).trim();
|
||||
logger.info(message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.axis.innovators.box.decompilation.gui;
|
||||
|
||||
import com.axis.innovators.box.gui.LoadIcon;
|
||||
import com.axis.innovators.box.gui.WindowsJDialog;
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
import com.github.javaparser.JavaParser;
|
||||
import com.github.javaparser.ParseResult;
|
||||
import com.github.javaparser.Position;
|
||||
@@ -19,7 +17,6 @@ import java.awt.event.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
@@ -283,7 +280,7 @@ public class ModernJarViewer extends JFrame {
|
||||
int offsetX = -5;
|
||||
int offsetY = 0;
|
||||
|
||||
currentSearchDialog.setLocation(editorLocation.x + editorWidth - dialogWidth - offsetX, editorLocation.y + offsetY);
|
||||
//currentSearchDialog.setLocation(editorLocation.x + editorWidth - dialogWidth - offsetX, editorLocation.y + offsetY);
|
||||
currentSearchDialog.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
package com.axis.innovators.box.decompilation.util;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
import org.objectweb.asm.commons.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.jar.*;
|
||||
|
||||
/**
|
||||
* 混淆工具(未完工)
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class JarObfuscator {
|
||||
private final File jarFile;
|
||||
private final Map<String, byte[]> jarEntries = new LinkedHashMap<>();
|
||||
private final List<String> whitelistList = new ArrayList<>();
|
||||
private final Map<String, String> classMapping = new HashMap<>();
|
||||
private final Map<String, String> classNameMapping = new HashMap<>();
|
||||
private final Map<String, String> fieldMapping = new HashMap<>();
|
||||
private final Map<String, String> methodMapping = new HashMap<>();
|
||||
private final ClassNameGenerator classNameGenerator = new ClassNameGenerator();
|
||||
private final MemberNameGenerator fieldNameGenerator = new MemberNameGenerator();
|
||||
private final MemberNameGenerator methodNameGenerator = new MemberNameGenerator();
|
||||
|
||||
|
||||
public JarObfuscator(File file) {
|
||||
this.jarFile = file;
|
||||
}
|
||||
|
||||
public void obfuscate() throws IOException {
|
||||
collectObfuscationNames();
|
||||
applyObfuscation();
|
||||
}
|
||||
|
||||
public void addWhitelist(String classPath) {
|
||||
whitelistList.add(classPath.replace(".", "/"));
|
||||
}
|
||||
|
||||
private void collectObfuscationNames() throws IOException {
|
||||
try (JarFile jar = new JarFile(jarFile)) {
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String entryName = entry.getName();
|
||||
|
||||
if (entry.isDirectory() || !entryName.endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
byte[] bytes = readAllBytes(is);
|
||||
ClassReader cr = new ClassReader(bytes);
|
||||
cr.accept(new MappingCollector(), ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MappingCollector extends ClassVisitor {
|
||||
private String originalClassName;
|
||||
|
||||
public MappingCollector() {
|
||||
super(Opcodes.ASM9);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
this.originalClassName = name;
|
||||
if (whitelistList.contains(originalClassName)){
|
||||
classMapping.put(originalClassName, originalClassName);
|
||||
return;
|
||||
}
|
||||
|
||||
String originalPackage = name.contains("/") ?
|
||||
name.substring(0, name.lastIndexOf('/')) : "";
|
||||
String className = name.contains("/") ?
|
||||
name.substring(name.lastIndexOf('/') + 1) : name;
|
||||
|
||||
String obfuscatedPackage = classNameGenerator.generatePackageName(originalPackage);
|
||||
String obfuscatedClassName = classNameGenerator.generateClassName(className);
|
||||
String newInternalName = obfuscatedPackage.isEmpty() ?
|
||||
obfuscatedClassName : obfuscatedPackage + "/" + obfuscatedClassName;
|
||||
|
||||
classMapping.put(originalClassName, newInternalName);
|
||||
classNameMapping.put(originalClassName, obfuscatedClassName);
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor,
|
||||
String signature, Object value) {
|
||||
if (whitelistList.contains(originalClassName)) {
|
||||
fieldMapping.put(originalClassName + "#" + name + "#" + descriptor, name);
|
||||
return super.visitField(access, name, descriptor, signature, value);
|
||||
}
|
||||
String newName = fieldNameGenerator.generate(name);
|
||||
fieldMapping.put(originalClassName + "#" + name + "#" + descriptor, newName);
|
||||
return super.visitField(access, name, descriptor, signature, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
||||
String signature, String[] exceptions) {
|
||||
if (!"<init>".equals(name) && !"<clinit>".equals(name)
|
||||
|| whitelistList.contains(originalClassName)) {
|
||||
String newName = methodNameGenerator.generate(name);
|
||||
methodMapping.put(originalClassName + "#" + name + "#" + descriptor, newName);
|
||||
} else {
|
||||
methodMapping.put(originalClassName + "#" + name + "#" + descriptor, name);
|
||||
}
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyObfuscation() throws IOException {
|
||||
try (JarFile jar = new JarFile(jarFile)) {
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String entryName = entry.getName();
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
byte[] bytes = readAllBytes(is);
|
||||
if (entryName.endsWith(".class")) {
|
||||
bytes = processClass(bytes);
|
||||
String newName = classMapping.get(entryName.replace(".class", "")) + ".class";
|
||||
jarEntries.put(newName, bytes);
|
||||
} else {
|
||||
jarEntries.put(entryName, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClassNameGenerator {
|
||||
private final Map<String, String> packageMappings = new HashMap<>();
|
||||
private final Map<String, String> classMappings = new HashMap<>();
|
||||
private final Map<String, String> pathMappings = new HashMap<>();
|
||||
private int packageCounter = 0;
|
||||
private int classCounter = 0;
|
||||
|
||||
public String generatePackageName(String originalPath) {
|
||||
return pathMappings.computeIfAbsent(originalPath, k -> {
|
||||
String[] parts = originalPath.split("/");
|
||||
StringBuilder newPath = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
String pkg = packageMappings.computeIfAbsent(part, p -> generateShortName(packageCounter++, 'a'));
|
||||
newPath.append(pkg).append('/');
|
||||
}
|
||||
return newPath.substring(0, newPath.length() - 1);
|
||||
});
|
||||
}
|
||||
|
||||
public String generateClassName(String originalName) {
|
||||
return classMappings.computeIfAbsent(originalName, k -> generateShortName(classCounter++, 'A'));
|
||||
}
|
||||
|
||||
private String generateShortName(int index, char base) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
do {
|
||||
sb.append((char) (base + index % 26));
|
||||
index = index / 26 - 1;
|
||||
} while (index >= 0);
|
||||
return sb.reverse().toString();
|
||||
}
|
||||
}
|
||||
|
||||
// 专用成员名生成器(处理字段和方法名)
|
||||
private static class MemberNameGenerator {
|
||||
private final Map<String, String> cache = new HashMap<>();
|
||||
private int counter = 0;
|
||||
|
||||
public String generate(String original) {
|
||||
return cache.computeIfAbsent(original, k -> generateShortName(counter++));
|
||||
}
|
||||
|
||||
private String generateShortName(int index) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
do {
|
||||
sb.append((char) ('a' + index % 26));
|
||||
index = index / 26 - 1;
|
||||
} while (index >= 0);
|
||||
return sb.reverse().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] processClass(byte[] original) {
|
||||
ClassReader cr = new ClassReader(original);
|
||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
Remapper remapper = new CustomRemapper();
|
||||
ClassVisitor cv = new ClassRemapper(cw, remapper);
|
||||
|
||||
ClassVisitor chain = new ClassVisitor(Opcodes.ASM9, cv) {
|
||||
private String originalClassName;
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
this.originalClassName = name;
|
||||
String newInternalName = classMapping.get(name);
|
||||
|
||||
super.visit(version, access, newInternalName,
|
||||
remapper.mapSignature(signature, false),
|
||||
remapper.mapType(superName),
|
||||
interfaces != null ? Arrays.stream(interfaces)
|
||||
.map(remapper::mapType)
|
||||
.toArray(String[]::new) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor,
|
||||
String signature, Object value) {
|
||||
String newName = fieldMapping.get(originalClassName + "#" + name + "#" + descriptor);
|
||||
return super.visitField(access, newName,
|
||||
remapper.mapDesc(descriptor),
|
||||
remapper.mapSignature(signature, true),
|
||||
value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
||||
String signature, String[] exceptions) {
|
||||
|
||||
if ("<init>".equals(name) || "<clinit>".equals(name)) {
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
|
||||
String newName = methodMapping.get(originalClassName + "#" + name + "#" + descriptor);
|
||||
|
||||
MethodVisitor mv = super.visitMethod(access, newName,
|
||||
remapper.mapMethodDesc(descriptor),
|
||||
remapper.mapSignature(signature, false),
|
||||
exceptions != null ? Arrays.stream(exceptions)
|
||||
.map(remapper::mapType)
|
||||
.toArray(String[]::new) : null);
|
||||
|
||||
return new MethodVisitor(Opcodes.ASM9, mv) {
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name,
|
||||
String descriptor, boolean isInterface) {
|
||||
super.visitMethodInsn(opcode,
|
||||
remapper.mapType(owner),
|
||||
remapper.mapMethodName(owner, name, descriptor),
|
||||
remapper.mapMethodDesc(descriptor),
|
||||
isInterface);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name,
|
||||
String descriptor) {
|
||||
super.visitFieldInsn(opcode,
|
||||
remapper.mapType(owner),
|
||||
remapper.mapFieldName(owner, name, descriptor),
|
||||
remapper.mapDesc(descriptor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
String mappedType = remapper.mapType(type);
|
||||
super.visitTypeInsn(opcode, mappedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String descriptor,
|
||||
String signature, Label start, Label end, int index) {
|
||||
super.visitLocalVariable(name,
|
||||
remapper.mapDesc(descriptor),
|
||||
remapper.mapSignature(signature, true),
|
||||
start, end, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (value instanceof String) {
|
||||
String encrypted = xorEncrypt((String) value);
|
||||
super.visitLdcInsn(encrypted);
|
||||
super.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"StringDecoder",
|
||||
"decode",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;",
|
||||
false
|
||||
);
|
||||
} else {
|
||||
super.visitLdcInsn(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
cr.accept(chain, ClassReader.EXPAND_FRAMES);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private class CustomRemapper extends Remapper {
|
||||
@Override
|
||||
public String mapType(String internalName) {
|
||||
return classMapping.getOrDefault(internalName, internalName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapMethodName(String owner, String name, String descriptor) {
|
||||
return methodMapping.getOrDefault(owner + "#" + name + "#" + descriptor, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapFieldName(String owner, String name, String descriptor) {
|
||||
return fieldMapping.getOrDefault(owner + "#" + name + "#" + descriptor, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapSignature(String signature, boolean typeSignature) {
|
||||
return super.mapSignature(signature, typeSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String map(String internalName) {
|
||||
return classMapping.getOrDefault(internalName, internalName);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(String path) throws IOException {
|
||||
injectStringDecoder();
|
||||
|
||||
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(path))) {
|
||||
for (Map.Entry<String, byte[]> entry : jarEntries.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
jos.putNextEntry(new JarEntry(name));
|
||||
jos.write(entry.getValue());
|
||||
jos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void injectStringDecoder() {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
|
||||
"StringDecoder", null, "java/lang/Object", null);
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
|
||||
"decode", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C", false);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 1);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitInsn(Opcodes.ARRAYLENGTH);
|
||||
mv.visitVarInsn(Opcodes.ISTORE, 2);
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitVarInsn(Opcodes.ISTORE, 3);
|
||||
|
||||
Label loop = new Label();
|
||||
mv.visitLabel(loop);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 3);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 2);
|
||||
Label end = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IF_ICMPGE, end);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 3);
|
||||
mv.visitInsn(Opcodes.DUP2);
|
||||
mv.visitInsn(Opcodes.CALOAD);
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, 0x55);
|
||||
mv.visitInsn(Opcodes.IXOR);
|
||||
mv.visitInsn(Opcodes.I2C);
|
||||
mv.visitInsn(Opcodes.CASTORE);
|
||||
mv.visitIincInsn(3, 1);
|
||||
mv.visitJumpInsn(Opcodes.GOTO, loop);
|
||||
mv.visitLabel(end);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/String", "valueOf", "([C)Ljava/lang/String;", false);
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(4, 4);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
jarEntries.put("StringDecoder.class", cw.toByteArray());
|
||||
}
|
||||
|
||||
public Map<String, String> getComparisonTable() {
|
||||
return Collections.unmodifiableMap(classMapping);
|
||||
}
|
||||
|
||||
public void saveMappingTable(String path) throws IOException {
|
||||
Properties prop = new Properties();
|
||||
classMapping.forEach((k, v) -> {
|
||||
//System.out.println("[Class Mapping] " + k + " → " + v);
|
||||
prop.setProperty("class:" + k, v);
|
||||
});
|
||||
fieldMapping.forEach((k, v) -> {
|
||||
//System.out.println("[Field Mapping] " + k + " → " + v);
|
||||
prop.setProperty("field:" + k, v);
|
||||
});
|
||||
methodMapping.forEach((k, v) -> {
|
||||
//System.out.println("[Method Mapping] " + k + " → " + v);
|
||||
prop.setProperty("method:" + k, v);
|
||||
});
|
||||
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8)) {
|
||||
prop.store(writer, "Obfuscation Mapping Table");
|
||||
}
|
||||
}
|
||||
|
||||
private static String xorEncrypt(String input) {
|
||||
char[] chars = input.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
chars[i] ^= 0x55;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
private static byte[] readAllBytes(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
byte[] data = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, bytesRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
File inputJar = new File("C:\\Users\\Administrator\\MCreatorWorkspaces\\AxisInnovatorsBox\\build\\libs\\AxisInnovatorsBox-0.0.1.jar");
|
||||
JarObfuscator obfuscator = new JarObfuscator(inputJar);
|
||||
//obfuscator.addWhitelist("com/axis/innovators/box/Main");
|
||||
//obfuscator.addWhitelist("com/axis/innovators/box/plugins/BoxClassLoader");
|
||||
//obfuscator.addWhitelist("com/axis/innovators/box/plugins/IClassTransformer");
|
||||
//obfuscator.addWhitelist("com/axis/innovators/box/plugins/LoadingCorePlugin");
|
||||
//obfuscator.addWhitelist("com/axis/innovators/box/plugins/PluginDescriptor");
|
||||
//obfuscator.addWhitelist("com/axis/innovators/box/plugins/PluginDescriptor");
|
||||
obfuscator.obfuscate();
|
||||
obfuscator.saveMappingTable("C:\\Users\\Administrator\\Desktop\\mapping.properties");
|
||||
obfuscator.save("C:\\Users\\Administrator\\Desktop\\obfuscated.jar");
|
||||
}
|
||||
}
|
||||
306
src/main/java/com/axis/innovators/box/gui/FridaSyntax.java
Normal file
306
src/main/java/com/axis/innovators/box/gui/FridaSyntax.java
Normal file
@@ -0,0 +1,306 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import org.fife.ui.autocomplete.BasicCompletion;
|
||||
import org.fife.ui.autocomplete.DefaultCompletionProvider;
|
||||
|
||||
public class FridaSyntax {
|
||||
public static void addFridaCompletions(DefaultCompletionProvider provider) {
|
||||
// Java 相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.perform(function() {\n // Your code here\n});",
|
||||
"Java.perform",
|
||||
"在 Java 上下文中执行代码"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.choose('$className$', {\n" +
|
||||
" onMatch: function(instance) {\n // Handle instance\n },\n" +
|
||||
" onComplete: function() {\n // Complete handler\n }\n})",
|
||||
"Java.choose",
|
||||
"枚举 Java 对象实例"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.use('$className$')",
|
||||
"Java.use",
|
||||
"获取 Java 类的引用"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.enumerateLoadedClasses({\n" +
|
||||
" onMatch: function(className) {\n // Handle class name\n },\n" +
|
||||
" onComplete: function() {\n // Complete handler\n }\n})",
|
||||
"Java.enumerateLoadedClasses",
|
||||
"枚举已加载的 Java 类"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.enumerateMethods('$className$')",
|
||||
"Java.enumerateMethods",
|
||||
"枚举指定 Java 类的方法"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.scheduleOnMainThread(function() {\n // Your code here\n});",
|
||||
"Java.scheduleOnMainThread",
|
||||
"在主线程中执行代码"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Java.deoptimizeEverything()",
|
||||
"Java.deoptimizeEverything",
|
||||
"禁用 JIT 优化"));
|
||||
|
||||
// 模块和符号相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Module.findExportByName('$module$', '$name$')",
|
||||
"Module.findExportByName",
|
||||
"查找模块导出函数"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Module.findBaseAddress('$module$')",
|
||||
"Module.findBaseAddress",
|
||||
"查找模块的基地址"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Module.enumerateImports('$module$')",
|
||||
"Module.enumerateImports",
|
||||
"枚举模块的导入函数"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Module.enumerateExports('$module$')",
|
||||
"Module.enumerateExports",
|
||||
"枚举模块的导出函数"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Module.enumerateSymbols('$module$')",
|
||||
"Module.enumerateSymbols",
|
||||
"枚举模块的符号"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Module.load('$path$')",
|
||||
"Module.load",
|
||||
"加载指定路径的模块"));
|
||||
|
||||
// 内存操作相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Memory.scan('$address$', $size$, '$pattern$', {\n" +
|
||||
" onMatch: function(address, size) {\n // Handle match\n },\n" +
|
||||
" onComplete: function() {\n // Complete handler\n }\n})",
|
||||
"Memory.scan",
|
||||
"扫描内存中的指定模式"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Memory.alloc($size$)",
|
||||
"Memory.alloc",
|
||||
"分配指定大小的内存"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Memory.copy('$destination$', '$source$', $size$)",
|
||||
"Memory.copy",
|
||||
"复制内存数据"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Memory.protect('$address$', $size$, '$protection$')",
|
||||
"Memory.protect",
|
||||
"修改内存保护属性"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Memory.readByteArray('$address$', $size$)",
|
||||
"Memory.readByteArray",
|
||||
"读取内存中的字节数组"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Memory.writeByteArray('$address$', [$bytes$])",
|
||||
"Memory.writeByteArray",
|
||||
"写入字节数组到内存"));
|
||||
|
||||
// 拦截器相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Interceptor.attach('$address$', {\n" +
|
||||
" onEnter: function(args) {\n // Handle enter\n },\n" +
|
||||
" onLeave: function(retval) {\n // Handle leave\n }\n})",
|
||||
"Interceptor.attach",
|
||||
"附加拦截器到指定地址"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Interceptor.replace('$address$', new NativeCallback(function(args) {\n // Your code here\n}, '$returnType$', ['$argTypes$']))",
|
||||
"Interceptor.replace",
|
||||
"替换函数实现"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Interceptor.detach('$address$')",
|
||||
"Interceptor.detach",
|
||||
"分离拦截器"));
|
||||
|
||||
// 进程和线程相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Process.enumerateModules()",
|
||||
"Process.enumerateModules",
|
||||
"枚举已加载的模块"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Process.enumerateThreads()",
|
||||
"Process.enumerateThreads",
|
||||
"枚举进程中的线程"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Process.getCurrentThreadId()",
|
||||
"Process.getCurrentThreadId",
|
||||
"获取当前线程 ID"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Process.setExceptionHandler(function(exception) {\n // Handle exception\n})",
|
||||
"Process.setExceptionHandler",
|
||||
"设置异常处理函数"));
|
||||
|
||||
// 文件系统相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"File.open('$path$', '$mode$')",
|
||||
"File.open",
|
||||
"打开文件"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"File.read('$file$', $size$)",
|
||||
"File.read",
|
||||
"读取文件内容"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"File.write('$file$', $data$)",
|
||||
"File.write",
|
||||
"写入数据到文件"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"File.remove('$path$')",
|
||||
"File.remove",
|
||||
"删除文件"));
|
||||
|
||||
// 网络相关
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Socket.listen('$address$', $port$)",
|
||||
"Socket.listen",
|
||||
"监听指定地址和端口"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Socket.connect('$address$', $port$)",
|
||||
"Socket.connect",
|
||||
"连接到指定地址和端口"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Socket.read('$socket$', $size$)",
|
||||
"Socket.read",
|
||||
"从套接字读取数据"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"Socket.write('$socket$', $data$)",
|
||||
"Socket.write",
|
||||
"写入数据到套接字"));
|
||||
|
||||
// 其他实用工具
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"send($message$)",
|
||||
"send",
|
||||
"发送消息到 Frida 客户端"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"recv(function(message) {\n // Handle message\n})",
|
||||
"recv",
|
||||
"接收来自 Frida 客户端的消息"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"hexdump('$address$', { offset: $offset$, length: $length$ })",
|
||||
"hexdump",
|
||||
"以十六进制格式打印内存内容"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"console.log('$message$')",
|
||||
"console.log",
|
||||
"打印日志信息"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"console.error('$message$')",
|
||||
"console.error",
|
||||
"打印错误信息"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"setTimeout(function() {\n // Your code here\n}, $delay$)",
|
||||
"setTimeout",
|
||||
"设置延时执行"));
|
||||
|
||||
provider.addCompletion(new BasicCompletion(provider,
|
||||
"setInterval(function() {\n // Your code here\n}, $interval$)",
|
||||
"setInterval",
|
||||
"设置定时执行"));
|
||||
}
|
||||
|
||||
public static void addBasicCompletions(DefaultCompletionProvider provider) {
|
||||
String[] jsKeywords = {"function", "var", "let", "const", "if", "else",
|
||||
"for", "while(/* logic */)", "try", "catch", "true", "false","()", "for (int i = 0; i < ; i++) {\n" +
|
||||
" \n" +
|
||||
"}"};
|
||||
|
||||
String[] fridaTypes = {
|
||||
"Frida", "Process", "Module", "ModuleMap", "Memory", "Thread", "ThreadBacktrace",
|
||||
"ThreadDetails", "Instruction", "Interceptor", "NativePointer", "NativeFunction",
|
||||
"NativeCallback", "SystemFunction", "SystemFunctionOptions", "SocketListener",
|
||||
"SocketConnection", "File", "InputStream", "OutputStream", "Database", "Statement",
|
||||
"SqliteError", "ObjC", "ObjC.Object", "ObjC.Class", "ObjC.Protocol", "ObjC.Block",
|
||||
"ObjC.Method", "ObjC.Ivar", "ObjC.Property", "ObjC.Selector", "Java", "Java.Class",
|
||||
"Java.Method", "Java.Field", "Java.Scheduler", "Java.Cast", "Java.Perform", "Java.use",
|
||||
"Java.available", "Java.scheduleOnMainThread", "Java.deoptimizeEverything",
|
||||
"Java.enumerateLoadedClasses", "Java.enumerateMethods", "Java.vm", "Java.vm.tryGetEnv",
|
||||
"Java.vm.perform", "Java.vm.getEnv", "Java.vm.tryGetEnv", "Java.vm.tryGetEnv","Script",
|
||||
"ScriptRuntime", "ScriptSource", "ScriptOptions", "CompiledScript",
|
||||
"ScriptExports", "ScriptMessage", "ScriptMessageHandler", "ScriptPostMessageHandler",
|
||||
"ScriptUnloadHandler", "ScriptDestroyHandler", "ScriptLogHandler", "ScriptErrorHandler",
|
||||
"ScriptDebugHandler", "ScriptStdinHandler", "ScriptStdoutHandler", "ScriptStderrHandler",
|
||||
"Memory.scan", "Memory.scanSync", "Memory.alloc", "Memory.allocUtf8String",
|
||||
"Memory.allocAnsiString", "Memory.copy", "Memory.protect", "Memory.readByteArray",
|
||||
"Memory.readCString", "Memory.readUtf8String", "Memory.readAnsiString",
|
||||
"Memory.writeByteArray", "Memory.writeUtf8String", "Memory.writeAnsiString",
|
||||
"Memory.writePointer", "Memory.writeS8", "Memory.writeS16", "Memory.writeS32",
|
||||
"Memory.writeS64", "Memory.writeU8", "Memory.writeU16", "Memory.writeU32",
|
||||
"Memory.writeU64", "Memory.writeFloat", "Memory.writeDouble",
|
||||
"Process.id", "Process.arch", "Process.platform", "Process.pageSize", "Process.pointerSize",
|
||||
"Process.codeSigningPolicy", "Process.enumerateThreads", "Process.enumerateModules",
|
||||
"Process.enumerateRanges", "Process.enumerateMallocRanges", "Process.enumerateModuleRanges",
|
||||
"Process.findModuleByName", "Process.findModuleByAddress", "Process.getModuleByName",
|
||||
"Process.getModuleByAddress", "Process.getCurrentThreadId", "Process.getThreadById",
|
||||
"Process.setExceptionHandler", "Module.load", "Module.ensureInitialized",
|
||||
"Module.findBaseAddress", "Module.findExportByName", "Module.enumerateImports",
|
||||
"Module.enumerateExports", "Module.enumerateSymbols", "Module.enumerateSections",
|
||||
"Module.enumerateDependencies", "Module.findExportByAddress", "Module.getExportByName",
|
||||
"Module.getExportByAddress", "Interceptor.attach", "Interceptor.detach",
|
||||
"Interceptor.replace", "Interceptor.revert", "Interceptor.flush", "NativePointer.isNull",
|
||||
"NativePointer.add", "NativePointer.sub", "NativePointer.and", "NativePointer.or",
|
||||
"NativePointer.xor", "NativePointer.shr", "NativePointer.shl", "NativePointer.compare",
|
||||
"NativePointer.toInt32", "NativePointer.toUInt32", "NativePointer.toString",
|
||||
"NativePointer.readPointer", "NativePointer.writePointer", "NativePointer.readS8",
|
||||
"NativePointer.readS16", "NativePointer.readS32", "NativePointer.readS64",
|
||||
"NativePointer.readU8", "NativePointer.readU16", "NativePointer.readU32",
|
||||
"NativePointer.readU64", "NativePointer.readFloat", "NativePointer.readDouble",
|
||||
"NativePointer.writeS8", "NativePointer.writeS16", "NativePointer.writeS32",
|
||||
"NativePointer.writeS64", "NativePointer.writeU8", "NativePointer.writeU16",
|
||||
"NativePointer.writeU32", "NativePointer.writeU64", "NativePointer.writeFloat",
|
||||
"NativePointer.writeDouble", "File.open", "File.close", "File.read", "File.write",
|
||||
"File.flush", "File.seek", "File.tell", "File.size", "File.copy", "File.move",
|
||||
"File.remove", "File.exists", "File.isDirectory", "File.list", "Database.open",
|
||||
"Database.close", "Database.execute", "Database.prepare", "Database.step",
|
||||
"Database.reset", "Database.finalize", "Database.lastInsertRowId", "Database.changes",
|
||||
"Database.backup", "Socket.listen", "Socket.connect", "Socket.bind", "Socket.accept",
|
||||
"Socket.read", "Socket.write", "Socket.close", "Socket.shutdown", "Socket.getPeerName",
|
||||
"Socket.getSockName", "Socket.setTimeout", "Socket.setNoDelay", "Socket.setKeepAlive",
|
||||
"ObjC.available", "ObjC.classes", "ObjC.protocols", "ObjC.registerClass", "ObjC.bind",
|
||||
"ObjC.unbind", "ObjC.schedule", "ObjC.perform", "ObjC.choose", "ObjC.enumerateLoadedClasses",
|
||||
"ObjC.enumerateMethods", "ObjC.enumerateProperties", "ObjC.enumerateIvars",
|
||||
"ObjC.enumerateProtocols", "Java.enumerateLoadedClasses", "Java.enumerateMethods",
|
||||
"Java.enumerateFields", "Java.enumerateClassLoaders", "Java.choose",
|
||||
"Java.scheduleOnMainThread", "Java.perform", "Java.performNow", "Java.performSync",
|
||||
"Java.deoptimizeEverything", "Java.vm.getEnv", "Java.vm.tryGetEnv", "Java.vm.perform",
|
||||
"Java.vm.tryGetEnv", "send", "recv", "rpc", "WeakRef", "WeakCallback", "ptr", "NULL",
|
||||
"hexdump", "console.log", "console.warn", "console.error", "console.dir", "console.trace",
|
||||
"setTimeout", "clearTimeout", "setInterval", "clearInterval", "setImmediate", "clearImmediate"};
|
||||
|
||||
for (String type : fridaTypes) {
|
||||
provider.addCompletion(new BasicCompletion(provider, type));
|
||||
}
|
||||
|
||||
for (String kw : jsKeywords) {
|
||||
provider.addCompletion(new BasicCompletion(provider, kw));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,27 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
import com.axis.innovators.box.gui.renderer.DarculaCompletionCellRenderer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.fife.ui.autocomplete.*;
|
||||
import org.tzd.frida.windows.Frida;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -24,11 +30,16 @@ import java.util.List;
|
||||
*/
|
||||
public class FridaWindow extends WindowsJDialog {
|
||||
private static final Logger logger = LogManager.getLogger(FridaWindow.class);
|
||||
private JTextArea scriptArea;
|
||||
private RSyntaxTextArea scriptArea;
|
||||
private JTextArea logArea;
|
||||
private JTextField pidField;
|
||||
private boolean isRepetition = false;
|
||||
|
||||
// IDEA风格配色
|
||||
private static final Color BACKGROUND = new Color(0x2B2B2B);
|
||||
private static final Color FOREGROUND = new Color(0xBBBBBB);
|
||||
private static final Color SELECTION_COLOR = new Color(0x214283);
|
||||
|
||||
public FridaWindow(Window owner) {
|
||||
super(owner, language.getText("fridaWindow.title"), ModalityType.APPLICATION_MODAL);
|
||||
initUI();
|
||||
@@ -37,110 +48,246 @@ public class FridaWindow extends WindowsJDialog {
|
||||
@Override
|
||||
public void initUI() {
|
||||
super.initUI();
|
||||
setSize(800, 600);
|
||||
setSize(1280, 800);
|
||||
setLocationRelativeTo(getOwner());
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
|
||||
JPanel mainPanel = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color color1 = new Color(235, 241, 250);
|
||||
Color color2 = new Color(255, 255, 255);
|
||||
GradientPaint gp = new GradientPaint(0, 0, color1, getWidth(), getHeight(), color2);
|
||||
g2d.setPaint(gp);
|
||||
g2d.fillRect(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
};
|
||||
mainPanel.setLayout(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
mainPanel.setBackground(BACKGROUND);
|
||||
|
||||
// 输入面板
|
||||
JPanel inputPanel = createInputPanel();
|
||||
mainPanel.add(inputPanel, BorderLayout.NORTH);
|
||||
|
||||
// 脚本编辑区
|
||||
// 中心区域(代码编辑器和日志)
|
||||
JSplitPane centerSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
centerSplit.setDividerLocation(500);
|
||||
centerSplit.setContinuousLayout(true);
|
||||
|
||||
// 脚本编辑器
|
||||
JPanel scriptPanel = createScriptPanel();
|
||||
mainPanel.add(scriptPanel, BorderLayout.CENTER);
|
||||
centerSplit.setTopComponent(scriptPanel);
|
||||
|
||||
// 日志输出
|
||||
JPanel logPanel = createLogPanel();
|
||||
mainPanel.add(logPanel, BorderLayout.SOUTH);
|
||||
centerSplit.setBottomComponent(logPanel);
|
||||
|
||||
mainPanel.add(centerSplit, BorderLayout.CENTER);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
scriptArea.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JPanel createInputPanel() {
|
||||
JPanel panel = new JPanel(new GridLayout(1, 3, 10, 10));
|
||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
|
||||
panel.setOpaque(false);
|
||||
|
||||
pidField = new JTextField(20);
|
||||
styleTextField(pidField, "输入进程ID或选择进程...");
|
||||
|
||||
pidField = new JTextField(language.getText("fridaWindow.pidField"));
|
||||
pidField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
pidField.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(57, 56, 56)),
|
||||
BorderFactory.createEmptyBorder(5, 10, 5, 10)
|
||||
));
|
||||
|
||||
JButton browseButton = new JButton(language.getText("fridaWindow.browseButton"));
|
||||
styleButton(browseButton, new Color(60, 179, 113));
|
||||
JButton browseButton = new JButton("选择进程");
|
||||
styleButton(browseButton, new Color(65, 131, 215));
|
||||
browseButton.addActionListener(this::openProcessSelectionWindow);
|
||||
|
||||
JButton injectButton = new JButton("注入脚本");
|
||||
styleButton(injectButton, new Color(215, 65, 65));
|
||||
injectButton.addActionListener(this::handleInject);
|
||||
|
||||
panel.add(new JLabel("目标进程:"));
|
||||
panel.add(pidField);
|
||||
//panel.add(attachButton);
|
||||
panel.add(browseButton);
|
||||
panel.add(injectButton);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createScriptPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createTitledBorder(language.getText("fridaWindow.panel")));
|
||||
panel.setBorder(BorderFactory.createTitledBorder("Frida脚本编辑器"));
|
||||
|
||||
scriptArea = new JTextArea();
|
||||
scriptArea.setFont(new Font("Consolas", Font.PLAIN, 14));
|
||||
JScrollPane scrollPane = new JScrollPane(scriptArea);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
||||
scriptArea = new RSyntaxTextArea();
|
||||
scriptArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
|
||||
scriptArea.setCodeFoldingEnabled(true);
|
||||
scriptArea.setAntiAliasingEnabled(true);
|
||||
scriptArea.setTabSize(4);
|
||||
scriptArea.setCaretPosition(0);
|
||||
scriptArea.setSelectionColor(SELECTION_COLOR);
|
||||
scriptArea.setCurrentLineHighlightColor(new Color(0x323232));
|
||||
scriptArea.setMarkOccurrences(true);
|
||||
scriptArea.setBracketMatchingEnabled(true);
|
||||
setupAutoCompletion();
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5));
|
||||
buttonPanel.setOpaque(false);
|
||||
Font ideaFont = new Font("JetBrains Mono", Font.PLAIN, 13);
|
||||
if (!Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()).contains("JetBrains Mono")) {
|
||||
ideaFont = new Font("Consolas", Font.PLAIN, 14);
|
||||
}
|
||||
scriptArea.setFont(ideaFont);
|
||||
|
||||
JButton injectButton = new JButton(language.getText("fridaWindow.injectButton"));
|
||||
styleButton(injectButton, new Color(220, 20, 60));
|
||||
injectButton.addActionListener(this::handleInject);
|
||||
scriptArea.setBackground(new Color(0x1E1F22));
|
||||
//codeArea.setForeground(new Color(0xBBBBBB));
|
||||
scriptArea.setSelectionColor(new Color(0x214283));
|
||||
scriptArea.setCurrentLineHighlightColor(new Color(0x323232));
|
||||
|
||||
buttonPanel.add(injectButton);
|
||||
SyntaxScheme scheme = new SyntaxScheme(true);
|
||||
scheme = configureIDEATheme(scheme);
|
||||
scriptArea.setSyntaxScheme(scheme);
|
||||
|
||||
panel.add(scrollPane, BorderLayout.CENTER);
|
||||
panel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
RTextScrollPane scrollPane = new RTextScrollPane(scriptArea);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41)));
|
||||
scrollPane.setFoldIndicatorEnabled(true);
|
||||
|
||||
panel.add(scrollPane);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private SyntaxScheme configureIDEATheme(SyntaxScheme scheme) {
|
||||
// 基础颜色配置
|
||||
Color background = new Color(0x2B2B2B); // 背景色
|
||||
Color foreground = new Color(0xE0E0E0); // 前景色(默认文本颜色)
|
||||
|
||||
// 设置全局默认样式
|
||||
Style defaultStyle = scheme.getStyle(Token.NULL);
|
||||
defaultStyle.foreground = foreground;
|
||||
defaultStyle.background = background;
|
||||
|
||||
// ======== Frida 关键字和 API 配置 ========
|
||||
// Frida 关键字(如 Java.perform, Interceptor.attach 等)
|
||||
setTokenStyle(scheme, Token.RESERVED_WORD, 0xCC7832); // 橙色
|
||||
|
||||
// Frida API(如 Java, Module, Memory 等)
|
||||
setTokenStyle(scheme, Token.FUNCTION, 0xFFC66D); // 黄色
|
||||
|
||||
// 字符串
|
||||
setTokenStyle(scheme, Token.LITERAL_STRING_DOUBLE_QUOTE, 0x6A8759); // 绿色
|
||||
|
||||
// 数字
|
||||
setTokenStyle(scheme, Token.LITERAL_NUMBER_DECIMAL_INT, 0x6897BB); // 蓝色
|
||||
|
||||
// 注释
|
||||
setTokenStyle(scheme, Token.COMMENT_EOL, 0x629755); // 浅绿色
|
||||
setTokenStyle(scheme, Token.COMMENT_MULTILINE, 0x629755); // 浅绿色
|
||||
setTokenStyle(scheme, Token.COMMENT_DOCUMENTATION, 0x629755); // 浅绿色
|
||||
|
||||
// 运算符
|
||||
setTokenStyle(scheme, Token.OPERATOR, 0xFFD700); // 金色
|
||||
|
||||
// 标识符(变量名、函数名等)
|
||||
setTokenStyle(scheme, Token.IDENTIFIER, 0xE0E0E0); // 白色
|
||||
|
||||
// 分隔符(如括号、逗号等)
|
||||
setTokenStyle(scheme, Token.SEPARATOR, 0x4EC9B0); // 青色
|
||||
|
||||
// 正则表达式
|
||||
setTokenStyle(scheme, Token.REGEX, 0xD25252); // 红色
|
||||
|
||||
// ======== Frida 特有 API 配置 ========
|
||||
// Java 相关 API
|
||||
setTokenStyle(scheme, Token.RESERVED_WORD_2, 0xCC7832); // 橙色
|
||||
|
||||
// 内存操作 API
|
||||
setTokenStyle(scheme, Token.DATA_TYPE, 0xE8BF6A); // 浅黄色
|
||||
|
||||
// 拦截器相关 API
|
||||
setTokenStyle(scheme, Token.ANNOTATION, 0xBBB529); // 黄色
|
||||
|
||||
// ======== 跨语言通用配置 ========
|
||||
// XML/HTML 标签(如果有需要)
|
||||
setTokenStyle(scheme, Token.MARKUP_TAG_NAME, 0xE8BF6A); // 浅黄色
|
||||
setTokenStyle(scheme, Token.MARKUP_TAG_DELIMITER, 0xCC7832); // 橙色
|
||||
setTokenStyle(scheme, Token.MARKUP_ENTITY_REFERENCE, 0x6897BB); // 蓝色
|
||||
|
||||
// 设置当前行高亮
|
||||
scriptArea.setHighlightCurrentLine(true);
|
||||
scriptArea.setCurrentLineHighlightColor(new Color(0x323232));
|
||||
|
||||
return scheme;
|
||||
}
|
||||
|
||||
private void setTokenStyle(SyntaxScheme scheme, int tokenType, int rgb) {
|
||||
Style style = scheme.getStyle(tokenType);
|
||||
style.foreground = new Color(rgb);
|
||||
}
|
||||
|
||||
private JPanel createLogPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setPreferredSize(new Dimension(0, 150));
|
||||
panel.setBorder(BorderFactory.createTitledBorder(language.getText("fridaWindow.panel.log")));
|
||||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createTitledBorder("日志输出"));
|
||||
|
||||
logArea = new JTextArea();
|
||||
logArea.setEditable(false);
|
||||
logArea.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
logArea.setFont(new Font("Consolas", Font.PLAIN, 12));
|
||||
logArea.setBackground(new Color(0x1E1E1E));
|
||||
logArea.setForeground(Color.WHITE);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(logArea);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41)));
|
||||
|
||||
panel.add(scrollPane);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void setupAutoCompletion() {
|
||||
try {
|
||||
CompletionProvider provider = createCompletionProvider();
|
||||
AutoCompletion ac = new AutoCompletion(provider);
|
||||
|
||||
// 关键配置
|
||||
ac.setAutoActivationEnabled(true);
|
||||
ac.setParameterAssistanceEnabled(true);
|
||||
ac.setShowDescWindow(true);
|
||||
ac.setListCellRenderer(new DarculaCompletionCellRenderer());
|
||||
|
||||
ac.install(scriptArea);
|
||||
ac.setDescriptionWindowColor(new Color(0x323232));
|
||||
//ac.setChoicesWindowSize(386,200);
|
||||
scriptArea.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
char keyChar = e.getKeyChar();
|
||||
if (Character.isLetterOrDigit(keyChar)) {
|
||||
SwingUtilities.invokeLater(ac::doCompletion);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("Autocompletion failed to initialize ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionProvider createCompletionProvider() {
|
||||
DefaultCompletionProvider provider = new DefaultCompletionProvider();
|
||||
FridaSyntax.addFridaCompletions(provider);
|
||||
FridaSyntax.addBasicCompletions(provider);
|
||||
return provider;
|
||||
}
|
||||
|
||||
private void styleTextField(JTextField field, String placeholder) {
|
||||
field.setFont(new Font("JetBrains Mono", Font.PLAIN, 14));
|
||||
field.setBackground(new Color(0x3C3F41));
|
||||
field.setForeground(Color.WHITE);
|
||||
field.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(0x515658)),
|
||||
BorderFactory.createEmptyBorder(5, 8, 5, 8)
|
||||
));
|
||||
field.putClientProperty("JTextField.placeholderText", placeholder);
|
||||
}
|
||||
|
||||
private void styleButton(JButton button, Color bgColor) {
|
||||
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
button.setFont(new Font("Segoe UI", Font.PLAIN, 13));
|
||||
button.setFocusPainted(false);
|
||||
button.setBackground(bgColor);
|
||||
button.setForeground(Color.black);
|
||||
button.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20));
|
||||
button.setForeground(Color.WHITE);
|
||||
button.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(bgColor.darker()),
|
||||
BorderFactory.createEmptyBorder(5, 15, 5, 15)
|
||||
));
|
||||
}
|
||||
|
||||
private void handleInject(ActionEvent e) {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.axis.innovators.box.gui.renderer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.AbstractBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
public class DarculaCompletionCellRenderer extends JPanel implements ListCellRenderer<Object> {
|
||||
private final JLabel titleLabel = new JLabel();
|
||||
private final JLabel descLabel = new JLabel();
|
||||
private final Color defaultBg = new Color(0x2B2B2B);
|
||||
private final Color hoverBg = new Color(0x323232);
|
||||
private final Color selectedBg = new Color(0x214283);
|
||||
private final Color textColor = new Color(0xBBBBBB);
|
||||
private final Color descColor = new Color(0x808080);
|
||||
|
||||
public DarculaCompletionCellRenderer() {
|
||||
setLayout(new BorderLayout());
|
||||
setOpaque(false); // 确保面板透明
|
||||
setBorder(new RoundedBorder(8, 1, new Color(0x3C3F41))); // 圆角边框
|
||||
|
||||
Font baseFont;
|
||||
try {
|
||||
baseFont = new Font("JetBrains Mono", Font.PLAIN, 13);
|
||||
} catch (Exception e) {
|
||||
baseFont = new Font("Consolas", Font.PLAIN, 13);
|
||||
}
|
||||
titleLabel.setFont(baseFont);
|
||||
titleLabel.setForeground(textColor);
|
||||
add(titleLabel, BorderLayout.NORTH);
|
||||
|
||||
try {
|
||||
baseFont = new Font("JetBrains Mono", Font.PLAIN, 11);
|
||||
} catch (Exception e) {
|
||||
baseFont = new Font("Consolas", Font.PLAIN, 11);
|
||||
}
|
||||
descLabel.setFont(baseFont);
|
||||
descLabel.setForeground(descColor);
|
||||
add(descLabel, BorderLayout.SOUTH);
|
||||
|
||||
setBorder(BorderFactory.createCompoundBorder(
|
||||
getBorder(),
|
||||
BorderFactory.createEmptyBorder(4, 12, 4, 12)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList<?> list,
|
||||
Object value,
|
||||
int index,
|
||||
boolean isSelected,
|
||||
boolean cellHasFocus) {
|
||||
|
||||
if (value instanceof Completion comp) {
|
||||
titleLabel.setText(comp.getTitle());
|
||||
descLabel.setText(comp.getDescription());
|
||||
} else {
|
||||
titleLabel.setText(value.toString());
|
||||
descLabel.setText("");
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
setBackground(selectedBg);
|
||||
titleLabel.setForeground(Color.WHITE);
|
||||
} else {
|
||||
setBackground(index % 2 == 0 ? defaultBg : hoverBg);
|
||||
titleLabel.setForeground(textColor);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
g2.setColor(getBackground());
|
||||
g2.fillRoundRect(0, 0, getWidth(), getHeight(), 8, 8);
|
||||
|
||||
g2.dispose();
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
private static class RoundedBorder extends AbstractBorder {
|
||||
private final int radius;
|
||||
private final int thickness;
|
||||
private final Color color;
|
||||
|
||||
public RoundedBorder(int radius, int thickness, Color color) {
|
||||
this.radius = radius;
|
||||
this.thickness = thickness;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setColor(color);
|
||||
g2.draw(new RoundRectangle2D.Float(x, y, width-1, height-1, radius, radius));
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets(Component c) {
|
||||
return new Insets(thickness, thickness, thickness, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Completion {
|
||||
String getTitle();
|
||||
String getDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.axis.innovators.box.network;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
|
||||
public class NetworkFileManagement {
|
||||
private static final String BASE_URL = NetworkUserManagement.BASE_URL;
|
||||
private static final int TIMESTAMP_THRESHOLD = 300;
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param user 上传用户
|
||||
* @param file 要上传的文件
|
||||
* @return 文件id
|
||||
* @throws IOException 上传失败抛出异常
|
||||
*/
|
||||
public int uploadFile(Users user, File file) throws IOException {
|
||||
validateTimestamp(user);
|
||||
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(BASE_URL + "/upload").openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Authorization", "Bearer " + user.getToken());
|
||||
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (OutputStream output = connection.getOutputStream();
|
||||
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) {
|
||||
|
||||
// Timestamp part
|
||||
writer.append("--").append(boundary).append("\r\n");
|
||||
writer.append("Content-Disposition: form-data; name=\"timestamp\"\r\n\r\n");
|
||||
writer.append(String.valueOf(user.getTimestamp())).append("\r\n").flush();
|
||||
|
||||
// File part
|
||||
writer.append("--").append(boundary).append("\r\n");
|
||||
writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"").append(file.getName()).append("\"\r\n");
|
||||
writer.append("Content-Type: application/octet-stream\r\n\r\n").flush();
|
||||
|
||||
try (FileInputStream input = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, bytesRead);
|
||||
}
|
||||
output.flush();
|
||||
}
|
||||
|
||||
writer.append("\r\n--").append(boundary).append("--\r\n").flush();
|
||||
}
|
||||
|
||||
String response = handleResponse(connection);
|
||||
return extractFileId(response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从JSON响应中提取file_id字段
|
||||
*/
|
||||
private int extractFileId(String jsonResponse) {
|
||||
String[] parts = jsonResponse.split("\"file_id\":");
|
||||
if (parts.length < 2) {
|
||||
throw new RuntimeException("Invalid response format: " + jsonResponse);
|
||||
}
|
||||
return Integer.parseInt(parts[1].replaceAll("[^0-9]", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param user 下载用户
|
||||
* @param fileId 文件ID
|
||||
* @param savePath 保存路径
|
||||
* @return 文件
|
||||
* @throws IOException 下载失败抛出异常
|
||||
*/
|
||||
public File downloadFile(Users user, int fileId, String savePath) throws IOException {
|
||||
validateTimestamp(user);
|
||||
String query = String.format("file_id=%d×tamp=%d", fileId, user.getTimestamp());
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(BASE_URL + "/download?" + query).openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Authorization", "Bearer " + user.getToken());
|
||||
|
||||
int status = connection.getResponseCode();
|
||||
if (status == HttpURLConnection.HTTP_OK) {
|
||||
String fileName = "downloaded_file";
|
||||
try (InputStream input = connection.getInputStream();
|
||||
FileOutputStream output = new FileOutputStream(savePath + File.separator + fileName)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
return new File(savePath + File.separator + fileName);
|
||||
} else {
|
||||
handleErrorResponse(connection);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateTimestamp(Users user) {
|
||||
long currentTimestamp = Instant.now().getEpochSecond();
|
||||
if (Math.abs(currentTimestamp - user.getTimestamp()) > TIMESTAMP_THRESHOLD) {
|
||||
throw new SecurityException("Timestamp validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
private String handleResponse(HttpURLConnection connection) throws IOException {
|
||||
int status = connection.getResponseCode();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
status >= 400 ? connection.getErrorStream() : connection.getInputStream()
|
||||
))) {
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
if (status >= 400) {
|
||||
throw new RuntimeException("HTTP Error " + status + ": " + response);
|
||||
}
|
||||
return response.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleErrorResponse(HttpURLConnection connection) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
throw new RuntimeException("HTTP Error " + connection.getResponseCode() + ": " + response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.axis.innovators.box.network;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 网络用户管理
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class NetworkUserManagement {
|
||||
static final String BASE_URL = "https://yourdomain.com";
|
||||
|
||||
/**
|
||||
* 注册用户
|
||||
* @param username 用户名
|
||||
* @param password 用户密码
|
||||
* @return 用户信息
|
||||
* @throws IOException 注册失败时抛出异常
|
||||
*/
|
||||
public Users registerUser(String username, String password) throws IOException {
|
||||
long timestamp = Instant.now().getEpochSecond();
|
||||
String jsonInput = String.format(
|
||||
"{\"username\": \"%s\", \"password\": \"%s\", \"timestamp\": %d}",
|
||||
username, password, timestamp
|
||||
);
|
||||
|
||||
HttpURLConnection connection = sendPostRequest("/register", jsonInput);
|
||||
return parseUserResponse(connection, username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录用户
|
||||
* @param username 用户名
|
||||
* @param password 用户密码
|
||||
* @return 用户信息
|
||||
* @throws IOException 登录失败时抛出异常
|
||||
*/
|
||||
public Users loginUser(String username, String password) throws IOException {
|
||||
long timestamp = Instant.now().getEpochSecond();
|
||||
String jsonInput = String.format(
|
||||
"{\"username\": \"%s\", \"password\": \"%s\", \"timestamp\": %d}",
|
||||
username, password, timestamp
|
||||
);
|
||||
|
||||
HttpURLConnection connection = sendPostRequest("/login", jsonInput);
|
||||
return parseUserResponse(connection, username, password);
|
||||
}
|
||||
|
||||
private HttpURLConnection sendPostRequest(String endpoint, String jsonInput) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(BASE_URL + endpoint).openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
byte[] input = jsonInput.getBytes(StandardCharsets.UTF_8);
|
||||
os.write(input, 0, input.length);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private Users parseUserResponse(HttpURLConnection connection, String username, String password) throws IOException {
|
||||
int status = connection.getResponseCode();
|
||||
if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
String token = extractToken(response.toString());
|
||||
Users user = new Users();
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
// 注意:实际应存储哈希值
|
||||
user.setToken(token);
|
||||
user.setTimestamp(Instant.now().getEpochSecond());
|
||||
return user;
|
||||
}
|
||||
} else {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
|
||||
StringBuilder errorResponse = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
errorResponse.append(line);
|
||||
}
|
||||
throw new RuntimeException("Registration/Login failed: " + errorResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String extractToken(String jsonResponse) {
|
||||
// 简单实现,实际应使用JSON解析库
|
||||
return jsonResponse.split("\"token\":\"")[1].split("\"")[0];
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/axis/innovators/box/network/Users.java
Normal file
21
src/main/java/com/axis/innovators/box/network/Users.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.axis.innovators.box.network;
|
||||
|
||||
public class Users {
|
||||
private String username;
|
||||
private String password;
|
||||
private String token;
|
||||
private long timestamp;
|
||||
|
||||
// Getters and Setters
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
|
||||
public String getToken() { return token; }
|
||||
public void setToken(String token) { this.token = token; }
|
||||
|
||||
public long getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.axis.innovators.box.gui.MainWindow;
|
||||
import com.axis.innovators.box.plugins.PluginDescriptor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.tzd.lm.LM;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -54,6 +55,15 @@ public class RegistrationTool {
|
||||
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
try {
|
||||
LM.loadLibrary(LM.CUDA);
|
||||
} catch (Exception ex) {
|
||||
logger.error("无法加载AI推理库", ex);
|
||||
JOptionPane.showMessageDialog(null, "无法加载AI推理库",
|
||||
"无法加载AI推理库", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
||||
LocalWindow dialog = new LocalWindow(owner);
|
||||
main.popupWindow(dialog);
|
||||
|
||||
Reference in New Issue
Block a user