commit d06b32a92fe92ab636534228bb3dab3c57a5d6d2 Author: tzdwindows 7 <3076584115@qq.com> Date: Wed Feb 5 15:02:27 2025 +0800 第一次提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/$PROJECT_FILE$ b/.idea/$PROJECT_FILE$ new file mode 100644 index 0000000..58b7e3e --- /dev/null +++ b/.idea/$PROJECT_FILE$ @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..2a65317 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6d87073 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d4f59ee --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/qaplug_profiles.xml b/.idea/qaplug_profiles.xml new file mode 100644 index 0000000..3dfd21f --- /dev/null +++ b/.idea/qaplug_profiles.xml @@ -0,0 +1,465 @@ + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Windows AxisInnovatorsBox.zip b/Windows AxisInnovatorsBox.zip new file mode 100644 index 0000000..2006c46 Binary files /dev/null and b/Windows AxisInnovatorsBox.zip differ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..ab3110d --- /dev/null +++ b/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group = 'com.axis.innovators.box' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bf8819e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Feb 04 17:20:23 CST 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/library/FridaNative.dll b/library/FridaNative.dll new file mode 100644 index 0000000..27648f1 Binary files /dev/null and b/library/FridaNative.dll differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..a0a2d03 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'AxisInnovatorsBox' + diff --git a/src/main/java/com/axis/innovators/box/Main.java b/src/main/java/com/axis/innovators/box/Main.java new file mode 100644 index 0000000..88e31ff --- /dev/null +++ b/src/main/java/com/axis/innovators/box/Main.java @@ -0,0 +1,70 @@ +package com.axis.innovators.box; + +import com.axis.innovators.box.events.GlobalEventBus; +import com.axis.innovators.box.events.SettingsLoadEvents; +import com.axis.innovators.box.events.SubscribeEvent; +import com.axis.innovators.box.gui.FridaWindow; +import com.axis.innovators.box.gui.MainWindow; +import com.axis.innovators.box.tools.FolderCreator; +import com.axis.innovators.box.tools.LibraryLoad; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; + +/** + * 主类 + * @author tzdwindows 7 + */ +public class Main { + + static { + LibraryLoad.loadLibrary("FridaNative"); + } + + @SubscribeEvent + public void onSettingsLoad(SettingsLoadEvents event) { + JLabel placeholder = new JLabel("设置功能开发中...", SwingConstants.CENTER); + placeholder.setFont(new Font("微软雅黑", Font.PLAIN, 24)); + placeholder.setForeground(new Color(127, 140, 153)); + event.content().add(placeholder, BorderLayout.CENTER); + + // 我不想写这个了你们自己实现 + } + + public static void main(String[] args) { + // 注册事件 + GlobalEventBus.EVENT_BUS.register(new Main()); + // 设置系统外观 + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception ignored) {} + + // 创建窗口 + SwingUtilities.invokeLater(() -> { + MainWindow ex = new MainWindow(); + int id = 0; + MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具", + "debug/debug.png", + "用于调试指定Windows工具的一个分类"); + + debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png", + "使用frida注入目标进程的脚本程序 " + + "\n作者:tzdwindows 7", ++id, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + Window owner = SwingUtilities.windowForComponent((Component) e.getSource()); + FridaWindow fridaWindow = new FridaWindow(owner); + fridaWindow.setVisible(true); + } + })); + + // 在后面注册你自己的项或是添加你自己的分类 + // .... + + ex.addToolCategory(debugCategory); + ex.initUI(); + ex.setVisible(true); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/events/CategoryRenderingEvent.java b/src/main/java/com/axis/innovators/box/events/CategoryRenderingEvent.java new file mode 100644 index 0000000..52d2745 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/CategoryRenderingEvent.java @@ -0,0 +1,138 @@ +package com.axis.innovators.box.events; + +import com.axis.innovators.box.gui.MainWindow; + +import java.awt.*; + +/** + * 分类栏的渲染事件 + * @author tzdwindows 7 + */ +public class CategoryRenderingEvent { + private final MainWindow.CustomTabbedPaneUI ui; + private final Graphics graphics; + private final int tabPlacement; + private final int tabIndex; + private final int x; + private final int y; + private final int width; + private final int height; + private final boolean isSelected; + private boolean isEnd = false; + + public CategoryRenderingEvent(MainWindow.CustomTabbedPaneUI ui, Graphics graphics, int tabPlacement, int tabIndex, int x, int y, int width, int height, boolean isSelected) { + this.ui = ui; + this.graphics = graphics; + this.tabPlacement = tabPlacement; + this.tabIndex = tabIndex; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.isSelected = isSelected; + } + + public MainWindow.CustomTabbedPaneUI getUi() { + return ui; + } + + public Graphics getGraphics() { + return graphics; + } + + public int getTabPlacement() { + return tabPlacement; + } + + public int getTabIndex() { + return tabIndex; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public boolean isSelected() { + return isSelected; + } + + public boolean isEnd() { + return isEnd; + } + + public void setEnd(boolean end) { + isEnd = end; + } + + public static class paintTabBorder { + private final MainWindow.CustomTabbedPaneUI event; + private final Graphics graphics; + private final int tabPlacement; + private final int tabIndex; + private final int x; + private final int y; + private final int width; + private final int height; + private final boolean isSelected; + + public paintTabBorder(MainWindow.CustomTabbedPaneUI event, Graphics graphics, int tabPlacement, int tabIndex, int x, int y, int width, int height, boolean isSelected) { + this.event = event; + this.graphics = graphics; + this.tabPlacement = tabPlacement; + this.tabIndex = tabIndex; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.isSelected = isSelected; + } + + public MainWindow.CustomTabbedPaneUI getEvent() { + return event; + } + + public Graphics getGraphics() { + return graphics; + } + + public int getTabPlacement() { + return tabPlacement; + } + + public int getTabIndex() { + return tabIndex; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public boolean isSelected() { + return isSelected; + } + } +} diff --git a/src/main/java/com/axis/innovators/box/events/EventBus.java b/src/main/java/com/axis/innovators/box/events/EventBus.java new file mode 100644 index 0000000..92309e1 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/EventBus.java @@ -0,0 +1,150 @@ +package com.axis.innovators.box.events; + +import java.lang.reflect.Method; +import java.util.*; + +/** + * 事件总线 + * @author tzdwindows 7 + */ +public class EventBus { + private static int maxID = 0; + private final int busID; + private final Map, List> eventSubscribers = new HashMap<>(); + private final Map> targetSubscribers = new HashMap<>(); + private boolean shutdown; + + public EventBus() { + this.busID = maxID++; + } + + private static class Subscriber { + final Object target; + final Method method; + final Class eventType; + + Subscriber(Object target, Method method, Class eventType) { + this.target = target; + this.method = method; + this.eventType = eventType; + } + } + + /** + * 注册目标对象的事件监听器 + * @param target 目标对象 + */ + public void register(Object target) { + if (targetSubscribers.containsKey(target)) { + return; + } + + List subs = new ArrayList<>(); + for (Method method : getAnnotatedMethods(target)) { + SubscribeEvent annotation = method.getAnnotation(SubscribeEvent.class); + if (annotation == null) { + continue; + } + + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1) { + continue; + } + + Class eventType = paramTypes[0]; + Subscriber sub = new Subscriber(target, method, eventType); + eventSubscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(sub); + subs.add(sub); + } + + if (!subs.isEmpty()) { + targetSubscribers.put(target, subs); + } + } + + /** + * 获取目标对象中所有带有 @SubscribeEvent 注解的方法 + * @param target 目标对象 + * @return 方法集合 + */ + private Set getAnnotatedMethods(Object target) { + Set methods = new HashSet<>(); + Class clazz = target.getClass(); + while (clazz != null) { + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(SubscribeEvent.class)) { + methods.add(method); + } + } + clazz = clazz.getSuperclass(); + } + return methods; + } + + /** + * 注销目标对象的事件监听器 + * @param target 目标对象 + */ + public void unregister(Object target) { + List subs = targetSubscribers.remove(target); + if (subs == null) { + return; + } + + for (Subscriber sub : subs) { + List eventSubs = eventSubscribers.get(sub.eventType); + if (eventSubs != null) { + eventSubs.remove(sub); + if (eventSubs.isEmpty()) { + eventSubscribers.remove(sub.eventType); + } + } + } + } + + /** + * 发布事件 + * @param event 事件对象 + * @return 返回事件是否被取消的状态 + */ + public boolean post(Object event) { + if (shutdown) { + return false; + } + + boolean cancelled = false; + Class eventType = event.getClass(); + List subs = eventSubscribers.get(eventType); + if (subs == null) { + return cancelled; + } + + // 创建副本以避免并发修改异常 + List copySubs = new ArrayList<>(subs); + for (Subscriber sub : copySubs) { + try { + sub.method.setAccessible(true); + sub.method.invoke(sub.target, event); + } catch (Exception e) { + handleException(event, e); + } + } + return cancelled; + } + + /** + * 关闭事件总线,停止处理事件 + */ + public void shutdown() { + shutdown = true; + } + + /** + * 处理事件处理过程中出现的异常 + * @param event 事件 + * @param e 异常 + */ + private void handleException(Object event, Exception e) { + e.printStackTrace(); + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/events/GlobalEventBus.java b/src/main/java/com/axis/innovators/box/events/GlobalEventBus.java new file mode 100644 index 0000000..cccaa88 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/GlobalEventBus.java @@ -0,0 +1,8 @@ +package com.axis.innovators.box.events; + +/** + * @author tzdwindows 7 + */ +public class GlobalEventBus { + public static final EventBus EVENT_BUS = new EventBus(); +} diff --git a/src/main/java/com/axis/innovators/box/events/MainWindowEvents.java b/src/main/java/com/axis/innovators/box/events/MainWindowEvents.java new file mode 100644 index 0000000..5cc5d53 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/MainWindowEvents.java @@ -0,0 +1,25 @@ +package com.axis.innovators.box.events; + +import com.axis.innovators.box.gui.MainWindow; + +import javax.swing.*; +import java.awt.*; + +/** + * 主窗口事件 + * @author tzdwindows 7 + */ +public class MainWindowEvents { + + /** + * 在更新主窗口时调用 + */ + public record update(MainWindow mainWindow, Graphics g) { + } + + /** + * 在初始化主窗口时调用 + */ + public record initialize(MainWindow mainWindow, JPanel mainPanel) { + } +} diff --git a/src/main/java/com/axis/innovators/box/events/SettingsLoadEvents.java b/src/main/java/com/axis/innovators/box/events/SettingsLoadEvents.java new file mode 100644 index 0000000..8fb948b --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/SettingsLoadEvents.java @@ -0,0 +1,12 @@ +package com.axis.innovators.box.events; + +import javax.swing.*; + +/** + * 当设置被初始化加载时被调用 + * 可以在这里添加新的设置选项 + * + * @author tzdwindows 7 + */ +public record SettingsLoadEvents(JDialog dialog, JPanel content) { +} diff --git a/src/main/java/com/axis/innovators/box/events/SubscribeEvent.java b/src/main/java/com/axis/innovators/box/events/SubscribeEvent.java new file mode 100644 index 0000000..320d8ae --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/SubscribeEvent.java @@ -0,0 +1,12 @@ +package com.axis.innovators.box.events; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * 事件注解,事件订阅注解,用于标记事件监听方法 + * @author tzdwindows 7 + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface SubscribeEvent { +} diff --git a/src/main/java/com/axis/innovators/box/events/TABUIEvents.java b/src/main/java/com/axis/innovators/box/events/TABUIEvents.java new file mode 100644 index 0000000..781cfac --- /dev/null +++ b/src/main/java/com/axis/innovators/box/events/TABUIEvents.java @@ -0,0 +1,47 @@ +package com.axis.innovators.box.events; + +import javax.swing.*; +import java.awt.*; + +/** + * 选项卡Ui事件,当设置选项卡Ui属性时被调用 + * @author tzdwindows 7 + */ +public class TABUIEvents { + private final JComponent javax; + private final JPanel card; + + public TABUIEvents(JPanel card, JComponent c){ + this.javax = c; + this.card = card; + } + + public JComponent getJavax() { + return javax; + } + + public JPanel getCard() { + return card; + } + + /** + * 选项卡更新事件 + */ + public static class update { + private final JComponent javax; + private final Graphics graphics; + + public update(Graphics g, JComponent c){ + this.javax = c; + this.graphics = g; + } + + public JComponent getJavax() { + return javax; + } + + public Graphics getGraphics() { + return graphics; + } + } +} diff --git a/src/main/java/com/axis/innovators/box/gui/FridaWindow.java b/src/main/java/com/axis/innovators/box/gui/FridaWindow.java new file mode 100644 index 0000000..124bc10 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/FridaWindow.java @@ -0,0 +1,298 @@ +package com.axis.innovators.box.gui; + +import org.tzd.frida.windows.CallbackMessage; +import org.tzd.frida.windows.Frida; +import org.tzd.frida.windows.FridaRunnable; + +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.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +public class FridaWindow extends JDialog { + private JTextArea scriptArea; + private JTextArea logArea; + private JTextField pidField; + private boolean isRepetition = false; + + public FridaWindow(Window owner) { + super(owner, "Frida 注入工具", ModalityType.APPLICATION_MODAL); + initializeUI(); + } + + private void initializeUI() { + setSize(800, 600); + 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 inputPanel = createInputPanel(); + mainPanel.add(inputPanel, BorderLayout.NORTH); + + // 脚本编辑区 + JPanel scriptPanel = createScriptPanel(); + mainPanel.add(scriptPanel, BorderLayout.CENTER); + + // 日志输出 + JPanel logPanel = createLogPanel(); + mainPanel.add(logPanel, BorderLayout.SOUTH); + + setContentPane(mainPanel); + } + + private JPanel createInputPanel() { + JPanel panel = new JPanel(new GridLayout(1, 3, 10, 10)); + panel.setOpaque(false); + + pidField = new JTextField("请输入进程PID"); + 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 attachButton = new JButton("附加进程"); + //styleButton(attachButton, new Color(70, 130, 180)); + //attachButton.setEnabled(false); + + JButton browseButton = new JButton("选择进程"); + styleButton(browseButton, new Color(60, 179, 113)); + browseButton.addActionListener(this::openProcessSelectionWindow); + panel.add(pidField); + //panel.add(attachButton); + panel.add(browseButton); + + return panel; + } + + private JPanel createScriptPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setOpaque(false); + panel.setBorder(BorderFactory.createTitledBorder("脚本编辑器")); + + 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))); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5)); + buttonPanel.setOpaque(false); + + JButton injectButton = new JButton("注入脚本"); + styleButton(injectButton, new Color(220, 20, 60)); + injectButton.addActionListener(this::handleInject); + + buttonPanel.add(injectButton); + + panel.add(scrollPane, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + return panel; + } + + private JPanel createLogPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setPreferredSize(new Dimension(0, 150)); + panel.setBorder(BorderFactory.createTitledBorder("日志输出")); + panel.setOpaque(false); + + logArea = new JTextArea(); + logArea.setEditable(false); + logArea.setFont(new Font("微软雅黑", Font.PLAIN, 12)); + JScrollPane scrollPane = new JScrollPane(logArea); + scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0))); + + panel.add(scrollPane); + return panel; + } + + private void styleButton(JButton button, Color bgColor) { + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setFocusPainted(false); + button.setBackground(bgColor); + button.setForeground(Color.black); + button.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20)); + } + + private void handleInject(ActionEvent e) { + try { + long pid = Long.parseLong(pidField.getText()); + String script = scriptArea.getText(); + + Frida frida = new Frida(script, pid); + if (!isRepetition) { + frida.run(() -> { + // 执行注入操作 + }).execute(frida1 -> frida1.addCallbackMessage(message -> + SwingUtilities.invokeLater(() -> + logArea.append("[LOG] " + message + "\n") + ) + )).start(); + isRepetition = true; + } else { + frida.run(() -> {}).execute(frida12 -> {}).start(); + } + + } catch (NumberFormatException ex) { + JOptionPane.showMessageDialog(this, "无效的进程ID", "错误", JOptionPane.ERROR_MESSAGE); + } + } + + private void openProcessSelectionWindow(ActionEvent e) { + // 打开一个窗口,显示所有进程的ID、名称和图标 + ProcessSelectionWindow selectionWindow = new ProcessSelectionWindow(this); + selectionWindow.setVisible(true); + } + + // 选择进程的子窗口 + private static class ProcessSelectionWindow extends JDialog { + private List processList; // 存储所有进程信息 + private JTable table; + + public ProcessSelectionWindow(Window owner) { + super(owner, "选择进程", ModalityType.APPLICATION_MODAL); + setSize(600, 400); + setLocationRelativeTo(owner); + initializeUI(); + } + + private void initializeUI() { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + // 获取所有进程信息 + processList = getProcesses(); + + // 创建搜索框 + JPanel searchPanel = new JPanel(); + searchPanel.setLayout(new BorderLayout()); + JTextField searchField = new JTextField(); + searchField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + filterProcesses(searchField.getText()); + } + + @Override + public void removeUpdate(DocumentEvent e) { + filterProcesses(searchField.getText()); + } + + @Override + public void changedUpdate(DocumentEvent e) { + filterProcesses(searchField.getText()); + } + }); + searchPanel.add(new JLabel("搜索进程: "), BorderLayout.WEST); + searchPanel.add(searchField, BorderLayout.CENTER); + panel.add(searchPanel, BorderLayout.NORTH); + + // 显示进程信息的表格 + String[] columns = {"进程名称", "进程ID"}; + Object[][] data = getTableData(processList); + table = new JTable(data, columns); + JScrollPane scrollPane = new JScrollPane(table); + panel.add(scrollPane, BorderLayout.CENTER); + + JButton selectButton = new JButton("选择"); + selectButton.addActionListener(e -> { + int selectedRow = table.getSelectedRow(); + if (selectedRow >= 0) { + long selectedPid = (Long) table.getValueAt(selectedRow, 1); // 注意这里获取的是PID列 + ((FridaWindow) getOwner()).pidField.setText(String.valueOf(selectedPid)); + dispose(); + } + }); + panel.add(selectButton, BorderLayout.SOUTH); + + setContentPane(panel); + } + + private void filterProcesses(String query) { + List filteredList = new ArrayList<>(); + for (ProcessInfo process : processList) { + if (process.getName().toLowerCase().contains(query.toLowerCase())) { + filteredList.add(process); + } + } + // 更新表格数据 + Object[][] filteredData = getTableData(filteredList); + table.setModel(new DefaultTableModel(filteredData, new String[]{"进程名称", "进程ID"})); + } + + private Object[][] getTableData(List processes) { + Object[][] data = new Object[processes.size()][2]; + for (int i = 0; i < processes.size(); i++) { + ProcessInfo process = processes.get(i); + data[i] = new Object[]{process.getName(), process.getPid()}; + } + return data; + } + + private List getProcesses() { + List processList = new ArrayList<>(); + try { + // 执行cmd命令获取所有进程信息 + Process process = Runtime.getRuntime().exec("tasklist /fo csv /nh"); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + + while ((line = reader.readLine()) != null) { + String[] columns = line.split(","); + if (columns.length > 1) { + long pid = Long.parseLong(columns[1].replaceAll("\"", "").trim()); + String processName = columns[0].replaceAll("\"", "").trim(); + processList.add(new ProcessInfo(pid, processName)); + } + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return processList; + } + } + + // 存储进程信息的类 + private static class ProcessInfo { + private long pid; + private String name; + + public ProcessInfo(long pid, String name) { + this.pid = pid; + this.name = name; + } + + public long getPid() { + return pid; + } + + public String getName() { + return name; + } + } +} diff --git a/src/main/java/com/axis/innovators/box/gui/LoadIcon.java b/src/main/java/com/axis/innovators/box/gui/LoadIcon.java new file mode 100644 index 0000000..afa293e --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/LoadIcon.java @@ -0,0 +1,36 @@ +package com.axis.innovators.box.gui; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.net.URL; + +/** + * 负责加载图片 + * @author tzdwindows 7 + */ +public class LoadIcon { + private static final String ICON_PATH = "/icons/"; + public static ImageIcon loadIcon(String filename, int size) { + try { + String fullPath = ICON_PATH + filename; + URL imgUrl = LoadIcon.class.getResource(fullPath); + if (imgUrl == null) { + return createPlaceholderIcon(size); + } + Image image = new ImageIcon(imgUrl).getImage(); + return new ImageIcon(image.getScaledInstance(size, size, Image.SCALE_SMOOTH)); + } catch (Exception e) { + return createPlaceholderIcon(size); + } + } + + private static ImageIcon createPlaceholderIcon(int size) { + BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = img.createGraphics(); + g2d.setColor(Color.LIGHT_GRAY); + g2d.fillRect(0, 0, size, size); + g2d.dispose(); + return new ImageIcon(img); + } +} diff --git a/src/main/java/com/axis/innovators/box/gui/MainWindow.java b/src/main/java/com/axis/innovators/box/gui/MainWindow.java new file mode 100644 index 0000000..e750548 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/MainWindow.java @@ -0,0 +1,716 @@ +package com.axis.innovators.box.gui; + +import com.axis.innovators.box.events.*; + +import javax.swing.*; +import javax.swing.Timer; +import javax.swing.plaf.PanelUI; +import javax.swing.plaf.basic.BasicScrollBarUI; +import javax.swing.plaf.basic.BasicTabbedPaneUI; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; +import java.util.*; +import java.util.List; + +/** + * 显示窗口(显示的主窗口) + * @author tzdwindows 7 + */ +public class MainWindow extends JFrame { + private final Map cardScales = new HashMap<>(); + private final Map cardElevations = new HashMap<>(); + // 选项卡颜色 + private final Color CARD_COLOR = Color.WHITE; + private final List categories = new ArrayList<>(); + // 是否启用背景图片 + private final boolean isBackground = true; + // 启动背景图片后是否启动背景模糊 + private final boolean isBlur = true; + + public MainWindow() { + setIconImage(LoadIcon.loadIcon("logo.png", 32).getImage()); + } + + /** + * 添加工具分类 + * @param category 工具分类 + */ + public void addToolCategory(ToolCategory category){ + categories.add(category); + } + + public void initUI() { + setTitle("轴创工具箱 v1.0"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(1200, 800); + setLocationRelativeTo(null); + + 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()); + + if (isBackground) { + try { + ImageIcon backgroundImage = LoadIcon.loadIcon("start_page.png", 500); + if (isBlur) { + BufferedImage bufferedImage = toBufferedImage(backgroundImage.getImage()); + BufferedImage blurredImage = applyGaussianBlur(bufferedImage, 5); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f)); + int x = (getWidth() - blurredImage.getWidth()) / 2; + int y = (getHeight() - blurredImage.getHeight()) / 2; + g2d.drawImage(blurredImage, x, y, this); + } else { + int x = (getWidth() - backgroundImage.getIconWidth()) / 2; + int y = (getHeight() - backgroundImage.getIconHeight()) / 2; + g2d.drawImage(backgroundImage.getImage(), x, y, this); + } + + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }; + + mainPanel.setLayout(new BorderLayout(20, 20)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); + + mainPanel.add(createHeader(), BorderLayout.NORTH); + mainPanel.add(createCategoryTabs(), BorderLayout.CENTER); + + GlobalEventBus.EVENT_BUS.post(new MainWindowEvents.initialize(this, mainPanel)); + + add(mainPanel); + } + + private BufferedImage toBufferedImage(Image img) { + if (img instanceof BufferedImage) { + return (BufferedImage) img; + } + BufferedImage bimage = new BufferedImage( + img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); + Graphics2D bGr = bimage.createGraphics(); + bGr.drawImage(img, 0, 0, null); + bGr.dispose(); + return bimage; + } + + /** + * 对图像应用动感模糊效果。 + * + * @param srcImage 需要应用动感模糊的源图像。 + * 这个参数应该是一个 `BufferedImage` 类型的对象。 + * @param radius 动感模糊的半径,决定模糊效果的范围。 + * 半径越大,模糊范围越广,模糊效果越强。 + * @param angle 动感模糊的角度,决定模糊效果的方向。 + * 角度单位为度,表示从正Y轴顺时针的旋转角度。 + * 比如: + * - `0` 表示水平方向的模糊。 + * - `90` 表示垂直方向的模糊。 + * - 其他角度则表示不同方向的模糊。 + * @return 返回应用了动感模糊效果的 `BufferedImage` 图像。 + * 原始图像不会被修改,返回的是处理过的新图像。 + */ + public BufferedImage applyMotionBlur(BufferedImage srcImage, int radius, int angle) { + double radian = Math.toRadians(angle); + + float[] matrix = new float[radius * radius]; + float sum = 0.0f; + int index = 0; + + for (int y = -radius / 2; y <= radius / 2; y++) { + for (int x = -radius / 2; x <= radius / 2; x++) { + float weight = (float) (Math.cos(radian) * x + Math.sin(radian) * y); + if (Math.abs(weight) < 1) { + matrix[index++] = 1.0f; + sum += 1.0f; + } else { + matrix[index++] = 0.0f; + } + } + } + + return getBufferedImage(srcImage, radius, matrix, sum); + } + + private BufferedImage getBufferedImage(BufferedImage srcImage, int radius, float[] matrix, float sum) { + for (int i = 0; i < matrix.length; i++) { + matrix[i] /= sum; + } + Kernel kernel = new Kernel(radius, radius, matrix); + ConvolveOp convolve = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); + return convolve.filter(srcImage, null); + } + + /** + * 应用高斯模糊效果到给定的图像。 + * + * @param srcImage 输入的原始图像,类型为 `BufferedImage`。 + * @param radius 高斯模糊的半径,表示模糊的强度。半径越大,模糊效果越强。 + * @return 返回模糊处理后的图像,类型为 `BufferedImage`。 + */ + public BufferedImage applyGaussianBlur(BufferedImage srcImage, int radius) { + if (radius > 100) { + radius = 100; + } + + float[] matrix = new float[radius * radius]; + float sigma = radius / 2f; + float sum = 0.0f; + int index = 0; + + for (int y = -radius / 2; y <= radius / 2; y++) { + for (int x = -radius / 2; x <= radius / 2; x++) { + float weight = (float) Math.exp(-(x * x + y * y) / (2 * sigma * sigma)); + matrix[index] = weight; + index++; + sum += weight; + } + } + + return getBufferedImage(srcImage, radius, matrix, sum); + } + + private JComponent createCategoryTabs() { + JTabbedPane tabbedPane = new JTabbedPane(); + + tabbedPane.setOpaque(false); + tabbedPane.setBackground(new Color(0, 0, 0, 0)); + tabbedPane.setBorder(null); + + tabbedPane.setUI(new CustomTabbedPaneUI()); + + for (ToolCategory category : categories) { + JPanel toolsPanel = createToolsPanel(category); + toolsPanel.setOpaque(false); + toolsPanel.setBorder(null); + + JScrollPane scrollPane = new JScrollPane(toolsPanel); + scrollPane.setBorder(null); + scrollPane.setOpaque(false); + scrollPane.getViewport().setOpaque(false); + JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar(); + verticalScrollBar.setUI(new CustomScrollBarUI()); + verticalScrollBar.setPreferredSize(new Dimension(10, 100)); + + tabbedPane.addTab( + category.getName(), + LoadIcon.loadIcon(category.getIcon(), 24), + scrollPane + ); + } + + return tabbedPane; + } + + private JPanel createToolsPanel(ToolCategory category) { + JPanel panel = new JPanel(new GridLayout(0, 3, 20, 20)); + for (ToolItem tool : category.getTools()) { + panel.add(createToolCard(tool)); + } + panel.setOpaque(false); + panel.setBorder(null); + return panel; + } + + private JPanel createHeader() { + JPanel header = new JPanel(new BorderLayout()); + header.setOpaque(false); + header.setBorder(BorderFactory.createEmptyBorder(15, 30, 15, 30)); + + // 创建标题 + JLabel title = new JLabel("轴创工具箱"); + title.setFont(new Font("微软雅黑", Font.BOLD, 28)); + title.setForeground(new Color(44, 62, 80)); + + //JLabel iconLabel = new JLabel(LoadIcon.loadIcon("logo.png", 28)); + //iconLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + + JButton settings = new JButton(LoadIcon.loadIcon("settings.png", 32)); + settings.setContentAreaFilled(false); + settings.addActionListener(e -> showSettings()); + + JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + titlePanel.setOpaque(false); + //titlePanel.add(iconLabel); + titlePanel.add(title); + + header.add(titlePanel, BorderLayout.WEST); + header.add(settings, BorderLayout.EAST); + + return header; + } + + + @Override + public void update(Graphics g) { + GlobalEventBus.EVENT_BUS.post(new MainWindowEvents.update(this, g)); + super.update(g); + } + + private void showSettings() { + JDialog dialog = new JDialog(this, "系统设置", true); + dialog.setSize(600, 400); + dialog.setLocationRelativeTo(this); + + JPanel content = new JPanel(new BorderLayout()); + content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + //JLabel placeholder = new JLabel("设置功能开发中...", SwingConstants.CENTER); + //placeholder.setFont(new Font("微软雅黑", Font.PLAIN, 24)); + //placeholder.setForeground(new Color(127, 140, 153)); + //content.add(placeholder, BorderLayout.CENTER); + + GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, content)); + + dialog.add(content); + dialog.setVisible(true); + } + + + + private JPanel createToolCard(ToolItem tool) { + JPanel card = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g.create(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 绘制阴影 + int elevation = cardElevations.getOrDefault(this, 2); + for (int i = 0; i < elevation; i++) { + g2d.setColor(new Color(0, 0, 0, 20 - i * 2)); + g2d.fillRoundRect(i, i, getWidth() - i * 2, getHeight() - i * 2, 15, 15); + } + + float scale = cardScales.getOrDefault(this, 1.0f); + int offset = (int) ((scale - 1) * getWidth() / 2); + g2d.translate(-offset, -offset); + g2d.scale(scale, scale); + + super.paintComponent(g2d); + g2d.dispose(); + } + }; + + card.setLayout(new BorderLayout(15, 15)); + card.setBackground(CARD_COLOR); + card.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(225, 229, 234), 1), + BorderFactory.createEmptyBorder(20, 20, 20, 20) + )); + card.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + JLabel iconLabel = new JLabel(LoadIcon.loadIcon(tool.icon(), 64)); + iconLabel.setHorizontalAlignment(SwingConstants.CENTER); + + // 文字面板 + JPanel textPanel = new JPanel(); + textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS)); + textPanel.setOpaque(false); + + JLabel titleLabel = new JLabel(tool.title()); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18)); + titleLabel.setForeground(new Color(44, 62, 80)); + titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + JTextArea descArea = new JTextArea(tool.description()); + descArea.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + descArea.setForeground(new Color(127, 140, 153)); + descArea.setLineWrap(true); + descArea.setWrapStyleWord(true); + descArea.setEditable(false); + descArea.setOpaque(false); + descArea.setAlignmentX(Component.CENTER_ALIGNMENT); + + card.setToolTipText(createToolTipHTML(tool)); + + textPanel.add(titleLabel); + textPanel.add(Box.createVerticalStrut(10)); + textPanel.add(descArea); + + card.add(iconLabel, BorderLayout.NORTH); + card.add(textPanel, BorderLayout.CENTER); + + // 鼠标悬停动画 + card.addMouseListener(new CardMouseAdapter(card, tool)); + card.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(() -> { + + }); + } + }); + card.setUI(new PanelUI() { + @Override + public void installUI(JComponent c) { + GlobalEventBus.EVENT_BUS.post(new TABUIEvents(card, c)); + super.installUI(c); + } + + @Override + public void update(Graphics g, JComponent c) { + GlobalEventBus.EVENT_BUS.post(new TABUIEvents.update(g, c)); + super.update(g, c); + } + }); + return card; + } + + private BufferedImage captureWindowImage(Window window) { + try { + Rectangle bounds = window.getBounds(); + BufferedImage capture = new Robot().createScreenCapture(bounds); + return capture.getSubimage(0, 0, window.getWidth(), window.getHeight()); + } catch (AWTException e) { + e.printStackTrace(); + return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + } + } + + private static JPanel getjPanel(BufferedImage[] blurredImage, Window window) { + JPanel blurPanel = new JPanel() { + private float currentOpacity = 1.0f; + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + if (blurredImage[0] != null) { + Graphics2D g2d = (Graphics2D) g.create(); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, currentOpacity)); + g2d.drawImage(blurredImage[0], 0, 0, null); + g2d.dispose(); + } + } + }; + + // 面板属性设置 + blurPanel.setBounds(0, 0, window.getWidth(), window.getHeight()); + blurPanel.setOpaque(false); + return blurPanel; + } + + + private String createToolTipHTML(ToolItem tool) { + return "" + + "

" + tool.getName() + "

" + + "

" + tool.description() + "

" + + ""; + } + + private void animateHover(JComponent component, float targetScale, int targetElevation) { + final int ANIMATION_DURATION = 200; + final float startScale = cardScales.getOrDefault(component, 1.0f); + final int startElevation = cardElevations.getOrDefault(component, 2); + + new Timer(10, new AbstractAction() { + long startTime = -1; + + @Override + public void actionPerformed(ActionEvent e) { + if (startTime < 0) { + startTime = System.currentTimeMillis(); + } + long elapsed = System.currentTimeMillis() - startTime; + float progress = Math.min(1.0f, elapsed / (float) ANIMATION_DURATION); + + // 使用缓动函数实现平滑动画 + float easedProgress = (float) (1 - Math.pow(1 - progress, 3)); + + float currentScale = startScale + (targetScale - startScale) * easedProgress; + int currentElevation = (int) (startElevation + (targetElevation - startElevation) * easedProgress); + + cardScales.put(component, currentScale); + cardElevations.put(component, currentElevation); + + component.repaint(); + + if (progress >= 1.0f) { + ((Timer) e.getSource()).stop(); + } + } + }).start(); + } + + // 工具类别内部类 + public static class ToolCategory { + private final String name; + private final String icon; + private final String description; + private final List tools = new ArrayList<>(); + + /** + * 一个大的分类项类 + * @param name 分类项的显示名称 + * @param icon 分类项的图标(resources的路径) + * @param description 分类项的描述 + */ + public ToolCategory(String name, String icon, String description) { + this.name = name; + this.icon = icon; + this.description = description; + } + + /** + * 注册工具的方法 + * @param tool 工具项 + */ + public void addTool(ToolItem tool) { + tools.add(tool); + } + + public String getDescription() { + return description; + } + + public String getIcon() { + return icon; + } + + public String getName() { + return name; + } + + public List getTools() { + return tools; + } + } + + // 工具项数据类 + + /** + * 工具注册类 + * @param title 工具的标题(显示名称) + * @param icon 工具的图标(resources的路径) + * @param description 工具的描述 + * @param id 工具的id(请不要重复注册相同id的工具) + * @param action 工具的点击事件 + */ + public record ToolItem(String title, String icon, String description, int id, Action action) { + private static JPanel getjPanel() { + JPanel content = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 渐变背景 + GradientPaint gp = new GradientPaint( + 0, 0, new Color(245, 247, 250), + getWidth(), getHeight(), new Color(255, 255, 255) + ); + g2d.setPaint(gp); + g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20); + + // 边框阴影 + g2d.setColor(new Color(0, 0, 0, 20)); + g2d.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 20, 20); + } + }; + content.setLayout(new BorderLayout(20, 20)); + content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + return content; + } + + public String getName() { + return title; + } + } + + // 分类栏面卡 + public static class CustomTabbedPaneUI extends BasicTabbedPaneUI { + private static final Color SELECTED_COLOR = new Color(183, 202, 221); + private static final Color UNSELECTED_COLOR = new Color(125, 174, 237); + + + @Override + protected void paintTabBackground(Graphics g, int tabPlacement, + int tabIndex, int x, int y, int w, int h, + boolean isSelected) { + CategoryRenderingEvent event = new CategoryRenderingEvent(this, g, tabPlacement, tabIndex, x, y, w, h, isSelected); + GlobalEventBus.EVENT_BUS.post(event); + if (event.isEnd()){ + return; + } + Graphics2D g2d = (Graphics2D) g.create(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + if (isSelected) { + g2d.setColor(SELECTED_COLOR); + } else { + g2d.setColor(UNSELECTED_COLOR); + } + + g2d.fillRoundRect(x + 2, y + 2, w - 4, h - 4, 10, 10); + g2d.dispose(); + } + + @Override + protected void paintTabBorder(Graphics g, int tabPlacement, + int tabIndex, int x, int y, int w, int h, + boolean isSelected) { + GlobalEventBus.EVENT_BUS.post(new CategoryRenderingEvent.paintTabBorder(this, g, + tabPlacement, + tabIndex, x, y, w, h, isSelected)); + } + } + + private class CardMouseAdapter extends MouseAdapter { + private final JPanel card; + private final ToolItem tool; + private Timer pressTimer; + private Timer releaseTimer; + + public CardMouseAdapter(JPanel card, ToolItem tool) { + this.card = card; + this.tool = tool; + } + + @Override + public void mousePressed(MouseEvent e) { + startPressAnimation(); + } + + @Override + public void mouseReleased(MouseEvent e) { + startReleaseAnimation(() -> tool.action().actionPerformed( + new ActionEvent(card, ActionEvent.ACTION_PERFORMED, "") + )); + } + + @Override + public void mouseExited(MouseEvent e) { + if (pressTimer != null && pressTimer.isRunning()) { + startReleaseAnimation(null); + } + } + + private void startPressAnimation() { + if (pressTimer != null && pressTimer.isRunning()) { + return; + } + + pressTimer = new Timer(10, new AbstractAction() { + private final long startTime = System.currentTimeMillis(); + + @Override + public void actionPerformed(ActionEvent e) { + float progress = Math.min(1.0f, + (System.currentTimeMillis() - startTime) / 150f); + + // 使用二次缓动函数 + float scale = 1.0f - 0.1f * (float) Math.pow(progress, 0.5); + cardScales.put(card, scale); + card.repaint(); + + if (progress >= 1.0f) { + ((Timer) e.getSource()).stop(); + } + } + }); + pressTimer.start(); + } + + private void startReleaseAnimation(Runnable callback) { + if (pressTimer != null) { + pressTimer.stop(); + } + if (releaseTimer != null && releaseTimer.isRunning()) { + return; + } + + final float startScale = cardScales.getOrDefault(card, 1.0f); + releaseTimer = new Timer(10, new AbstractAction() { + private final long startTime = System.currentTimeMillis(); + + @Override + public void actionPerformed(ActionEvent e) { + float progress = Math.min(1.0f, + (System.currentTimeMillis() - startTime) / 200f); + + // 使用弹性缓动函数 + float scale = startScale + + (1.0f - startScale) * (float) (1 - Math.pow(1 - progress, 3)); + cardScales.put(card, scale); + card.repaint(); + + if (progress >= 1.0f) { + ((Timer) e.getSource()).stop(); + if (callback != null) { + callback.run(); + } + } + } + }); + releaseTimer.start(); + } + } + + public static class CustomScrollBarUI extends BasicScrollBarUI { + @Override + protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 设置轨道背景颜色 + Color trackColor = new Color(240, 240, 240); + g2d.setColor(trackColor); + g2d.fillRoundRect( + trackBounds.x, + trackBounds.y, + trackBounds.width - 1, + trackBounds.height - 1, + 5, 5 // 圆角半径 + ); + } + + @Override + protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + Color thumbColor = new Color(180, 180, 180); + g2d.setColor(thumbColor); + g2d.fillRoundRect( + thumbBounds.x, + thumbBounds.y, + thumbBounds.width - 1, + thumbBounds.height - 1, + 5, 5 + ); + + Color borderColor = new Color(160, 160, 160); + g2d.setColor(borderColor); + g2d.drawRoundRect( + thumbBounds.x, + thumbBounds.y, + thumbBounds.width - 1, + thumbBounds.height - 1, + 5, 5 + ); + } + + @Override + protected void paintDecreaseHighlight(Graphics g) {} + + @Override + protected void paintIncreaseHighlight(Graphics g) {} + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/tools/FolderCreator.java b/src/main/java/com/axis/innovators/box/tools/FolderCreator.java new file mode 100644 index 0000000..fed0a10 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/tools/FolderCreator.java @@ -0,0 +1,38 @@ +package com.axis.innovators.box.tools; + +import java.io.File; + +/** + * 在当前jar下创建文件夹 + * @author tzdwindows 7 + */ +public class FolderCreator { + + public static final String LIBRARY_NAME = "library"; + + public static String getLibraryFolder() { + String folder = createFolder(LIBRARY_NAME); + if (folder == null) { + System.out.println("Library folder creation failure"); + return null; + } + return folder; + } + + /** + * 创建文件夹 + * @param folderName folder name + * @return folder path + */ + private static String createFolder(String folderName) { + String jarDir = System.getProperty("user.dir"); + File folder = new File(jarDir, folderName); + if (!folder.exists()) { + if (!folder.mkdir()) { + System.out.println("Folder creation failure"); + return null; + } + } + return folder.getAbsolutePath(); + } +} diff --git a/src/main/java/com/axis/innovators/box/tools/LibraryLoad.java b/src/main/java/com/axis/innovators/box/tools/LibraryLoad.java new file mode 100644 index 0000000..ce3aab8 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/tools/LibraryLoad.java @@ -0,0 +1,40 @@ +package com.axis.innovators.box.tools; + +/** + * 在程序链接库文件中加载指定链接库 + * @author tzdwindows 7 + */ +public class LibraryLoad { + public static final String LIBRARY_NAME = FolderCreator.getLibraryFolder() + "\\"; + + /** + * 加载链接库 + * @param libraryName 链接库名称 + */ + public static void loadLibrary(String libraryName) { + String libraryWithExtension = addLibraryExtension(libraryName); + System.load(LIBRARY_NAME + libraryWithExtension); + } + + /** + * 判断并添加正确的库文件后缀 + * @param libraryName 链接库名称 + * @return 带有后缀名的库文件名称 + */ + private static String addLibraryExtension(String libraryName) { + String os = System.getProperty("os.name").toLowerCase(); + + if (libraryName.toLowerCase().endsWith(".dll") + || libraryName.toLowerCase().endsWith(".so")) { + return libraryName; + } + + if (os.contains("win")) { + return libraryName + ".dll"; + } else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) { + return libraryName + ".so"; + } else { + return libraryName; + } + } +} diff --git a/src/main/java/org/tzd/frida/Main.java b/src/main/java/org/tzd/frida/Main.java new file mode 100644 index 0000000..1c53626 --- /dev/null +++ b/src/main/java/org/tzd/frida/Main.java @@ -0,0 +1,87 @@ +package org.tzd.frida; + +import org.tzd.frida.windows.CallbackMessage; +import org.tzd.frida.windows.Frida; +import org.tzd.frida.windows.FridaRunnable; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * Main 类用于启动和执行 Frida 注入操作。 + * + *

此类主要包含一个入口方法 main,用于加载必要的本地库并初始化 Frida 对象。

+ *

它会通过调用 getProcessPid 方法获取指定进程的 PID,并将 JavaScript 代码注入到目标进程中。

+ * + * @author tzdwindows 7 + */ +public class Main { + + /** + * 程序入口方法。 + * + *

此方法加载本地 DLL 库并创建一个 Frida 实例。接着通过 runexecute 方法执行指定的任务。

+ *

执行期间,会将回调函数添加到 Frida 实例中,接收并打印消息。

+ * + * @param args 命令行参数 + */ + public static void main(String[] args) { + // 加载本地 DLL 文件 + System.load("C:\\Users\\Administrator\\source\\repos\\FridaNative\\x64\\Release\\FridaNative.dll"); + + // 创建 Frida 实例并传入 JavaScript 代码及进程 PID + Frida frida = new Frida("console.log('Hello, Frida!');", getProcessPid("java.exe")); + + // 执行 Frida 任务,注入代码并注册回调函数 + frida.run(new Runnable() { + @Override + public void run() { + // 在此处添加要执行的代码 + } + }).execute(new FridaRunnable() { + @Override + public void run(Frida frida) { + // 注册回调消息,接收到消息时打印 + frida.addCallbackMessage(new CallbackMessage() { + @Override + public void onMessage(String message) { + System.out.println(message); // 打印收到的消息 + } + }); + } + }).start(); // 启动线程 + } + + /** + * 获取指定进程的 PID。 + * + *

此方法通过运行 Windows 命令 tasklist 获取系统中所有正在运行的进程,并查找指定进程名。

+ *

一旦找到匹配的进程,它将提取进程的 PID 并返回。

+ * + * @param processName 要查找的进程名称。 + * @return 目标进程的 PID,如果未找到则返回 -1。 + */ + public static long getProcessPid(String processName) { + long pid = -1; + try { + ProcessBuilder builder = new ProcessBuilder("tasklist"); + builder.redirectErrorStream(true); + Process process = builder.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + + while ((line = reader.readLine()) != null) { + if (line.contains(processName)) { + String[] parts = line.split("\\s+"); + pid = Long.parseLong(parts[1]); + break; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + return pid; + } +} diff --git a/src/main/java/org/tzd/frida/windows/CallbackMessage.java b/src/main/java/org/tzd/frida/windows/CallbackMessage.java new file mode 100644 index 0000000..b10dd4b --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/CallbackMessage.java @@ -0,0 +1,12 @@ +package org.tzd.frida.windows; + +/** + * @author tzdwindows 7 + */ +public interface CallbackMessage { + /** + * 回调消息 + * @param message 消息 + */ + void onMessage(String message); +} diff --git a/src/main/java/org/tzd/frida/windows/Frida.java b/src/main/java/org/tzd/frida/windows/Frida.java new file mode 100644 index 0000000..2fccbfe --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/Frida.java @@ -0,0 +1,126 @@ +package org.tzd.frida.windows; + +/** + * @author tzdwindows 7 + */ +public class Frida { + private final String jsCode; + private FridaThread thread; + boolean isRunning; + long pid; + + /** + * 构造一个新的 Frida 实例。 + * + * @param jsCode 需要注入的 JavaScript 代码。 + * @param pid 目标进程的进程ID。 + * + *

此构造函数用于初始化一个新的 Frida 对象,指定要注入的 JavaScript 代码及目标进程ID。

+ */ + public Frida(String jsCode, long pid) { + this.jsCode = jsCode; + this.pid = pid; + isRunning = false; + } + + /** + * 启动并返回一个新的线程,用于执行JavaScript注入。 + * + * @return 返回一个线程管理类 FridaThread,方便对线程进行统一管理。 + * + *

该方法的工作流程如下:

+ *
    + * new FridaThread(() -> { ... }): 创建一个新的线程,该线程执行 JavaScript 注入操作。线程内部会设置 isRunning = true 并调用 Frida0.injection(jsCode, this) 来注入 JavaScript 代码。 + *
+ * + *

示例用法:

+ *
+     * FridaThread thread = frida.run();
+     * thread.setStart(true)
+     * thread.start();  // 启动线程
+     * 
+ */ + public FridaThread run() { + thread = new FridaThread(() -> { + isRunning = true; + Frida0.injection(jsCode,this); + },this); + return thread; + } + + /** + * 启动并返回一个新的线程,用于执行JavaScript注入。 + * + * @param callback 在循环中的回调函数 + * @return 一个线程管理类,方便对线程进行统一管理 + * + *

该方法的工作流程如下:

+ *
    + *
  1. run(Runnable... callback): 首先创建一个新的 FridaThread,并传入一个 Runnable 任务,任务会在新线程中执行。任务内会调用 Frida0.injection(jsCode, this, callback),注入并执行 JavaScript 代码。
  2. + *
  3. execute(FridaRunnable runnable): execute 方法允许你定义在 FridaThread 中执行的额外任务。FridaRunnable 接口的 run 方法会被调用,允许你与 Frida 对象交互,添加回调消息或其他操作。
  4. + *
  5. start(): 启动线程,执行上述所有任务。
  6. + *
+ * + *

示例用法:

+ *
+     * frida.run(new Runnable() {
+     *     @Override
+     *     public void run() {
+     *         // 在此处添加需要执行的代码
+     *     }
+     * }).execute(new FridaRunnable() {
+     *     @Override
+     *     public void run(Frida frida) {
+     *         // 注册回调消息,接收信息
+     *         frida.addCallbackMessage(new CallbackMessage() {
+     *             @Override
+     *             public void onMessage(String message) {
+     *                 System.out.println(message); // 打印收到的消息
+     *             }
+     *         });
+     *     }
+     * }).start(); // 启动线程
+     * 
+ * + *

在这个示例中,run() 方法首先创建并配置一个新的线程,execute() 方法为线程添加了一个回调函数,接收到消息时会触发该回调函数。最后,调用 start() 启动线程的执行。

+ *

当然你如果非要在FridaThread中直接使用start,而不经过execute的话我们会抛出异常 throw new RuntimeException("Basic information is not configured");

+ */ + public FridaThread run(Runnable... callback) { + thread = new FridaThread(() -> { + isRunning = true; + Frida0.injection(jsCode, this, callback); + }, this); + return thread; + } + + /** + * 将回调消息添加到消息列表中。 + * + * @param callbackMessage 要添加的回调消息对象。 + * + *

此方法会将传入的 callbackMessage 对象添加到 Frida0.CALLBACK_MESSAGE_LIST 中。

+ */ + public void addCallbackMessage(CallbackMessage callbackMessage) { + Frida0.CALLBACK_MESSAGE_LIST.add(callbackMessage); + } + + /** + * 从消息列表中移除指定的回调消息。 + * + * @param callbackMessage 要移除的回调消息对象。 + * + *

此方法会将传入的 callbackMessage 对象从 Frida0.CALLBACK_MESSAGE_LIST 中移除。

+ */ + public void clearCallbackMessage(CallbackMessage callbackMessage) { + Frida0.CALLBACK_MESSAGE_LIST.remove(callbackMessage); + } + + /** + * 清空所有回调消息。 + * + *

此方法会清空 Frida0.CALLBACK_MESSAGE_LIST 中的所有回调消息。

+ */ + public void clearCallbackMessage() { + Frida0.CALLBACK_MESSAGE_LIST.clear(); + } +} diff --git a/src/main/java/org/tzd/frida/windows/Frida0.java b/src/main/java/org/tzd/frida/windows/Frida0.java new file mode 100644 index 0000000..cd4b551 --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/Frida0.java @@ -0,0 +1,85 @@ +package org.tzd.frida.windows; + +import java.util.ArrayList; +import java.util.List; + +/** + * Frida0 类用于处理 Frida 注入相关的操作和消息传递。 + * + *

此类包括注入 JavaScript 代码到目标进程、消息的发送与接收处理等功能。

+ *

通过该类,用户可以与 Frida 注入的进程进行交互,并通过回调函数获取日志或错误信息。

+ * + * @author tzdwindows 7 + */ +public class Frida0 { + + // 存储回调消息的列表 + static final List CALLBACK_MESSAGE_LIST = new ArrayList<>(); + + /** + * 注入 JavaScript 代码到指定的 Frida 实例,并执行回调任务。 + * + *

该方法调用本地代码注入 JavaScript,并执行传入的回调函数。在 Frida 进程运行期间,回调函数将不断被执行。

+ *

方法会在 Frida 进程完成后释放资源,并设置 Frida 的运行状态为 `false`。

+ * + * @param jsCode 注入的 JavaScript 代码。 + * @param frida 当前的 Frida 实例,包含进程 PID 等信息。 + * @param callback 运行中的回调任务列表,多个任务可以同时执行。 + */ + static void injection(String jsCode, Frida frida, Runnable... callback) { + // 调用本地方法进行 JavaScript 注入 + FridaNative.injection(frida.pid, jsCode); + + // 如果有回调任务,循环执行回调 + if (callback != null) { + while (FridaNative.isRunning()) { + FridaNative.update(); + for (Runnable runnable : callback) { + runnable.run(); + } + } + } + + // 释放本地资源 + FridaNative.release(); + frida.isRunning = false; + } + + /** + * 发送消息给所有已注册的回调消息对象。 + * + *

此方法会遍历 CALLBACK_MESSAGE_LIST 列表,并触发每个回调对象的 onMessage 方法。

+ * + * @param message 需要发送的消息内容。 + */ + private static void sendMessage(String message) { + CALLBACK_MESSAGE_LIST.forEach(callbackMessage -> callbackMessage.onMessage(message)); + } + + /** + * 处理接收到的消息,并根据消息类型做出响应。 + * + *

根据消息中的 `type` 字段,如果是 `error` 类型,抛出运行时异常;如果是 `log` 类型,打印日志。

+ *

其他类型的消息直接通过回调发送。

+ * + * @param message 接收到的消息内容。 + */ + private static void onMessage(String message) { + // 获取消息类型 + String type = FridaNative.getStringMember(message, "type"); + + // 处理错误类型的消息 + if (!type.isEmpty()) { + if ("error".equals(type)) { + sendMessage(FridaNative.getStringMember(message, "payload")); + throw new RuntimeException(FridaNative.getStringMember(message, "description")); + } else if ("log".equals(type)) { + sendMessage(FridaNative.getStringMember(message, "payload")); + } + return; + } + + // 处理普通消息 + sendMessage(message); + } +} \ No newline at end of file diff --git a/src/main/java/org/tzd/frida/windows/FridaJsInjector.java b/src/main/java/org/tzd/frida/windows/FridaJsInjector.java new file mode 100644 index 0000000..7681d36 --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/FridaJsInjector.java @@ -0,0 +1,71 @@ +package org.tzd.frida.windows; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.io.IOException; + +/** + * FridaJsInjector 类负责通过 Frida 向目标进程注入 JavaScript 代码。 + *

此类提供了一个方法,允许通过读取指定的 JS 文件,并将其中的代码注入到指定的进程中。

+ *

如果注入过程中发生错误,类会抛出相应的异常。

+ * + * @author tzdwindows 7 + * @since 1.0 + * @version 1.0 + */ +public class FridaJsInjector { + + private Frida frida; + + /** + * 构造方法,通过传入 PID 和 JavaScript 文件路径来初始化 Frida 实例。 + * + * @param pid 目标进程的 PID。 + * @param jsFilePath JavaScript 文件的路径。 + */ + public FridaJsInjector(long pid, String jsFilePath) { + String jsCode = readJsFile(jsFilePath); + this.frida = new Frida(jsCode, pid); + } + + /** + * 读取指定路径的 JavaScript 文件内容。 + * + * @param jsFilePath JavaScript 文件路径。 + * @return 文件内容的字符串表示。 + */ + private String readJsFile(String jsFilePath) { + String jsCode = null; + try { + jsCode = new String(Files.readAllBytes(Paths.get(jsFilePath))); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("Failed to read the JavaScript file: " + jsFilePath, e); + } + return jsCode; + } + + /** + * 向目标进程注入 JavaScript 代码。 + *

该方法会使用 Frida 将 JavaScript 代码注入到指定的目标进程,并执行相关操作。

+ *

方法会启动一个线程,并在该线程中执行注入操作。可以通过提供回调函数来接收注入过程中的消息。

+ *

此方法会检查 Frida 实例是否已经初始化,并在初始化完成后执行 JavaScript 注入。

+ * + * @param callback 传入的回调消息,当接收到来自 Frida 的消息时,该回调会被触发。 + * @param callbackMessage 可选的额外回调函数,用于处理 JavaScript 注入的过程中的操作。 + * @return 返回一个线程对象,用于统一管理和控制线程执行。 + * + * @throws RuntimeException 如果 Frida 实例未初始化,则抛出运行时异常。 + */ + public Thread injectJs(CallbackMessage callback, Runnable... callbackMessage) { + if (frida == null) { + throw new RuntimeException("Frida instance is not initialized."); + } + return frida.run(callbackMessage).execute(new FridaRunnable() { + @Override + public void run(Frida frida) { + frida.addCallbackMessage(callback); + } + }); + } +} diff --git a/src/main/java/org/tzd/frida/windows/FridaNative.java b/src/main/java/org/tzd/frida/windows/FridaNative.java new file mode 100644 index 0000000..96ed750 --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/FridaNative.java @@ -0,0 +1,54 @@ +package org.tzd.frida.windows; + +/** + * FridaNative 类封装了与本地 Frida 库的交互方法。 + *

该类提供了几个静态本地方法,用于将 JavaScript 代码注入到目标进程并进行管理。

+ *

所有方法都通过 JNI 调用本地代码实现与 Frida 库的交互。

+ */ +public class FridaNative { + + /** + * 使用 Frida 将 JavaScript 代码注入到指定进程。 + * + *

该方法通过 JNI 调用本地 Frida 函数,将 JavaScript 代码注入到指定的进程中。

+ * + * @param pid 目标进程的 PID,通常是目标程序的进程 ID(例如,QQ 进程)。 + * @param jsCode 要注入的 JavaScript 代码字符串。 + * @return 返回注入是否成功,成功返回 `true`,失败返回 `false`。 + */ + native static boolean injection(long pid, String jsCode); + + /** + * 更新 Frida 的状态,保持与注入进程的交互。 + *

此方法调用本地 Frida 函数以保持与注入的进程进行通信和交互。

+ * + * @return 返回更新是否成功,成功返回 `true`,失败返回 `false`。 + */ + native static boolean update(); + + /** + * 判断 Frida 是否正在运行。 + *

此方法用于检查 Frida 是否正在执行并保持与目标进程的交互。

+ * + * @return 如果 Frida 正在运行,返回 `true`,否则返回 `false`。 + */ + native static boolean isRunning(); + + /** + * 释放 Frida 的资源。 + *

该方法调用本地函数释放与 Frida 进程的所有资源。

+ * + * @return 如果资源释放成功,返回 `true`,否则返回 `false`。 + */ + native static boolean release(); + + /** + * 从消息中提取指定的字符串成员。 + *

该方法通过 JNI 调用本地函数获取消息中的指定成员,并返回其字符串值。

+ * + * @param message 输入的消息字符串,通常是 Frida 的 JSON 格式的响应消息。 + * @param member 要获取的成员名称。 + * @return 返回指定成员的字符串值。 + */ + native static String getStringMember(String message, String member); +} diff --git a/src/main/java/org/tzd/frida/windows/FridaRunnable.java b/src/main/java/org/tzd/frida/windows/FridaRunnable.java new file mode 100644 index 0000000..c2142e2 --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/FridaRunnable.java @@ -0,0 +1,8 @@ +package org.tzd.frida.windows; + +/** + * @author tzdwindows 7 + */ +public interface FridaRunnable { + void run(Frida frida); +} diff --git a/src/main/java/org/tzd/frida/windows/FridaThread.java b/src/main/java/org/tzd/frida/windows/FridaThread.java new file mode 100644 index 0000000..18ec54c --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/FridaThread.java @@ -0,0 +1,62 @@ +package org.tzd.frida.windows; + +/** + * FridaThread 类继承自 {@link Thread},用于封装与 Frida 相关的线程操作。 + *

此类用于创建并启动线程,以便在新的线程中执行与 Frida 相关的任务。

+ *

它允许在启动线程之前执行额外的操作,例如调用 `FridaRunnable` 的 `run` 方法来配置与 Frida 的交互。

+ * + * @author tzdwindows 7 + */ +public class FridaThread extends Thread { + private boolean isStart = false; // 用于标记线程是否已配置正确 + private final Frida frida; // 与线程相关联的 Frida 实例 + + /** + * 构造一个新的 FridaThread 实例。 + *

该构造函数接受一个任务和一个 Frida 实例,创建一个线程并传入任务。

+ * + * @param task 线程执行的任务,类型为 {@link Runnable}。 + * @param frida 与该线程关联的 {@link Frida} 实例。 + */ + public FridaThread(Runnable task, Frida frida) { + super(task); // 调用父类构造器 + this.frida = frida; + setName("Frida Thread"); // 设置线程名称 + } + + /** + * 执行与 Frida 相关的额外任务。 + *

此方法会执行传入的 {@link FridaRunnable} 任务,并传入当前的 Frida 实例。

+ * + * @param runnable 执行任务的 {@link FridaRunnable} 实例。 + * @return 返回当前的 {@link FridaThread} 实例,以便链式调用。 + */ + public FridaThread execute(FridaRunnable runnable) { + runnable.run(frida); // 执行任务 + isStart = true; // 标记线程配置完成 + return this; // 返回当前线程实例 + } + + /** + * 设置线程是否已经开始执行。 + *

此方法可用于手动控制线程的启动配置。

+ * + * @param start 设置线程是否已配置完成。 + */ + public void setStart(boolean start) { + isStart = start; + } + + /** + * 启动线程。 + *

重写了 `Thread` 类的 `start()` 方法,首先检查线程是否已正确配置。

+ *

如果未正确配置,则抛出一个运行时异常,避免线程未准备好就启动。

+ */ + @Override + public void start() { + if (!isStart) { + throw new RuntimeException("Basic information is not configured"); + } + super.start(); // 调用父类的 `start()` 方法启动线程 + } +} diff --git a/src/main/java/org/tzd/frida/windows/JsCodeError.java b/src/main/java/org/tzd/frida/windows/JsCodeError.java new file mode 100644 index 0000000..6f14c84 --- /dev/null +++ b/src/main/java/org/tzd/frida/windows/JsCodeError.java @@ -0,0 +1,31 @@ +package org.tzd.frida.windows; + +/** + * 当 JavaScript 代码执行或语法出错时抛出此异常。 + *

此异常用于处理与 JavaScript 代码相关的错误,例如代码执行失败或语法错误。

+ *

通过该异常,程序能够捕获并处理 JavaScript 代码运行过程中可能出现的问题。

+ * + * @author tzdwindows 7 + * @since 1.0 + * @version 1.0 + */ +public class JsCodeError extends Exception { + + /** + * 默认构造方法。 + *

调用父类 {@link Exception} 的默认构造方法。

+ */ + public JsCodeError() { + super(); + } + + /** + * 带有错误信息的构造方法。 + *

使用传入的错误信息来初始化异常。

+ * + * @param message 错误信息,用于描述异常的具体内容。 + */ + public JsCodeError(String message) { + super(message); // 调用父类构造器,传入错误信息 + } +} diff --git a/src/main/resources/icons/debug/debug.png b/src/main/resources/icons/debug/debug.png new file mode 100644 index 0000000..f7f530f Binary files /dev/null and b/src/main/resources/icons/debug/debug.png differ diff --git a/src/main/resources/icons/debug/frida/frida_main.png b/src/main/resources/icons/debug/frida/frida_main.png new file mode 100644 index 0000000..b64bc68 Binary files /dev/null and b/src/main/resources/icons/debug/frida/frida_main.png differ diff --git a/src/main/resources/icons/logo.png b/src/main/resources/icons/logo.png new file mode 100644 index 0000000..15d6b5d Binary files /dev/null and b/src/main/resources/icons/logo.png differ diff --git a/src/main/resources/icons/settings.png b/src/main/resources/icons/settings.png new file mode 100644 index 0000000..cf964b3 Binary files /dev/null and b/src/main/resources/icons/settings.png differ diff --git a/src/main/resources/icons/start_page.png b/src/main/resources/icons/start_page.png new file mode 100644 index 0000000..80a8d1d Binary files /dev/null and b/src/main/resources/icons/start_page.png differ