From f4baf0b1e4c4c348d74c636fb104ce510ed64d17 Mon Sep 17 00:00:00 2001 From: tzdwindows 7 <3076584115@qq.com> Date: Sat, 1 Mar 2025 16:19:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(gui):=20=E9=87=8D=E6=9E=84=20FridaWindow?= =?UTF-8?q?=20=E7=B1=BB=E5=B9=B6=E6=B7=BB=E5=8A=A0=E6=96=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -重新设计了窗口布局和样式,增加了更多控制按钮 -集成了自动补全功能,支持 Frida 相关的关键字和 API - 优化了代码编辑器的配色方案,支持语法高亮 - 改进了日志输出区域的可读性- 移除了未使用的 LanguageManager 导入 --- build.gradle | 66 ++- plug-in/Template-1.0-SNAPSHOT.jar | Bin 4537 -> 0 bytes plug-in/python/Testing/main.py | 90 ---- plug-in/python/Testing/metadata.json | 13 - .../innovators/box/AxisInnovatorsBox.java | 12 +- .../innovators/box/Log4j2OutputStream.java | 2 + .../decompilation/gui/ModernJarViewer.java | 5 +- .../box/decompilation/util/JarObfuscator.java | 445 ++++++++++++++++++ .../axis/innovators/box/gui/FridaSyntax.java | 306 ++++++++++++ .../axis/innovators/box/gui/FridaWindow.java | 257 +++++++--- .../DarculaCompletionCellRenderer.java | 116 +++++ .../box/network/NetworkFileManagement.java | 137 ++++++ .../box/network/NetworkUserManagement.java | 100 ++++ .../axis/innovators/box/network/Users.java | 21 + .../box/register/RegistrationTool.java | 10 + 15 files changed, 1398 insertions(+), 182 deletions(-) delete mode 100644 plug-in/Template-1.0-SNAPSHOT.jar delete mode 100644 plug-in/python/Testing/main.py delete mode 100644 plug-in/python/Testing/metadata.json create mode 100644 src/main/java/com/axis/innovators/box/decompilation/util/JarObfuscator.java create mode 100644 src/main/java/com/axis/innovators/box/gui/FridaSyntax.java create mode 100644 src/main/java/com/axis/innovators/box/gui/renderer/DarculaCompletionCellRenderer.java create mode 100644 src/main/java/com/axis/innovators/box/network/NetworkFileManagement.java create mode 100644 src/main/java/com/axis/innovators/box/network/NetworkUserManagement.java create mode 100644 src/main/java/com/axis/innovators/box/network/Users.java diff --git a/build.gradle b/build.gradle index a9dafea..ed3ae0a 100644 --- a/build.gradle +++ b/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 目录 diff --git a/plug-in/Template-1.0-SNAPSHOT.jar b/plug-in/Template-1.0-SNAPSHOT.jar deleted file mode 100644 index 94d65c2a065501158691e27b984a6406e51aca39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4537 zcmb7HcU)8H5)FbB2}OzvQj|_8p#%X1bd_%CHGl%42?C)7r5QwubRq&P3Md+?fJhMt zO&}n>Yv@HR^xmbsWL*_`>+XA#-@W<%m~-ad%w%Sc4wRIP2>_s^1ej$SngR%Rgm@zu z72(oUHn=FNuB9SI3efrPsA{KaTHt?Vo{iwZO1KE{AC8(AwbWIV^$oz9Dl3|;EgDc! za2E|!RH(hRqfk%07c%zo{l_B*$m|oeeu_#)AhULH{wd&LAL5e{ec)aW9{=b_d(hF^ z#ohKN@53+r+kbf@ZJk}6;K=`msZBbJ{y_o&P>=!u(!Yio{Ds$Wf!jDBt|=0pK*!1R zngarC?F9Gmh}YB99{ipD7BsS?lvbT4ZPbrlh>sl`M@>P0ojFTDQJC*iw$Fep6lSxa z;z+SnIGGlY9-~-QHB9C;Qd57P{YPSQ;>JjO$@l2gZu=Bhc}uV4K8;3bGPQGLB|6x%Owd4D%{Emj!;g$|5zPbC zt(R1s(<+N^mcKT3%aFZALn24|;nLNztFq49U)a}1w-YgExkKh0NW(m@@6ANt=f6-4 z){nN1hkrh`+1JeJP?EU`;#e;phgo}l$&e^t*8?^@xYF9iQjsv%^?Nal%~QS=+w5s0 zlcvEmsFW_yJ*9hvzw*6C5BH6U{W+J%T$niyb?0W=Jl)NzTT&2j6R#nARH6k~vGbMU zADMJMy^Y%z{yvbWR@A~s0bUhVeJ<^iOY2n1cjS4hOLsYDg%wUL#^qelBKJ~}P68~l zi#0+jaPI0Ope7Wq8a_46S$?i=J!94wCf4t}$S=D}t`IJgu^OVoCXFq>>;jgt%wn#ae)_E628YV@KwkZ`2#DeHjsGzsRXf-&o zcKzdUSfQd>(`~LYE8kb9wv8PKBd{^J?JW=}X(RXch98tO+?5h^1%lSHW^oaZJU{Vf z2w7?@^IRa60jyZ9xq1LkQuN`Xu+yTUP|9|C`QLk zI~WKljHgC0_v4`+$!4HP>)O{gC-H89@Eqvx@oD7E2a^#-jFEJkd+#TstK$l z*rT!YiH+x^h1)gNYv}x&>H4Z?TYSh@S)UaR!M*y9+G$TU(%x{@XU1mf=e}O|+@}k^F+7pY zie7&$ar5B|SaEh7yfnM?ZQpy8@F~#J5JOc#79^_@+e?yVU<=8|VL7HoCog$ONIn%? z>?>qvjn%y(Kz`m8?<@4T^DTgV*H>bFR60ByCfxdU6DOZ?5Y?}Ma z=fA{^kA#Vt_XN#lb&o%7<;b`$ta}nCU01n$ypm^TsK>tV!~5DMP~yrXifCui&wZzF zfVf0t9?OY6upQWaIF1?|c>5Zg2*JoGXp>=;ll|s|RSEWU?2<33*Yh3a;7AQDTRs`aE%Gl*zMv~E_EtV1)M6*+eFpU zL^5@1q;?ZZZ7p%9*fqRFDi3_y;{~9~m)0vbDG)|?e~kq(!9Q*L;%~VW#2K}g%34Ld6#*6f*SC#7+_GJd8?9-v4_+uh# z!|zx`!(OT%<-vq3I{4d~6bQ}5|z18V!%&`zTxs}$|!oq6Q z{wCEJ^JP|EbP@cl8dsZmk?4)1F5ypVjAsVBBUrk{c3#JrOY#n1X=`8(Y0^34#d6C3 zo+lU?Atn?2?i8jkR*001l@#BBc9&`4w;$Vi64M-`feyULD21~uV`hZZiZ29Ly*O-*w5*^f8(IvW z)+~rLP}G)kytHLE3Nh`vX3@j9OBV&a)zy{Y=?QJ#B6J#B2#;H|5xd22X>U{qz=Yfy&`icrbY z{NY~R(C*B5I;|JvYy~7^Cw)1S{hs3FAP>a8(s8aeJ*!t{GUjspHaoMy8NGX6fRtyK zZXLrV7IP?b@zeU>uxn3RX}ekFVfT6Ni~lHKlQWIiilso@$(oFUh4#W{wjsANyep^C zra`{WTemmt69>N#%JK z9}y!WMwjZ*jmFFM%RTX@E&~&5IcqBOofw|66uR~`9LKQmQ$$*aQat5~KS5Hbgw2N( zJEdEVQ-?V2E}l$9zAIft#hDN9Z|U`K471~(j#E_Q+~R6b8yznD`)i-eNB1`uJmsP% zfOV#V$vD|6DW4RZ(z4yYSXvCek=CloAXYXCa-w}e_U;O>$CvkNw|pmDxH;KNDoi?F zVA$e=Z9y&~(P&3Ac8eQqQtC8uj6drt% zCg%96j`s7j`WzvNBr{lkDS^HRIr_TtBfObBLQcgyzeo5elVvL+ZBPglB4FIIa*)C% z9bf_>&8+dw6`HQ6#HIU++(~MQXK1dq4iOG)QcWE^iu1-6Nj!isd>ZI>3jMT->D&-D z9e#TCDZqD{xep4{cV_pwIKzVIyct&!Kn0u-tQ3B;SiCg5Il*5Q=2{5jb_wL8#BV;n z6}9wOk1@+SE6iB-rSxz^mNdAvLh0NYe8Y~`=6$J@TGpr6$2vSuV;Ak#Mep&3O-5}+ zb2hLfG+m#3(aEBU5RUITE@2t88?_n{IvABFJlSy}Xt1khi_$VS=cfHaUai}Ze%$CZ zqj>NaFUu20gU>LvjVQWn#jeP5!a2g|bf6?hm`H!E90`9}g3$x!E)u`L>&Sz!ztfRK zXQE*OK!p2u*a1~}kl}D=B8tbaMhtv|a3uDre?kzYtza4RIr%&$hoE)kADW52h=!z71A=QkQmfFMDN{(|INYy5=-Js9Kf)Xt9> zjD%+VFERc``yA#!ybb-}*C#ak|DFG9r$5MkxE6?0>Z=idX4Yc|*bmICgA9igMVwhb p7&L#u@XK@^o?zd|FahEJM)fUe-`?^lIS~vbd>jc`ko~jz{sWOV8m|BV diff --git a/plug-in/python/Testing/main.py b/plug-in/python/Testing/main.py deleted file mode 100644 index 8a5f2ad..0000000 --- a/plug-in/python/Testing/main.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/plug-in/python/Testing/metadata.json b/plug-in/python/Testing/metadata.json deleted file mode 100644 index 0aacb29..0000000 --- a/plug-in/python/Testing/metadata.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": "testing", - "name": "测试", - "version": "0.0.1", - "description": "测试插件", - "author": "tzdwindows 7", - "dependencies": [], - - "_comment": { - "warning": "本文件为插件元数据配置,修改后需重启应用生效", - "path": "插件资源应放置在plugins/{id}/目录下" - } -} diff --git a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java index 4361202..389afec 100644 --- a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java +++ b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java @@ -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> validFiles = ArgsParser.parseArgs(args); for (Map fileInfo : validFiles) { diff --git a/src/main/java/com/axis/innovators/box/Log4j2OutputStream.java b/src/main/java/com/axis/innovators/box/Log4j2OutputStream.java index 6f95af6..4381e84 100644 --- a/src/main/java/com/axis/innovators/box/Log4j2OutputStream.java +++ b/src/main/java/com/axis/innovators/box/Log4j2OutputStream.java @@ -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); + } /** diff --git a/src/main/java/com/axis/innovators/box/decompilation/gui/ModernJarViewer.java b/src/main/java/com/axis/innovators/box/decompilation/gui/ModernJarViewer.java index cfa31fa..8627a7e 100644 --- a/src/main/java/com/axis/innovators/box/decompilation/gui/ModernJarViewer.java +++ b/src/main/java/com/axis/innovators/box/decompilation/gui/ModernJarViewer.java @@ -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) { diff --git a/src/main/java/com/axis/innovators/box/decompilation/util/JarObfuscator.java b/src/main/java/com/axis/innovators/box/decompilation/util/JarObfuscator.java new file mode 100644 index 0000000..1414f28 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/decompilation/util/JarObfuscator.java @@ -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 jarEntries = new LinkedHashMap<>(); + private final List whitelistList = new ArrayList<>(); + private final Map classMapping = new HashMap<>(); + private final Map classNameMapping = new HashMap<>(); + private final Map fieldMapping = new HashMap<>(); + private final Map 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 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 (!"".equals(name) && !"".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 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 packageMappings = new HashMap<>(); + private final Map classMappings = new HashMap<>(); + private final Map 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 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 ("".equals(name) || "".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 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 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"); + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/gui/FridaSyntax.java b/src/main/java/com/axis/innovators/box/gui/FridaSyntax.java new file mode 100644 index 0000000..54db963 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/FridaSyntax.java @@ -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)); + } + } +} diff --git a/src/main/java/com/axis/innovators/box/gui/FridaWindow.java b/src/main/java/com/axis/innovators/box/gui/FridaWindow.java index e242988..c3c3a52 100644 --- a/src/main/java/com/axis/innovators/box/gui/FridaWindow.java +++ b/src/main/java/com/axis/innovators/box/gui/FridaWindow.java @@ -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) { diff --git a/src/main/java/com/axis/innovators/box/gui/renderer/DarculaCompletionCellRenderer.java b/src/main/java/com/axis/innovators/box/gui/renderer/DarculaCompletionCellRenderer.java new file mode 100644 index 0000000..c58bb82 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/renderer/DarculaCompletionCellRenderer.java @@ -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 { + 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(); + } +} diff --git a/src/main/java/com/axis/innovators/box/network/NetworkFileManagement.java b/src/main/java/com/axis/innovators/box/network/NetworkFileManagement.java new file mode 100644 index 0000000..c5fed1f --- /dev/null +++ b/src/main/java/com/axis/innovators/box/network/NetworkFileManagement.java @@ -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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/network/NetworkUserManagement.java b/src/main/java/com/axis/innovators/box/network/NetworkUserManagement.java new file mode 100644 index 0000000..2d7d399 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/network/NetworkUserManagement.java @@ -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]; + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/network/Users.java b/src/main/java/com/axis/innovators/box/network/Users.java new file mode 100644 index 0000000..e94ef67 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/network/Users.java @@ -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; } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/register/RegistrationTool.java b/src/main/java/com/axis/innovators/box/register/RegistrationTool.java index ae0780d..bfcd1bb 100644 --- a/src/main/java/com/axis/innovators/box/register/RegistrationTool.java +++ b/src/main/java/com/axis/innovators/box/register/RegistrationTool.java @@ -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);