diff --git a/language/sys_zh_CN.properties b/language/sys_zh_CN.properties index 1aabffd..decc10e 100644 --- a/language/sys_zh_CN.properties +++ b/language/sys_zh_CN.properties @@ -105,6 +105,66 @@ localWindow.prompt.system=\u4F60\u662F\u4E00\u4E2A\u4E50\u4E8E\u52A9\u4EBA\u7684 localWindow.prompt.system.2=\u4F60\u662F\u4E00\u4E2A\u4E13\u6CE8\u68C0\u7D22\u641C\u7D22\u4FE1\u606F\u7684\u52A9\u624B\uFF0C\u7528\u6237\u5C06\u63D0\u4F9B\u4E00\u4E9B\u94FE\u63A5\u3001\u6458\u8981\u3001\u6807\u9898\u3001\u722C\u53D6\u5230\u7684\u5185\u5BB9\uFF0C\u4F60\u7684\u4F5C\u7528\u5C31\u662F\u8F93\u51FA\u4E3B\u8981\u5185\u5BB9\u4E0E\u7528\u6237\u95EE\u9898\u76F8\u5173\u7684\u5185\u5BB9\u3002 localWindow.prompt.name=\u3010\u7528\u6237\u3011 +# \u8F93\u5165\u6846\u63D0\u793A +fridaWindow.pid.placeholder=\u8F93\u5165\u8FDB\u7A0BPID +fridaWindow.process.tooltip=\u9009\u62E9\u76EE\u6807\u8FDB\u7A0B + +# \u9762\u677F\u6807\u9898 +fridaWindow.script.title=\u811A\u672C\u7F16\u8F91\u5668 +fridaWindow.log.title=\u8F93\u51FA\u65E5\u5FD7 + +# \u8FDB\u7A0B\u76F8\u5173 +fridaWindow.process.name=\u8FDB\u7A0B\u540D\u79F0 +fridaWindow.process.pid=\u8FDB\u7A0BID +fridaWindow.processContext.inject=\u6CE8\u5165\u6B64\u8FDB\u7A0B +fridaWindow.search.placeholder=\u641C\u7D22\u8FDB\u7A0B... + +# \u9519\u8BEF\u63D0\u793A +fridaWindow.error.title=\u9519\u8BEF +fridaWindow.error.noPid=\u8BF7\u5148\u8F93\u5165\u8FDB\u7A0BPID +fridaWindow.error.invalidPid=\u65E0\u6548\u7684\u8FDB\u7A0BID +fridaWindow.error.injectFail=\u6CE8\u5165\u5931\u8D25 +fridaWindow.error.open=\u6587\u4EF6\u6253\u5F00\u5931\u8D25 +fridaWindow.error.save=\u6587\u4EF6\u4FDD\u5B58\u5931\u8D25 + +# \u6587\u4EF6\u8FC7\u6EE4 +fridaWindow.fileFilter=Frida\u811A\u672C\u6587\u4EF6 (*.frida) + +# \u8BBE\u7F6E\u7A97\u53E3 +fridaWindow.settings.tab.color=\u8BED\u6CD5\u9AD8\u4EAE +fridaWindow.settings.tab.font=\u5B57\u4F53\u8BBE\u7F6E +fridaWindow.settings.save=\u4FDD\u5B58 +fridaWindow.settings.default=\u6062\u590D\u9ED8\u8BA4 +fridaWindow.settings.cancel=\u53D6\u6D88 +fridaWindow.chooseColor=\u9009\u62E9\u989C\u8272 + +# \u8BED\u6CD5\u9AD8\u4EAE\u8BBE\u7F6E +fridaWindow.col.syntax=\u8BED\u6CD5\u5143\u7D20 +fridaWindow.col.color=\u989C\u8272 +fridaWindow.syntax.keyword=\u5173\u952E\u5B57 +fridaWindow.syntax.stdlib=\u6807\u51C6\u5E93 +fridaWindow.syntax.builtin=\u5185\u7F6E\u51FD\u6570 +fridaWindow.syntax.string=\u5B57\u7B26\u4E32 +fridaWindow.syntax.number=\u6570\u5B57 +fridaWindow.syntax.comment=\u6CE8\u91CA +fridaWindow.syntax.operator=\u8FD0\u7B97\u7B26 +fridaWindow.syntax.identifier=\u6807\u8BC6\u7B26 +fridaWindow.syntax.separator=\u5206\u9694\u7B26 +fridaWindow.syntax.regex=\u6B63\u5219\u8868\u8FBE\u5F0F + +# \u5B57\u4F53\u8BBE\u7F6E +fridaWindow.font.name=\u5B57\u4F53\u540D\u79F0 +fridaWindow.font.size=\u5B57\u4F53\u5927\u5C0F +fridaWindow.font.style=\u5B57\u4F53\u6837\u5F0F +fridaWindow.font.bold=\u7C97\u4F53 +fridaWindow.font.italic=\u659C\u4F53 +fridaWindow.font.previewLabel=\u9884\u89C8 +fridaWindow.font.preview=\u793A\u4F8B\u6587\u672C\uFF1Aconsole.log("Hello Frida"); + +# \u5173\u4E8E\u7A97\u53E3 +fridaWindow.about.title=\u5173\u4E8E +fridaWindow.about.content=\u57FA\u4E8EFrida\u7684\u73B0\u4EE3\u6CE8\u5165\u5DE5\u5177\n\n\u7248\u6743\u6240\u6709 \u00A9 {0} + settings.1.title=\u63D2\u4EF6 settings.2.title=\u57FA\u7840\u8BBE\u7F6E settings.3.title=\u5173\u4E8E diff --git a/src/main/java/com/axis/innovators/box/Main.java b/src/main/java/com/axis/innovators/box/Main.java index 94f2a4a..c24e633 100644 --- a/src/main/java/com/axis/innovators/box/Main.java +++ b/src/main/java/com/axis/innovators/box/Main.java @@ -76,11 +76,9 @@ public class Main { return; } } - if (!acquireLock()) { return; } - AxisInnovatorsBox.run(args, debugWindowEnabled); } diff --git a/src/main/java/com/axis/innovators/box/window/FridaWindow.java b/src/main/java/com/axis/innovators/box/window/FridaWindow.java index b74aac5..f98bb3b 100644 --- a/src/main/java/com/axis/innovators/box/window/FridaWindow.java +++ b/src/main/java/com/axis/innovators/box/window/FridaWindow.java @@ -9,6 +9,7 @@ import org.fife.rsta.ac.LanguageSupportFactory; import org.fife.ui.autocomplete.AutoCompletion; import org.fife.ui.autocomplete.CompletionProvider; import org.fife.ui.autocomplete.DefaultCompletionProvider; +import org.fife.ui.autocomplete.ShorthandCompletion; import org.fife.ui.rsyntaxtextarea.*; import org.fife.ui.rsyntaxtextarea.folding.CurlyFoldParser; import org.fife.ui.rsyntaxtextarea.folding.FoldParserManager; @@ -22,24 +23,21 @@ import javax.swing.table.DefaultTableModel; import java.awt.*; import java.awt.event.*; import java.io.*; -import java.util.List; import java.util.*; +import java.util.List; import java.util.prefs.Preferences; /** * Frida注入工具窗口 - * @author tzdwindows 7 */ public class FridaWindow extends WindowsJDialog { private static final Logger logger = LogManager.getLogger(FridaWindow.class); + private RSyntaxTextArea scriptArea; private JTextArea logArea; private boolean isRepetition = false; private JComboBox processCombo; private JTextField pidField; - // IDEA风格配色 - private static final Color BACKGROUND = new Color(0x2B2B2B); - private static final Color SELECTION_COLOR = new Color(0x214283); private JDialog settingsDialog; public FridaWindow(Window owner) { @@ -54,95 +52,138 @@ public class FridaWindow extends WindowsJDialog { setLocationRelativeTo(getOwner()); setDefaultCloseOperation(DISPOSE_ON_CLOSE); - JPanel mainPanel = new JPanel(new BorderLayout()); - mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - mainPanel.setBackground(BACKGROUND); + // 从 UIManager 获取主题颜色与字体 + Color panelBg = safeColor("Panel.background", new Color(0x2B2B2B)); + Color accent = safeColor("controlHighlight", new Color(0x214283)); + Color textColor = safeColor("textText", Color.WHITE); + Color secondary = safeColor("textInactiveText", new Color(0xBBBBBB)); + Font baseFont = UIManager.getFont("Label.font"); + if (baseFont == null) baseFont = new Font("Segoe UI", Font.PLAIN, 13); + + // 顶层面板 + JPanel mainPanel = new JPanel(new BorderLayout(12, 12)); + mainPanel.setBorder(new EmptyBorder(14, 14, 14, 14)); + mainPanel.setBackground(panelBg); + + // 顶栏(标题 + 控件) + JPanel topBar = new JPanel(new BorderLayout(12, 0)); + topBar.setOpaque(false); + + JLabel titleLabel = new JLabel(language.getText("fridaWindow.title")); + titleLabel.setFont(baseFont.deriveFont(Font.BOLD, 16f)); + titleLabel.setForeground(textColor); + topBar.add(titleLabel, BorderLayout.WEST); + + // 右侧控制 + JPanel rightTop = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + rightTop.setOpaque(false); pidField = new JTextField(); - styleTextField(pidField, "输入PID或进程名"); - mainPanel.add(pidField, BorderLayout.CENTER); + applyFont(pidField, baseFont.deriveFont(13f)); + pidField.setPreferredSize(new Dimension(220, 36)); + styleTextField(pidField, language.getText("fridaWindow.pid.placeholder"), panelBg, textColor); - // 输入面板 - JPanel inputPanel = createInputPanel(); - mainPanel.add(inputPanel, BorderLayout.NORTH); + processCombo = new JComboBox<>(); + processCombo.setPreferredSize(new Dimension(360, 36)); + applyFont(processCombo, baseFont.deriveFont(13f)); + styleComboBox(processCombo, panelBg, textColor, secondary); + refreshProcessCombo(); - // 中心区域(代码编辑器和日志) + JButton injectBtn = createModernButton(language.getText("fridaWindow.button.inject"), accent, textColor, baseFont); + JButton selectBtn = createModernButton(language.getText("fridaWindow.button.inject.pid"), new Color(0xD25252), textColor, baseFont); + + injectBtn.addActionListener(this::handleInject); + selectBtn.addActionListener(this::openProcessSelectionWindow); + + rightTop.add(pidField); + rightTop.add(processCombo); + rightTop.add(injectBtn); + rightTop.add(selectBtn); + + topBar.add(rightTop, BorderLayout.EAST); + mainPanel.add(topBar, BorderLayout.NORTH); + + // 中央分割:上代码编辑器,下日志 JSplitPane centerSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - centerSplit.setDividerLocation(500); + centerSplit.setDividerLocation(520); centerSplit.setContinuousLayout(true); + centerSplit.setBorder(null); + centerSplit.setOpaque(false); - // 脚本编辑器 - JPanel scriptPanel = createScriptPanel(); + JPanel scriptPanel = createScriptPanel(panelBg, accent, textColor, baseFont); centerSplit.setTopComponent(scriptPanel); - // 日志输出 - JPanel logPanel = createLogPanel(); + JPanel logPanel = createLogPanel(panelBg, textColor, baseFont); centerSplit.setBottomComponent(logPanel); mainPanel.add(centerSplit, BorderLayout.CENTER); - createMenuBar(); + + createMenuBar(panelBg, textColor, secondary, baseFont); + setContentPane(mainPanel); addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { - scriptArea.requestFocusInWindow(); + if (scriptArea != null) scriptArea.requestFocusInWindow(); } }); } - private void createMenuBar() { + // ---------- UI helper: 安全取颜色与字体 ---------- + private Color safeColor(String uiKey, Color fallback) { + Color c = UIManager.getColor(uiKey); + return c != null ? c : fallback; + } + + private void applyFont(JComponent comp, Font f) { + if (f != null) comp.setFont(f); + } + + // ---------- 菜单栏 ---------- + private void createMenuBar(Color panelBg, Color textColor, Color secondary, Font baseFont) { JMenuBar menuBar = new JMenuBar(); - // 菜单栏深色背景 - menuBar.setBackground(new Color(0x3C3F41)); - menuBar.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0)); + menuBar.setBackground(panelBg); + menuBar.setBorder(BorderFactory.createEmptyBorder(4, 6, 4, 6)); + applyFont(menuBar, baseFont.deriveFont(13f)); - // 统一设置菜单UI属性 - UIManager.put("Menu.background", new Color(0x3C3F41)); - UIManager.put("Menu.foreground", new Color(0xBBBBBB)); - UIManager.put("MenuItem.background", new Color(0x3C3F41)); - UIManager.put("MenuItem.foreground", new Color(0xBBBBBB)); - UIManager.put("MenuItem.selectionBackground", new Color(0x214283)); + UIManager.put("Menu.background", panelBg); + UIManager.put("Menu.foreground", secondary); + UIManager.put("MenuItem.background", panelBg); + UIManager.put("MenuItem.foreground", secondary); + UIManager.put("MenuItem.selectionBackground", safeColor("controlHighlight", new Color(0x214283))); - // 文件菜单 JMenu fileMenu = new JMenu(language.getText("fridaWindow.menu")); - styleMenu(fileMenu); - - // 退出菜单项 - JMenuItem exitItem = new JMenuItem(language.getText("fridaWindow.menu.exit")); - styleMenuItem(exitItem); - exitItem.addActionListener(e -> dispose()); + styleMenu(fileMenu, textColor, panelBg, baseFont); JMenuItem openItem = new JMenuItem(language.getText("fridaWindow.menu.open")); - styleMenuItem(openItem); - openItem.addActionListener(e -> openFile()); - JMenuItem saveItem = new JMenuItem(language.getText("fridaWindow.menu.save")); - styleMenuItem(saveItem); - saveItem.addActionListener(e -> saveFile()); + JMenuItem exitItem = new JMenuItem(language.getText("fridaWindow.menu.exit")); + styleMenuItem(openItem, textColor, panelBg, baseFont); + styleMenuItem(saveItem, textColor, panelBg, baseFont); + styleMenuItem(exitItem, textColor, panelBg, baseFont); + + openItem.addActionListener(e -> openFile()); + saveItem.addActionListener(e -> saveFile()); + exitItem.addActionListener(e -> dispose()); - fileMenu.add(exitItem); fileMenu.add(openItem); fileMenu.add(saveItem); + fileMenu.addSeparator(); + fileMenu.add(exitItem); - // 帮助菜单 - JMenu helpMenu = new JMenu(language.getText("fridaWindow.menu.help")); - styleMenu(helpMenu); - - // 设置菜单 JMenu settingsMenu = new JMenu(language.getText("fridaWindow.menu.settingsMenu")); - styleMenu(fileMenu); - + styleMenu(settingsMenu, textColor, panelBg, baseFont); JMenuItem settingsItem = new JMenuItem(language.getText("fridaWindow.menu.settingsMenu.settingsItem")); - styleMenuItem(settingsItem); + styleMenuItem(settingsItem, textColor, panelBg, baseFont); settingsItem.addActionListener(e -> settingsWindow()); settingsMenu.add(settingsItem); - // 关于菜单项 + JMenu helpMenu = new JMenu(language.getText("fridaWindow.menu.help")); + styleMenu(helpMenu, textColor, panelBg, baseFont); JMenuItem aboutItem = new JMenuItem(language.getText("fridaWindow.menu.help.about")); - styleMenuItem(aboutItem); + styleMenuItem(aboutItem, textColor, panelBg, baseFont); aboutItem.addActionListener(e -> showAboutDialog()); - helpMenu.add(aboutItem); menuBar.add(fileMenu); @@ -151,549 +192,203 @@ public class FridaWindow extends WindowsJDialog { setJMenuBar(menuBar); } - private void styleTextField(JTextField field, String placeholder) { - field.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - field.setBackground(new Color(0x3C3F41)); - field.setForeground(Color.WHITE); + private void styleMenu(JMenu menu, Color fg, Color bg, Font f) { + menu.setFont(f.deriveFont(Font.PLAIN, 13f)); + menu.setForeground(fg); + menu.setBorder(BorderFactory.createEmptyBorder(6, 10, 6, 10)); + menu.setOpaque(true); + menu.setBackground(bg); + } + + private void styleMenuItem(JMenuItem item, Color fg, Color bg, Font f) { + item.setFont(f.deriveFont(Font.PLAIN, 13f)); + item.setForeground(fg); + item.setBackground(bg); + item.setBorder(BorderFactory.createEmptyBorder(6, 12, 6, 8)); + item.setOpaque(true); + item.addMouseListener(new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + item.setBackground(bg.brighter()); + } + + public void mouseExited(MouseEvent e) { + item.setBackground(bg); + } + }); + } + + // ---------- 文本框 / 组合框样式 ---------- + private void styleTextField(JTextField field, String placeholder, Color panelBg, Color fg) { + field.setFont(UIManager.getFont("TextField.font") != null ? UIManager.getFont("TextField.font") : new Font("Microsoft YaHei", Font.PLAIN, 13)); + field.setBackground(panelBg.darker()); + field.setForeground(fg); field.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(0x515658)), - BorderFactory.createEmptyBorder(5, 8, 5, 8) + BorderFactory.createLineBorder(panelBg.brighter(), 1), + BorderFactory.createEmptyBorder(6, 8, 6, 8) )); field.putClientProperty("JTextField.placeholderText", placeholder); } - private JPanel createInputPanel() { - JPanel panel = new JPanel(new BorderLayout(10, 0)); - panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); - panel.setOpaque(false); - - // 进程组合框 - processCombo = new JComboBox<>(); - styleComboBox(processCombo); - - // 操作工具栏 - JPanel controlPanel = new JPanel(new GridLayout(1, 2, 8, 0)); - controlPanel.setOpaque(false); - - JButton injectBtn = createModernButton(language.getText("fridaWindow.button.inject"), 0x6897BB); - JButton stopBtn = createModernButton(language.getText("fridaWindow.button.inject.pid"), 0xD25252); - - injectBtn.addActionListener(this::handleInject); - stopBtn.addActionListener(this::openProcessSelectionWindow); - - controlPanel.add(injectBtn); - controlPanel.add(stopBtn); - - panel.add(processCombo, BorderLayout.CENTER); - panel.add(controlPanel, BorderLayout.EAST); - - return panel; - } - - private JButton createModernButton(String text, int color) { - JButton btn = new JButton(text); - btn.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - btn.setFocusPainted(false); - btn.setBackground(new Color(color)); - btn.setForeground(Color.WHITE); - btn.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(color).darker(), 1), - BorderFactory.createEmptyBorder(6, 15, 6, 15) - )); - - // 悬停效果 - btn.addMouseListener(new MouseAdapter() { - public void mouseEntered(MouseEvent e) { - btn.setBackground(new Color(color).brighter()); - } - - public void mouseExited(MouseEvent e) { - btn.setBackground(new Color(color)); - } - }); - return btn; - } - - private void settingsWindow() { - settingsDialog = new JDialog(this, "编辑器设置", true); - settingsDialog.setSize(600, 450); - settingsDialog.setLocationRelativeTo(this); - - JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); - mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - - // 配置选项面板 - JTabbedPane tabbedPane = new JTabbedPane(); - - // 颜色设置面板 - JPanel colorPanel = createColorSettingsPanel(); - tabbedPane.addTab("语法颜色", colorPanel); - - // 字体设置面板 - JPanel fontPanel = createFontSettingsPanel(); - tabbedPane.addTab("字体设置", fontPanel); - - // 底部按钮 - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - JButton saveBtn = createModernButton("保存", 0x6897BB); - JButton defaultBtn = createModernButton("恢复默认", 0x808080); - JButton cancelBtn = createModernButton("取消", 0xD25252); - - saveBtn.addActionListener(e -> applyAndSaveSettings()); - defaultBtn.addActionListener(e -> restoreDefaultSettings()); - cancelBtn.addActionListener(e -> settingsDialog.dispose()); - - buttonPanel.add(defaultBtn); - buttonPanel.add(cancelBtn); - buttonPanel.add(saveBtn); - - mainPanel.add(tabbedPane, BorderLayout.CENTER); - mainPanel.add(buttonPanel, BorderLayout.SOUTH); - - settingsDialog.setContentPane(mainPanel); - settingsDialog.setVisible(true); - } - - private JPanel createColorSettingsPanel() { - JPanel panel = new JPanel(new BorderLayout()); - - // 使用LinkedHashMap保持顺序 - Map tokenConfig = new LinkedHashMap<>(); - tokenConfig.put("关键字(Frida)", Token.RESERVED_WORD); - tokenConfig.put("标准库函数", Token.RESERVED_WORD_2); - tokenConfig.put("内置函数", Token.FUNCTION); - tokenConfig.put("字符串", Token.LITERAL_STRING_DOUBLE_QUOTE); - tokenConfig.put("数字", Token.LITERAL_NUMBER_DECIMAL_INT); - tokenConfig.put("注释", Token.COMMENT_EOL); - tokenConfig.put("运算符", Token.OPERATOR); - tokenConfig.put("标识符", Token.IDENTIFIER); - tokenConfig.put("分隔符", Token.SEPARATOR); - tokenConfig.put("正则表达式", Token.REGEX); - - DefaultTableModel model = new DefaultTableModel(new Object[]{"语法元素", "颜色示例"}, 0); - for (Map.Entry entry : tokenConfig.entrySet()) { - Style style = scriptArea.getSyntaxScheme().getStyle(entry.getValue()); - model.addRow(new Object[]{entry.getKey(), style.foreground}); - } - - JTable colorTable = new JTable(model); - colorTable.setRowHeight(30); - colorTable.getColumnModel().getColumn(1).setCellRenderer(new ColorRenderer()); - colorTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - int column = colorTable.columnAtPoint(e.getPoint()); - if (e.getClickCount() == 1 && column == 1) { - int row = colorTable.rowAtPoint(e.getPoint()); - Color current = (Color) colorTable.getValueAt(row, 1); - Color newColor = JColorChooser.showDialog(settingsDialog, - "选择颜色", current); - if (newColor != null) { - colorTable.setValueAt(newColor, row, 1); - } - } - } - }); - - JScrollPane scrollPane = new JScrollPane(colorTable); - panel.add(scrollPane, BorderLayout.CENTER); - - // 保存颜色配置引用 - panel.putClientProperty("tokenConfig", tokenConfig); - panel.putClientProperty("colorTable", colorTable); - - return panel; - } - - private JPanel createFontSettingsPanel() { - JPanel panel = new JPanel(new GridLayout(0, 2, 10, 10)); - - // 字体名称 - String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment() - .getAvailableFontFamilyNames(); - JComboBox fontCombo = new JComboBox<>(fontNames); - fontCombo.setSelectedItem(scriptArea.getFont().getFamily()); - - // 字体样式 - JCheckBox boldCheck = new JCheckBox("粗体"); - JCheckBox italicCheck = new JCheckBox("斜体"); - - // 字号 - SpinnerModel sizeModel = new SpinnerNumberModel( - scriptArea.getFont().getSize(), 8, 48, 1); - JSpinner sizeSpinner = new JSpinner(sizeModel); - - panel.add(new JLabel("字体名称:")); - panel.add(fontCombo); - panel.add(new JLabel("字号:")); - panel.add(sizeSpinner); - panel.add(new JLabel("样式:")); - JPanel stylePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - stylePanel.add(boldCheck); - stylePanel.add(italicCheck); - panel.add(stylePanel); - - // 预览区域 - JTextArea preview = new JTextArea("Frida示例代码:\nJava.perform(() => {\n // 注释\n console.log('Hello');\n for (var i = 0; i < 10; i++) {\n" + - " \n" + - "}});"); - preview.setFont(scriptArea.getFont()); - preview.setEditable(false); - preview.setBackground(scriptArea.getBackground()); - preview.setForeground(scriptArea.getForeground()); - - // 监听变化 - fontCombo.addActionListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); - sizeSpinner.addChangeListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); - boldCheck.addActionListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); - italicCheck.addActionListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); - - panel.add(new JLabel("预览:")); - panel.add(new JScrollPane(preview)); - - // 保存控件引用 - panel.putClientProperty("fontCombo", fontCombo); - panel.putClientProperty("sizeSpinner", sizeSpinner); - panel.putClientProperty("boldCheck", boldCheck); - panel.putClientProperty("italicCheck", italicCheck); - - return panel; - } - - private void updateFontPreview(JComboBox fontCombo, JSpinner sizeSpinner, - JCheckBox boldCheck, JCheckBox italicCheck, JTextArea preview) { - String fontName = (String) fontCombo.getSelectedItem(); - int size = (Integer) sizeSpinner.getValue(); - int style = Font.PLAIN; - if (boldCheck.isSelected()) style |= Font.BOLD; - if (italicCheck.isSelected()) style |= Font.ITALIC; - Font newFont = new Font(fontName, style, size); - preview.setFont(newFont); - } - - private void applyAndSaveSettings() { - // 应用颜色设置 - JPanel colorPanel = (JPanel) ((JTabbedPane)settingsDialog.getContentPane() - .getComponent(0)).getComponent(0); - - Map tokenConfig = (Map) colorPanel.getClientProperty("tokenConfig"); - JTable colorTable = (JTable) colorPanel.getClientProperty("colorTable"); - - SyntaxScheme scheme = scriptArea.getSyntaxScheme(); - for (int i = 0; i < colorTable.getRowCount(); i++) { - String tokenName = (String) colorTable.getValueAt(i, 0); - Color color = (Color) colorTable.getValueAt(i, 1); - Integer tokenType = tokenConfig.get(tokenName); - if (tokenType != null) { - scheme.getStyle(tokenType).foreground = color; - } - } - - // 应用字体设置 - JPanel fontPanel = (JPanel) ((JTabbedPane)settingsDialog.getContentPane() - .getComponent(0)).getComponent(1); - - JComboBox fontCombo = (JComboBox) fontPanel.getClientProperty("fontCombo"); - JSpinner sizeSpinner = (JSpinner) fontPanel.getClientProperty("sizeSpinner"); - JCheckBox boldCheck = (JCheckBox) fontPanel.getClientProperty("boldCheck"); - JCheckBox italicCheck = (JCheckBox) fontPanel.getClientProperty("italicCheck"); - - String fontName = (String) fontCombo.getSelectedItem(); - int size = (Integer) sizeSpinner.getValue(); - int style = Font.PLAIN; - if (boldCheck.isSelected()) style |= Font.BOLD; - if (italicCheck.isSelected()) style |= Font.ITALIC; - Font newFont = new Font(fontName, style, size); - scriptArea.setFont(newFont); - - // 保存到首选项 - Preferences prefs = Preferences.userNodeForPackage(FridaWindow.class); - prefs.putInt("editor.font.style", style); - prefs.putInt("editor.font.size", size); - prefs.put("editor.font.name", fontName); - - settingsDialog.dispose(); - scriptArea.repaint(); - } - - private void restoreDefaultSettings() { - // 恢复颜色默认 - configureIDEATheme(scriptArea.getSyntaxScheme()); - - // 恢复字体默认 - Font defaultFont = new Font("JetBrains Mono", Font.PLAIN, 13); - if (!Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment() - .getAvailableFontFamilyNames()).contains("JetBrains Mono")) { - defaultFont = new Font("Consolas", Font.PLAIN, 14); - } - scriptArea.setFont(defaultFont); - - // 清除首选项存储 - Preferences prefs = Preferences.userNodeForPackage(FridaWindow.class); - prefs.remove("editor.font.name"); - prefs.remove("editor.font.style"); - prefs.remove("editor.font.size"); - - settingsDialog.dispose(); - scriptArea.repaint(); - } - - // 自定义表格渲染器用于显示颜色 - private static class ColorRenderer extends DefaultTableCellRenderer { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, - isSelected, hasFocus, row, column); - if (value instanceof Color) { - setText(""); - setBackground((Color) value); - setBorder(BorderFactory.createLineBorder(Color.GRAY)); - } - return c; - } - } - - private void styleComboBox(JComboBox combo) { + private void styleComboBox(JComboBox combo, Color bg, Color fg, Color secondary) { + combo.setBackground(bg.darker()); + combo.setForeground(fg); + combo.setBorder(BorderFactory.createLineBorder(bg.brighter(), 1)); combo.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - setBackground(isSelected ? new Color(0x214283) : new Color(0x3C3F41)); - setForeground(Color.WHITE); + setBackground(isSelected ? bg.darker().brighter() : bg.darker()); + setForeground(fg); return this; } }); } - - private void openFile() { - AdvancedJFileChooser fileChooser = new AdvancedJFileChooser(); - fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Frida脚本配置 (*.frida)", "frida")); - int result = fileChooser.showOpenDialog(this); - if (result == JFileChooser.APPROVE_OPTION) { - try (BufferedReader reader = new BufferedReader(new FileReader(fileChooser.getSelectedFile()))) { - String pid = reader.readLine(); - StringBuilder script = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - script.append(line).append("\n"); - } - pidField.setText(pid); - scriptArea.setText(script.toString()); - } catch (IOException ex) { - logger.error("文件打开失败", ex); - JOptionPane.showMessageDialog(this, - "无法加载文件: " + ex.getMessage(), - "IO错误", - JOptionPane.ERROR_MESSAGE); - } - } - } - - private void saveFile() { - AdvancedJFileChooser fileChooser = new AdvancedJFileChooser(); - fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Frida脚本配置 (*.frida)", "frida")); - int result = fileChooser.showSaveDialog(this); - if (result == JFileChooser.APPROVE_OPTION) { - File file = fileChooser.getSelectedFile(); - if (!file.getName().toLowerCase().endsWith(".frida")) { - file = new File(file.getParentFile(), file.getName() + ".frida"); - } - try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { - writer.write(pidField.getText()); - writer.newLine(); - writer.write(scriptArea.getText()); - } catch (IOException ex) { - logger.error("文件保存失败", ex); - JOptionPane.showMessageDialog(this, - "无法保存文件: " + ex.getMessage(), - "IO错误", - JOptionPane.ERROR_MESSAGE); - } - } - } - - - private void styleMenu(JMenu menu) { - menu.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - menu.setForeground(new Color(0xBBBBBB)); - menu.setBorder(BorderFactory.createEmptyBorder(5, 8, 5, 8)); - menu.setOpaque(true); - menu.setBackground(new Color(0x3C3F41)); - } - - private void styleMenuItem(JMenuItem item) { - item.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - item.setForeground(new Color(0xBBBBBB)); - item.setBackground(new Color(0x3C3F41)); - item.setBorder(BorderFactory.createEmptyBorder(5, 20, 5, 10)); // 增加左侧间距 - item.setOpaque(true); - - // 鼠标悬停效果 - item.addMouseListener(new MouseAdapter() { + // ---------- 现代化按钮 ---------- + private JButton createModernButton(String text, Color bgColor, Color fg, Font baseFont) { + JButton btn = new JButton(text); + btn.setFont(baseFont.deriveFont(Font.PLAIN, 13f)); + btn.setFocusPainted(false); + btn.setBackground(bgColor); + btn.setForeground(fg); + btn.setOpaque(true); + btn.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(bgColor.darker(), 1), + BorderFactory.createEmptyBorder(8, 14, 8, 14) + )); + btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + btn.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { - item.setBackground(new Color(0x515658)); + btn.setBackground(bgColor.brighter()); } public void mouseExited(MouseEvent e) { - item.setBackground(new Color(0x3C3F41)); + btn.setBackground(bgColor); } }); + return btn; } - private void showAboutDialog() { - JDialog aboutDialog = new JDialog(this, "关于", true); - aboutDialog.setSize(300, 200); - aboutDialog.setLocationRelativeTo(this); - - JPanel content = new JPanel(new BorderLayout()); - content.setBorder(new EmptyBorder(20, 20, 20, 20)); - - JLabel title = new JLabel("Frida Injector v2.0"); - title.setFont(new Font("Segoe UI", Font.BOLD, 16)); - - JTextArea info = new JTextArea("基于Frida的现代注入工具\n\nCopyright ? 2024"); - info.setEditable(false); - - content.add(title, BorderLayout.NORTH); - content.add(info, BorderLayout.CENTER); - - aboutDialog.setContentPane(content); - aboutDialog.setVisible(true); - } - - private JPanel createScriptPanel() { + // ---------- 脚本编辑区 ---------- + private JPanel createScriptPanel(Color panelBg, Color accent, Color textColor, Font baseFont) { JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createTitledBorder("Frida脚本编辑器")); + panel.setBorder(BorderFactory.createTitledBorder(language.getText("fridaWindow.script.title"))); + panel.setBackground(panelBg); scriptArea = new RSyntaxTextArea(); + // 语言支持 LanguageSupportFactory lsf = LanguageSupportFactory.get(); - LanguageSupport support = lsf. - getSupportFor(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT); + LanguageSupport support = lsf.getSupportFor(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT); support.install(scriptArea); scriptArea.setCodeFoldingEnabled(true); FoldParserManager.get().addFoldParserMapping("text/javascript", new CurlyFoldParser()); - scriptArea.setFractionalFontMetricsEnabled(true); scriptArea.setAutoIndentEnabled(true); - - scriptArea.setCodeFoldingEnabled(true); scriptArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT); scriptArea.setAntiAliasingEnabled(true); scriptArea.setTabSize(4); scriptArea.setCaretPosition(0); - scriptArea.setSelectionColor(SELECTION_COLOR); scriptArea.setMarkOccurrences(true); scriptArea.setBracketMatchingEnabled(true); + setupAutoCompletion(); - 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); + // 编辑器字体:优先使用 UIManager 的 TextArea.font -> 否则选择等宽字体 + Font editorBase = UIManager.getFont("TextArea.font"); + if (editorBase == null) { + // 优先等宽字体 + if (Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()).contains("JetBrains Mono")) { + editorBase = new Font("JetBrains Mono", Font.PLAIN, 13); + } else if (Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()).contains("Consolas")) { + editorBase = new Font("Consolas", Font.PLAIN, 14); + } else { + editorBase = new Font(Font.MONOSPACED, Font.PLAIN, 13); + } } - scriptArea.setFont(ideaFont); + scriptArea.setFont(editorBase); - scriptArea.setBackground(new Color(0x1E1F22)); - scriptArea.setSelectionColor(new Color(0x214283)); - scriptArea.setCurrentLineHighlightColor(new Color(0x323232)); + Color editorBg = safeColor("TextArea.background", new Color(0x1E1F22)); + Color editorFg = safeColor("TextArea.foreground", textColor); + scriptArea.setBackground(editorBg); + scriptArea.setForeground(editorFg); + scriptArea.setSelectionColor(accent); + scriptArea.setCurrentLineHighlightColor(safeColor("TextArea.selectionBackground", new Color(0x323232))); + scriptArea.setHighlightCurrentLine(true); - SyntaxScheme scheme = new SyntaxScheme(true); - scheme = configureIDEATheme(scheme); + SyntaxScheme scheme = configureIDEATheme(new SyntaxScheme(true)); scriptArea.setSyntaxScheme(scheme); RTextScrollPane scrollPane = new RTextScrollPane(scriptArea); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41))); + scrollPane.setBorder(BorderFactory.createLineBorder(panelBg.darker())); scrollPane.setFoldIndicatorEnabled(true); - panel.add(scrollPane); - + panel.add(scrollPane, BorderLayout.CENTER); return panel; } + // ---------- 语法配色(IDEA 风格) ---------- private SyntaxScheme configureIDEATheme(SyntaxScheme scheme) { - // 基础颜色配置 - Color background = new Color(0x2B2B2B); // 背景色 - Color foreground = new Color(0xE0E0E0); // 前景色(默认文本颜色) + Color foreground = safeColor("TextArea.foreground", new Color(0xE0E0E0)); + Color background = safeColor("TextArea.background", new Color(0x2B2B2B)); - // 设置全局默认样式 Style defaultStyle = scheme.getStyle(Token.NULL); + if (defaultStyle == null) { + defaultStyle = new Style(); + scheme.setStyle(Token.NULL, defaultStyle); + } defaultStyle.foreground = foreground; defaultStyle.background = background; - // ======== JavaScript 标准库配置 ======== - // console 相关 API - setTokenStyle(scheme, Token.RESERVED_WORD_2, 0xCC7832); // 橙色 - setTokenStyle(scheme, Token.FUNCTION, 0xFFC66D); // 黄色 - - // ======== 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); // 红色 - - // ======== 跨语言通用配置 ======== - // 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)); + setTokenStyle(scheme, Token.RESERVED_WORD_2, 0xCC7832); + setTokenStyle(scheme, Token.FUNCTION, 0xFFC66D); + setTokenStyle(scheme, Token.RESERVED_WORD, 0xCC7832); + 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); + setTokenStyle(scheme, Token.MARKUP_TAG_NAME, 0xE8BF6A); + setTokenStyle(scheme, Token.MARKUP_TAG_DELIMITER, 0xCC7832); + setTokenStyle(scheme, Token.MARKUP_ENTITY_REFERENCE, 0x6897BB); return scheme; } - + // 修复:使用公开 API 设置样式(不要访问私有 fields) private void setTokenStyle(SyntaxScheme scheme, int tokenType, int rgb) { Style style = scheme.getStyle(tokenType); + if (style == null) { + style = new Style(); + scheme.setStyle(tokenType, style); + } style.foreground = new Color(rgb); } - private JPanel createLogPanel() { + // ---------- 日志面板 ---------- + private JPanel createLogPanel(Color panelBg, Color textColor, Font baseFont) { JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createTitledBorder("日志输出")); + panel.setBorder(BorderFactory.createTitledBorder(language.getText("fridaWindow.log.title"))); + panel.setBackground(panelBg); logArea = new JTextArea(); logArea.setEditable(false); - logArea.setFont(new Font("Consolas", Font.PLAIN, 12)); - logArea.setBackground(new Color(0x1E1E1E)); - logArea.setForeground(Color.WHITE); + logArea.setFont(UIManager.getFont("TextArea.font") != null ? UIManager.getFont("TextArea.font") : new Font("Consolas", Font.PLAIN, 12)); + logArea.setBackground(safeColor("TextArea.background", new Color(0x1E1E1E))); + logArea.setForeground(textColor); JScrollPane scrollPane = new JScrollPane(logArea); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41))); - + scrollPane.setBorder(BorderFactory.createLineBorder(panelBg.darker())); panel.add(scrollPane); return panel; } + // ---------- 自动补全 ---------- private void setupAutoCompletion() { try { CompletionProvider provider = createCompletionProvider(); @@ -705,12 +400,12 @@ public class FridaWindow extends WindowsJDialog { ac.setListCellRenderer(new DarculaCompletionCellRenderer()); ac.install(scriptArea); - ac.setDescriptionWindowColor(new Color(0x323232)); + ac.setDescriptionWindowColor(safeColor("ToolTip.background", new Color(0x323232))); scriptArea.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { char keyChar = e.getKeyChar(); - if (Character.isLetterOrDigit(keyChar)) { + if (Character.isLetterOrDigit(keyChar) || keyChar == '.' || keyChar == '_') { SwingUtilities.invokeLater(ac::doCompletion); } } @@ -724,64 +419,91 @@ public class FridaWindow extends WindowsJDialog { DefaultCompletionProvider provider = new DefaultCompletionProvider(); FridaSyntax.addFridaCompletions(provider); FridaSyntax.addBasicCompletions(provider); + + String[] extras = new String[]{ + "Java.perform", "Interceptor.attach", "Module.findExportByName", + "Memory.readUtf8String", "Memory.writeUtf8String", "ObjC.classes", + "Process.enumerateModulesSync", "Thread.backtrace", "send", "recv", + "console.log", "setTimeout", "setInterval", "clearInterval" + }; + for (String s : extras) { + provider.addCompletion(new ShorthandCompletion(provider, s, s + "()", s)); + } return provider; } + // ---------- 注入处理 ---------- private void handleInject(ActionEvent e) { try { - long pid = Long.parseLong(pidField.getText()); + String pidText = pidField.getText().trim(); + if (pidText.isEmpty()) { + JOptionPane.showMessageDialog(this, language.getText("fridaWindow.error.noPid"), language.getText("fridaWindow.error.title"), JOptionPane.ERROR_MESSAGE); + return; + } + long pid = Long.parseLong(pidText); String script = scriptArea.getText(); Frida frida = new Frida(script, pid); if (!isRepetition) { - try { - frida.run(() -> { - }).execute(frida1 -> frida1.addCallbackMessage(message -> - SwingUtilities.invokeLater(() -> { - logger.info("[LOG] {}", message); - logArea.append("[LOG] " + message + "\n"); - }) - )).start(); - } catch (Exception ex) { - logger.error("Injection failure", ex); - throw new RuntimeException(ex); - } + frida.run(() -> { + }).execute(frida1 -> frida1.addCallbackMessage(message -> + SwingUtilities.invokeLater(() -> { + logger.info("[LOG] {}", message); + logArea.append("[LOG] " + message + "\n"); + }) + )).start(); isRepetition = true; } else { - try { - frida.run(() -> { - }).execute(frida12 -> { - }).start(); - } catch (Exception ex) { - logger.error("Injection failure", ex); - } + frida.run(() -> { + }).execute(frida12 -> { + }).start(); } - } catch (NumberFormatException ex) { logger.error("无效的进程ID", ex); - JOptionPane.showMessageDialog(this, "无效的进程ID", "错误", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, language.getText("fridaWindow.error.invalidPid"), language.getText("fridaWindow.error.title"), JOptionPane.ERROR_MESSAGE); + } catch (Exception ex) { + logger.error("Injection failure", ex); + JOptionPane.showMessageDialog(this, language.getText("fridaWindow.error.injectFail") + ": " + ex.getMessage(), language.getText("fridaWindow.error.title"), JOptionPane.ERROR_MESSAGE); } } + // ---------- 进程选择窗口 ---------- private void openProcessSelectionWindow(ActionEvent e) { ProcessSelectionWindow selectionWindow = new ProcessSelectionWindow(this); selectionWindow.setVisible(true); } + private void refreshProcessCombo() { + SwingUtilities.invokeLater(() -> { + processCombo.removeAllItems(); + List processes = getProcesses(); + for (ProcessInfo p : processes) processCombo.addItem(p); + processCombo.setSelectedIndex(-1); + processCombo.setToolTipText(language.getText("fridaWindow.process.tooltip")); + processCombo.addActionListener(ev -> { + ProcessInfo selected = (ProcessInfo) processCombo.getSelectedItem(); + if (selected != null) pidField.setText(String.valueOf(selected.pid())); + }); + }); + } + + // ---------- ProcessSelectionWindow 内部类 ---------- private static class ProcessSelectionWindow extends JDialog { private List processList; private JTable table; public ProcessSelectionWindow(Window owner) { super(owner, language.getText("fridaWindow.processSelectionWindow"), ModalityType.APPLICATION_MODAL); - setSize(600, 400); + setSize(640, 420); setLocationRelativeTo(owner); initializeUI(); } private void initializeUI() { - JPanel panel = new JPanel(new BorderLayout(5, 5)); + Color panelBg = UIManager.getColor("Panel.background"); + if (panelBg == null) panelBg = new Color(0x2B2B2B); + JPanel panel = new JPanel(new BorderLayout(8, 8)); panel.setBorder(new EmptyBorder(10, 10, 10, 10)); - panel.setBackground(new Color(0x2B2B2B)); + panel.setBackground(panelBg); JTextField searchField = new JTextField(20); styleSearchField(searchField); @@ -791,7 +513,7 @@ public class FridaWindow extends WindowsJDialog { styleTable(table); JScrollPane scrollPane = new JScrollPane(table); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0x3C3F41))); + scrollPane.setBorder(BorderFactory.createLineBorder(panelBg.darker())); JPopupMenu contextMenu = createContextMenu(); table.addMouseListener(new MouseAdapter() { @@ -811,7 +533,7 @@ public class FridaWindow extends WindowsJDialog { private DefaultTableModel createTableModel() { processList = getProcesses(); - String[] columns = {"进程名称", "进程ID"}; + String[] columns = {language.getText("fridaWindow.process.name"), language.getText("fridaWindow.process.pid")}; Object[][] data = getTableData(processList); return new DefaultTableModel(data, columns) { @Override @@ -824,7 +546,8 @@ public class FridaWindow extends WindowsJDialog { private void injectSelectedProcess() { int selectedRow = table.getSelectedRow(); if (selectedRow != -1) { - long pid = (Long) table.getModel().getValueAt(selectedRow, 1); + Object val = table.getModel().getValueAt(selectedRow, 1); + long pid = val instanceof Long ? (Long) val : Long.parseLong(String.valueOf(val)); ((FridaWindow) getOwner()).pidField.setText(String.valueOf(pid)); dispose(); } @@ -832,10 +555,10 @@ public class FridaWindow extends WindowsJDialog { private void styleSearchField(JTextField field) { field.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - field.putClientProperty("JTextField.placeholderText", "输入进程名搜索..."); + field.putClientProperty("JTextField.placeholderText", language.getText("fridaWindow.search.placeholder")); field.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(new Color(0x515658)), - BorderFactory.createEmptyBorder(5, 8, 5, 8) + BorderFactory.createEmptyBorder(6, 8, 6, 8) )); } @@ -844,14 +567,11 @@ public class FridaWindow extends WindowsJDialog { table.setIntercellSpacing(new Dimension(0, 0)); table.setShowGrid(false); table.setSelectionBackground(new Color(0x214283)); - - // 自定义渲染器 table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, - isSelected, hasFocus, row, column); + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); c.setBackground(row % 2 == 0 ? new Color(0x323232) : new Color(0x2B2B2B)); c.setForeground(Color.WHITE); return c; @@ -861,7 +581,7 @@ public class FridaWindow extends WindowsJDialog { private JPopupMenu createContextMenu() { JPopupMenu menu = new JPopupMenu(); - JMenuItem injectItem = new JMenuItem("注入选中进程"); + JMenuItem injectItem = new JMenuItem(language.getText("fridaWindow.processContext.inject")); injectItem.addActionListener(e -> injectSelectedProcess()); menu.add(injectItem); return menu; @@ -876,24 +596,21 @@ public class FridaWindow extends WindowsJDialog { return data; } - /** - * 执行cmd命令获取所有进程信息 - * - * @return 进程信息列表 - */ private List getProcesses() { List processList = new ArrayList<>(); try { 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)); + try { + long pid = Long.parseLong(columns[1].replaceAll("\"", "").trim()); + String processName = columns[0].replaceAll("\"", "").trim(); + processList.add(new ProcessInfo(pid, processName)); + } catch (NumberFormatException ignore) { + } } } reader.close(); @@ -905,9 +622,327 @@ public class FridaWindow extends WindowsJDialog { } private record ProcessInfo(long pid, String name) { + @Override + public String toString() { + return name + " (" + pid + ")"; + } } - public static void main(String[] args) { + private List getProcesses() { + List list = new ArrayList<>(); + try { + 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) { + try { + long pid = Long.parseLong(columns[1].replaceAll("\"", "").trim()); + String processName = columns[0].replaceAll("\"", "").trim(); + list.add(new ProcessInfo(pid, processName)); + } catch (NumberFormatException ignore) { + } + } + } + reader.close(); + } catch (IOException e) { + logger.error("Failed to get process information ", e); + } + return list; + } + // ---------- 文件打开/保存 ---------- + private void openFile() { + AdvancedJFileChooser fileChooser = new AdvancedJFileChooser(); + fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter(language.getText("fridaWindow.fileFilter"), "frida")); + int result = fileChooser.showOpenDialog(this); + if (result == JFileChooser.APPROVE_OPTION) { + try (BufferedReader reader = new BufferedReader(new FileReader(fileChooser.getSelectedFile()))) { + String pid = reader.readLine(); + StringBuilder script = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + script.append(line).append("\n"); + } + pidField.setText(pid); + scriptArea.setText(script.toString()); + } catch (IOException ex) { + logger.error("文件打开失败", ex); + JOptionPane.showMessageDialog(this, language.getText("fridaWindow.error.open") + ": " + ex.getMessage(), language.getText("fridaWindow.error.title"), JOptionPane.ERROR_MESSAGE); + } + } + } + + private void saveFile() { + AdvancedJFileChooser fileChooser = new AdvancedJFileChooser(); + fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter(language.getText("fridaWindow.fileFilter"), "frida")); + int result = fileChooser.showSaveDialog(this); + if (result == JFileChooser.APPROVE_OPTION) { + File file = fileChooser.getSelectedFile(); + if (!file.getName().toLowerCase().endsWith(".frida")) { + file = new File(file.getParentFile(), file.getName() + ".frida"); + } + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + writer.write(pidField.getText()); + writer.newLine(); + writer.write(scriptArea.getText()); + } catch (IOException ex) { + logger.error("文件保存失败", ex); + JOptionPane.showMessageDialog(this, language.getText("fridaWindow.error.save") + ": " + ex.getMessage(), language.getText("fridaWindow.error.title"), JOptionPane.ERROR_MESSAGE); + } + } + } + + // ---------- 设置窗口(颜色/字体) ---------- + private void settingsWindow() { + settingsDialog = new JDialog(this, language.getText("fridaWindow.settings.title"), true); + settingsDialog.setSize(620, 480); + settingsDialog.setLocationRelativeTo(this); + + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(new EmptyBorder(12, 12, 12, 12)); + mainPanel.setBackground(safeColor("Panel.background", new Color(0x2B2B2B))); + + JTabbedPane tabbedPane = new JTabbedPane(); + JPanel colorPanel = createColorSettingsPanel(); + JPanel fontPanel = createFontSettingsPanel(); + tabbedPane.addTab(language.getText("fridaWindow.settings.tab.color"), colorPanel); + tabbedPane.addTab(language.getText("fridaWindow.settings.tab.font"), fontPanel); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.setOpaque(false); + JButton saveBtn = createModernButton(language.getText("fridaWindow.settings.save"), safeColor("control", new Color(0x6897BB)), safeColor("textText", Color.WHITE), UIManager.getFont("Label.font")); + JButton defaultBtn = createModernButton(language.getText("fridaWindow.settings.default"), new Color(0x808080), safeColor("textText", Color.WHITE), UIManager.getFont("Label.font")); + JButton cancelBtn = createModernButton(language.getText("fridaWindow.settings.cancel"), new Color(0xD25252), safeColor("textText", Color.WHITE), UIManager.getFont("Label.font")); + + saveBtn.addActionListener(a -> applyAndSaveSettings()); + defaultBtn.addActionListener(a -> restoreDefaultSettings()); + cancelBtn.addActionListener(a -> settingsDialog.dispose()); + + buttonPanel.add(defaultBtn); + buttonPanel.add(cancelBtn); + buttonPanel.add(saveBtn); + + mainPanel.add(tabbedPane, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + settingsDialog.setContentPane(mainPanel); + settingsDialog.setVisible(true); + } + + private JPanel createColorSettingsPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setOpaque(false); + + Map tokenConfig = new LinkedHashMap<>(); + tokenConfig.put(language.getText("fridaWindow.syntax.keyword"), Token.RESERVED_WORD); + tokenConfig.put(language.getText("fridaWindow.syntax.stdlib"), Token.RESERVED_WORD_2); + tokenConfig.put(language.getText("fridaWindow.syntax.builtin"), Token.FUNCTION); + tokenConfig.put(language.getText("fridaWindow.syntax.string"), Token.LITERAL_STRING_DOUBLE_QUOTE); + tokenConfig.put(language.getText("fridaWindow.syntax.number"), Token.LITERAL_NUMBER_DECIMAL_INT); + tokenConfig.put(language.getText("fridaWindow.syntax.comment"), Token.COMMENT_EOL); + tokenConfig.put(language.getText("fridaWindow.syntax.operator"), Token.OPERATOR); + tokenConfig.put(language.getText("fridaWindow.syntax.identifier"), Token.IDENTIFIER); + tokenConfig.put(language.getText("fridaWindow.syntax.separator"), Token.SEPARATOR); + tokenConfig.put(language.getText("fridaWindow.syntax.regex"), Token.REGEX); + + DefaultTableModel model = new DefaultTableModel(new Object[]{language.getText("fridaWindow.col.syntax"), language.getText("fridaWindow.col.color")}, 0); + SyntaxScheme scheme = scriptArea.getSyntaxScheme(); + for (Map.Entry entry : tokenConfig.entrySet()) { + Style style = scheme.getStyle(entry.getValue()); + Color c = style != null && style.foreground != null ? style.foreground : safeColor("textText", Color.WHITE); + model.addRow(new Object[]{entry.getKey(), c}); + } + + JTable colorTable = new JTable(model); + colorTable.setRowHeight(30); + colorTable.getColumnModel().getColumn(1).setCellRenderer(new ColorRenderer()); + colorTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + int column = colorTable.columnAtPoint(e.getPoint()); + if (e.getClickCount() == 1 && column == 1) { + int row = colorTable.rowAtPoint(e.getPoint()); + Color current = (Color) colorTable.getValueAt(row, 1); + Color newColor = JColorChooser.showDialog(settingsDialog, language.getText("fridaWindow.chooseColor"), current); + if (newColor != null) colorTable.setValueAt(newColor, row, 1); + } + } + }); + + JScrollPane scrollPane = new JScrollPane(colorTable); + panel.add(scrollPane, BorderLayout.CENTER); + panel.putClientProperty("tokenConfig", tokenConfig); + panel.putClientProperty("colorTable", colorTable); + return panel; + } + + private JPanel createFontSettingsPanel() { + JPanel panel = new JPanel(new GridLayout(0, 2, 10, 10)); + panel.setOpaque(false); + + String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); + JComboBox fontCombo = new JComboBox<>(fontNames); + fontCombo.setSelectedItem(scriptArea.getFont().getFamily()); + + JCheckBox boldCheck = new JCheckBox(language.getText("fridaWindow.font.bold")); + JCheckBox italicCheck = new JCheckBox(language.getText("fridaWindow.font.italic")); + boldCheck.setOpaque(false); + italicCheck.setOpaque(false); + + SpinnerModel sizeModel = new SpinnerNumberModel(scriptArea.getFont().getSize(), 8, 48, 1); + JSpinner sizeSpinner = new JSpinner(sizeModel); + + panel.add(new JLabel(language.getText("fridaWindow.font.name"))); + panel.add(fontCombo); + panel.add(new JLabel(language.getText("fridaWindow.font.size"))); + panel.add(sizeSpinner); + panel.add(new JLabel(language.getText("fridaWindow.font.style"))); + JPanel stylePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + stylePanel.setOpaque(false); + stylePanel.add(boldCheck); + stylePanel.add(italicCheck); + panel.add(stylePanel); + + JTextArea preview = new JTextArea(language.getText("fridaWindow.font.preview")); + preview.setFont(scriptArea.getFont()); + preview.setEditable(false); + preview.setBackground(scriptArea.getBackground()); + preview.setForeground(scriptArea.getForeground()); + + fontCombo.addActionListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); + sizeSpinner.addChangeListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); + boldCheck.addActionListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); + italicCheck.addActionListener(e -> updateFontPreview(fontCombo, sizeSpinner, boldCheck, italicCheck, preview)); + + panel.add(new JLabel(language.getText("fridaWindow.font.previewLabel"))); + panel.add(new JScrollPane(preview)); + + panel.putClientProperty("fontCombo", fontCombo); + panel.putClientProperty("sizeSpinner", sizeSpinner); + panel.putClientProperty("boldCheck", boldCheck); + panel.putClientProperty("italicCheck", italicCheck); + return panel; + } + + private void updateFontPreview(JComboBox fontCombo, JSpinner sizeSpinner, + JCheckBox boldCheck, JCheckBox italicCheck, JTextArea preview) { + String fontName = (String) fontCombo.getSelectedItem(); + int size = (Integer) sizeSpinner.getValue(); + int style = Font.PLAIN; + if (boldCheck.isSelected()) style |= Font.BOLD; + if (italicCheck.isSelected()) style |= Font.ITALIC; + Font newFont = new Font(fontName, style, size); + preview.setFont(newFont); + } + + private void applyAndSaveSettings() { + JPanel colorPanel = (JPanel) ((JTabbedPane) settingsDialog.getContentPane().getComponent(0)).getComponent(0); + Map tokenConfig = (Map) colorPanel.getClientProperty("tokenConfig"); + JTable colorTable = (JTable) colorPanel.getClientProperty("colorTable"); + SyntaxScheme scheme = scriptArea.getSyntaxScheme(); + for (int i = 0; i < colorTable.getRowCount(); i++) { + String tokenName = (String) colorTable.getValueAt(i, 0); + Color color = (Color) colorTable.getValueAt(i, 1); + Integer tokenType = tokenConfig.get(tokenName); + if (tokenType != null) { + Style s = scheme.getStyle(tokenType); + if (s == null) s = new Style(); + s.foreground = color; + scheme.setStyle(tokenType, s); + } + } + JPanel fontPanel = (JPanel) ((JTabbedPane) settingsDialog.getContentPane().getComponent(0)).getComponent(1); + JComboBox fontCombo = (JComboBox) fontPanel.getClientProperty("fontCombo"); + JSpinner sizeSpinner = (JSpinner) fontPanel.getClientProperty("sizeSpinner"); + JCheckBox boldCheck = (JCheckBox) fontPanel.getClientProperty("boldCheck"); + JCheckBox italicCheck = (JCheckBox) fontPanel.getClientProperty("italicCheck"); + String fontName = (String) fontCombo.getSelectedItem(); + int size = (Integer) sizeSpinner.getValue(); + int style = Font.PLAIN; + if (boldCheck.isSelected()) style |= Font.BOLD; + if (italicCheck.isSelected()) style |= Font.ITALIC; + Font newFont = new Font(fontName, style, size); + scriptArea.setFont(newFont); + Preferences prefs = Preferences.userNodeForPackage(FridaWindow.class); + prefs.putInt("editor.font.style", style); + prefs.putInt("editor.font.size", size); + prefs.put("editor.font.name", fontName); + settingsDialog.dispose(); + scriptArea.repaint(); + } + + private void restoreDefaultSettings() { + scriptArea.setFont(new Font("JetBrains Mono", Font.PLAIN, 13)); + configureIDEATheme(new SyntaxScheme(true)); + Preferences prefs = Preferences.userNodeForPackage(FridaWindow.class); + prefs.remove("editor.font.name"); + prefs.remove("editor.font.style"); + prefs.remove("editor.font.size"); + settingsDialog.dispose(); + scriptArea.repaint(); + } + + // ---------- 颜色表渲染器 ---------- + private static class ColorRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (value instanceof Color) { + setText(""); + setBackground((Color) value); + setBorder(BorderFactory.createLineBorder(Color.GRAY)); + } + return c; + } + } + + // ---------- About 框 ---------- + private void showAboutDialog() { + JDialog aboutDialog = new JDialog(this, language.getText("fridaWindow.about.title"), true); + aboutDialog.setSize(340, 220); + aboutDialog.setLocationRelativeTo(this); + + JPanel content = new JPanel(new BorderLayout()); + content.setBorder(new EmptyBorder(16, 16, 16, 16)); + content.setBackground(safeColor("Panel.background", new Color(0x2B2B2B))); + + JLabel title = new JLabel("Frida Injector v2.0"); + title.setFont(UIManager.getFont("Label.font") != null ? UIManager.getFont("Label.font").deriveFont(Font.BOLD, 16f) : new Font("Segoe UI", Font.BOLD, 16)); + title.setForeground(safeColor("textText", Color.WHITE)); + + JTextArea info = new JTextArea(language.getText( "fridaWindow.about.content")); + info.setEditable(false); + info.setOpaque(false); + info.setForeground(safeColor("textText", Color.WHITE)); + info.setFont(UIManager.getFont("Label.font") != null ? UIManager.getFont("Label.font").deriveFont(13f) : new Font("Microsoft YaHei", Font.PLAIN, 12)); + + content.add(title, BorderLayout.NORTH); + content.add(info, BorderLayout.CENTER); + + aboutDialog.setContentPane(content); + aboutDialog.setVisible(true); + } + + // ---------- 进程表格、弹窗相关 ---------- + private JPopupMenu createContextMenu() { + JPopupMenu menu = new JPopupMenu(); + JMenuItem injectItem = new JMenuItem(language.getText("fridaWindow.processContext.inject")); + injectItem.addActionListener(e -> { + // 由 ProcessSelectionWindow 处理 + }); + menu.add(injectItem); + return menu; + } + + // ---------- 入口 ---------- + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + JFrame f = new JFrame(); + FridaWindow w = new FridaWindow(f); + w.setVisible(true); + }); } }