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 'application'
|
||||||
id 'edu.sc.seis.launch4j' version '2.5.4'
|
id 'edu.sc.seis.launch4j' version '2.5.4'
|
||||||
id 'org.openjfx.javafxplugin' version '0.1.0'
|
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 版本检查
|
// JDK 版本检查
|
||||||
@@ -16,17 +22,16 @@ group = 'com.axis.innovators.box'
|
|||||||
version = '0.0.1'
|
version = '0.0.1'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { setUrl("https://maven.aliyun.com/repository/central") }
|
maven {
|
||||||
maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
|
url 'https://maven.aliyun.com/repository/public'
|
||||||
maven { setUrl("https://maven.aliyun.com/repository/google") }
|
metadataSources {
|
||||||
maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
|
mavenPom()
|
||||||
maven { setUrl("https://maven.aliyun.com/repository/public") }
|
artifact()
|
||||||
maven { setUrl("https://jitpack.io") }
|
ignoreGradleMetadataRedirection()
|
||||||
maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/") }
|
}
|
||||||
maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/jcenter") }
|
}
|
||||||
gradlePluginPortal()
|
maven { url 'https://jitpack.io' }
|
||||||
google()
|
// mavenCentral()
|
||||||
mavenCentral()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -40,11 +45,11 @@ dependencies {
|
|||||||
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
||||||
implementation 'org.apache.logging.log4j:log4j-slf4j-impl: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:9.7.1'
|
||||||
implementation 'org.ow2.asm:asm-commons:7.1'
|
implementation 'org.ow2.asm:asm-commons:9.7.1'
|
||||||
implementation 'org.ow2.asm:asm-analysis:7.1'
|
implementation 'org.ow2.asm:asm-analysis:9.7.1'
|
||||||
implementation 'org.ow2.asm:asm-util:7.0'
|
implementation 'org.ow2.asm:asm-util:9.7.1'
|
||||||
implementation 'org.ow2.asm:asm-tree:7.1'
|
implementation 'org.ow2.asm:asm-tree:9.7.1'
|
||||||
|
|
||||||
implementation 'org.jsoup:jsoup:1.17.2'
|
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-core:0.5.36' // 使用JitPack版本
|
||||||
implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.5.36'
|
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 'org.apache.commons:commons-compress:1.23.0'
|
||||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
|
||||||
implementation 'org.controlsfx:controlsfx:11.1.2' // 现代化UI组件
|
implementation 'org.controlsfx:controlsfx:11.1.2' // 现代化UI组件
|
||||||
@@ -76,6 +87,27 @@ dependencies {
|
|||||||
implementation 'org.benf:cfr:0.152'
|
implementation 'org.benf:cfr:0.152'
|
||||||
|
|
||||||
implementation 'com.github.javaparser:javaparser-core:3.25.1'
|
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 目录
|
// 分离依赖项到 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 {
|
try {
|
||||||
LibraryLoad.loadLibrary("FridaNative");
|
LibraryLoad.loadLibrary("FridaNative");
|
||||||
LibraryLoad.loadLibrary("ThrowSafely");
|
LibraryLoad.loadLibrary("ThrowSafely");
|
||||||
LM.loadLibrary(LM.CUDA);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Failed to load the 'FridaNative' library", e);
|
logger.error("Failed to load the 'FridaNative' library", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@@ -231,7 +230,6 @@ public class AxisInnovatorsBox {
|
|||||||
* 初始化Log4j2
|
* 初始化Log4j2
|
||||||
*/
|
*/
|
||||||
public void initLog4j2() {
|
public void initLog4j2() {
|
||||||
Log4j2OutputStream.redirectSystemStreams();
|
|
||||||
|
|
||||||
logger.info("Application Version: {}", VERSIONS);
|
logger.info("Application Version: {}", VERSIONS);
|
||||||
logger.info("Authors: {}", String.join(", ", AUTHOR));
|
logger.info("Authors: {}", String.join(", ", AUTHOR));
|
||||||
@@ -245,6 +243,8 @@ public class AxisInnovatorsBox {
|
|||||||
logger.info("Java Home: {}", System.getProperty("java.home"));
|
logger.info("Java Home: {}", System.getProperty("java.home"));
|
||||||
logger.info("Java Class Path: {}", System.getProperty("java.class.path"));
|
logger.info("Java Class Path: {}", System.getProperty("java.class.path"));
|
||||||
logger.info("ClassLoader.getSystemClassLoader(): {}", ClassLoader.getSystemClassLoader());
|
logger.info("ClassLoader.getSystemClassLoader(): {}", ClassLoader.getSystemClassLoader());
|
||||||
|
|
||||||
|
Log4j2OutputStream.redirectSystemStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -277,6 +277,12 @@ public class AxisInnovatorsBox {
|
|||||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||||
"system:flatLightLaf_theme");
|
"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());
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Failed to load the system facade class", e);
|
logger.warn("Failed to load the system facade class", e);
|
||||||
@@ -334,7 +340,7 @@ public class AxisInnovatorsBox {
|
|||||||
public static void run(String[] args) {
|
public static void run(String[] args) {
|
||||||
main = new AxisInnovatorsBox(args);
|
main = new AxisInnovatorsBox(args);
|
||||||
try {
|
try {
|
||||||
main.initLog4j2();
|
//main.initLog4j2();
|
||||||
main.setTopic();
|
main.setTopic();
|
||||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||||
for (Map<String, String> fileInfo : validFiles) {
|
for (Map<String, String> fileInfo : validFiles) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ public class Log4j2OutputStream extends OutputStream {
|
|||||||
systemOutContent.write(b, off, len);
|
systemOutContent.write(b, off, len);
|
||||||
String message = new String(b, off, len).trim();
|
String message = new String(b, off, len).trim();
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.axis.innovators.box.decompilation.gui;
|
package com.axis.innovators.box.decompilation.gui;
|
||||||
|
|
||||||
import com.axis.innovators.box.gui.LoadIcon;
|
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.JavaParser;
|
||||||
import com.github.javaparser.ParseResult;
|
import com.github.javaparser.ParseResult;
|
||||||
import com.github.javaparser.Position;
|
import com.github.javaparser.Position;
|
||||||
@@ -19,7 +17,6 @@ import java.awt.event.*;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.Timer;
|
|
||||||
import javax.swing.event.HyperlinkEvent;
|
import javax.swing.event.HyperlinkEvent;
|
||||||
import javax.swing.event.TreeSelectionEvent;
|
import javax.swing.event.TreeSelectionEvent;
|
||||||
import javax.swing.event.TreeSelectionListener;
|
import javax.swing.event.TreeSelectionListener;
|
||||||
@@ -283,7 +280,7 @@ public class ModernJarViewer extends JFrame {
|
|||||||
int offsetX = -5;
|
int offsetX = -5;
|
||||||
int offsetY = 0;
|
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() {
|
currentSearchDialog.addWindowListener(new WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent e) {
|
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;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.fife.ui.autocomplete.*;
|
||||||
import org.tzd.frida.windows.Frida;
|
import org.tzd.frida.windows.Frida;
|
||||||
|
|
||||||
|
import org.fife.ui.rsyntaxtextarea.*;
|
||||||
|
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
import javax.swing.table.DefaultTableModel;
|
import javax.swing.table.DefaultTableModel;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.*;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,11 +30,16 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class FridaWindow extends WindowsJDialog {
|
public class FridaWindow extends WindowsJDialog {
|
||||||
private static final Logger logger = LogManager.getLogger(FridaWindow.class);
|
private static final Logger logger = LogManager.getLogger(FridaWindow.class);
|
||||||
private JTextArea scriptArea;
|
private RSyntaxTextArea scriptArea;
|
||||||
private JTextArea logArea;
|
private JTextArea logArea;
|
||||||
private JTextField pidField;
|
private JTextField pidField;
|
||||||
private boolean isRepetition = false;
|
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) {
|
public FridaWindow(Window owner) {
|
||||||
super(owner, language.getText("fridaWindow.title"), ModalityType.APPLICATION_MODAL);
|
super(owner, language.getText("fridaWindow.title"), ModalityType.APPLICATION_MODAL);
|
||||||
initUI();
|
initUI();
|
||||||
@@ -37,110 +48,246 @@ public class FridaWindow extends WindowsJDialog {
|
|||||||
@Override
|
@Override
|
||||||
public void initUI() {
|
public void initUI() {
|
||||||
super.initUI();
|
super.initUI();
|
||||||
setSize(800, 600);
|
setSize(1280, 800);
|
||||||
setLocationRelativeTo(getOwner());
|
setLocationRelativeTo(getOwner());
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
JPanel mainPanel = new JPanel() {
|
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||||
@Override
|
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
protected void paintComponent(Graphics g) {
|
mainPanel.setBackground(BACKGROUND);
|
||||||
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 inputPanel = createInputPanel();
|
JPanel inputPanel = createInputPanel();
|
||||||
mainPanel.add(inputPanel, BorderLayout.NORTH);
|
mainPanel.add(inputPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
// 脚本编辑区
|
// 中心区域(代码编辑器和日志)
|
||||||
|
JSplitPane centerSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||||
|
centerSplit.setDividerLocation(500);
|
||||||
|
centerSplit.setContinuousLayout(true);
|
||||||
|
|
||||||
|
// 脚本编辑器
|
||||||
JPanel scriptPanel = createScriptPanel();
|
JPanel scriptPanel = createScriptPanel();
|
||||||
mainPanel.add(scriptPanel, BorderLayout.CENTER);
|
centerSplit.setTopComponent(scriptPanel);
|
||||||
|
|
||||||
// 日志输出
|
// 日志输出
|
||||||
JPanel logPanel = createLogPanel();
|
JPanel logPanel = createLogPanel();
|
||||||
mainPanel.add(logPanel, BorderLayout.SOUTH);
|
centerSplit.setBottomComponent(logPanel);
|
||||||
|
|
||||||
|
mainPanel.add(centerSplit, BorderLayout.CENTER);
|
||||||
|
|
||||||
setContentPane(mainPanel);
|
setContentPane(mainPanel);
|
||||||
|
|
||||||
|
addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowOpened(WindowEvent e) {
|
||||||
|
scriptArea.requestFocusInWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel createInputPanel() {
|
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);
|
panel.setOpaque(false);
|
||||||
|
|
||||||
|
pidField = new JTextField(20);
|
||||||
|
styleTextField(pidField, "输入进程ID或选择进程...");
|
||||||
|
|
||||||
pidField = new JTextField(language.getText("fridaWindow.pidField"));
|
JButton browseButton = new JButton("选择进程");
|
||||||
pidField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
styleButton(browseButton, new Color(65, 131, 215));
|
||||||
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));
|
|
||||||
browseButton.addActionListener(this::openProcessSelectionWindow);
|
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(pidField);
|
||||||
//panel.add(attachButton);
|
|
||||||
panel.add(browseButton);
|
panel.add(browseButton);
|
||||||
|
panel.add(injectButton);
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel createScriptPanel() {
|
private JPanel createScriptPanel() {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setOpaque(false);
|
panel.setBorder(BorderFactory.createTitledBorder("Frida脚本编辑器"));
|
||||||
panel.setBorder(BorderFactory.createTitledBorder(language.getText("fridaWindow.panel")));
|
|
||||||
|
|
||||||
scriptArea = new JTextArea();
|
scriptArea = new RSyntaxTextArea();
|
||||||
scriptArea.setFont(new Font("Consolas", Font.PLAIN, 14));
|
scriptArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
|
||||||
JScrollPane scrollPane = new JScrollPane(scriptArea);
|
scriptArea.setCodeFoldingEnabled(true);
|
||||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
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));
|
Font ideaFont = new Font("JetBrains Mono", Font.PLAIN, 13);
|
||||||
buttonPanel.setOpaque(false);
|
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"));
|
scriptArea.setBackground(new Color(0x1E1F22));
|
||||||
styleButton(injectButton, new Color(220, 20, 60));
|
//codeArea.setForeground(new Color(0xBBBBBB));
|
||||||
injectButton.addActionListener(this::handleInject);
|
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);
|
RTextScrollPane scrollPane = new RTextScrollPane(scriptArea);
|
||||||
panel.add(buttonPanel, BorderLayout.SOUTH);
|
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41)));
|
||||||
|
scrollPane.setFoldIndicatorEnabled(true);
|
||||||
|
|
||||||
|
panel.add(scrollPane);
|
||||||
|
|
||||||
return panel;
|
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() {
|
private JPanel createLogPanel() {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setPreferredSize(new Dimension(0, 150));
|
panel.setBorder(BorderFactory.createTitledBorder("日志输出"));
|
||||||
panel.setBorder(BorderFactory.createTitledBorder(language.getText("fridaWindow.panel.log")));
|
|
||||||
panel.setOpaque(false);
|
|
||||||
|
|
||||||
logArea = new JTextArea();
|
logArea = new JTextArea();
|
||||||
logArea.setEditable(false);
|
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);
|
JScrollPane scrollPane = new JScrollPane(logArea);
|
||||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41)));
|
||||||
|
|
||||||
panel.add(scrollPane);
|
panel.add(scrollPane);
|
||||||
return panel;
|
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) {
|
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.setFocusPainted(false);
|
||||||
button.setBackground(bgColor);
|
button.setBackground(bgColor);
|
||||||
button.setForeground(Color.black);
|
button.setForeground(Color.WHITE);
|
||||||
button.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20));
|
button.setBorder(BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createLineBorder(bgColor.darker()),
|
||||||
|
BorderFactory.createEmptyBorder(5, 15, 5, 15)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInject(ActionEvent e) {
|
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 com.axis.innovators.box.plugins.PluginDescriptor;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.tzd.lm.LM;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -54,6 +55,15 @@ public class RegistrationTool {
|
|||||||
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
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());
|
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
||||||
LocalWindow dialog = new LocalWindow(owner);
|
LocalWindow dialog = new LocalWindow(owner);
|
||||||
main.popupWindow(dialog);
|
main.popupWindow(dialog);
|
||||||
|
|||||||
Reference in New Issue
Block a user