我正在开发一个用 Java 编写的 Burp Suite 插件(我的第一个)。不会撒谎,自从我完成任何 Java 工作以来,这是一个热门时刻,所以我认为对这个问题存在根本性的误解。此外,代码目前非常混乱,因为我一直在尝试这个和尝试那个。我所做的一切都是基于这样一个前提:Java 在通过基于此处提出的问题和答案的方法传递变量时,本质上传递的是指针的等效项:Java 是“按引用传递”还是“按值传递” ?.
此插件的基本前提是,它将搜索文本的请求或响应,并允许您替换为预定义值、基于正则表达式的搜索,或动态发送请求并搜索该响应。这个插件的第一步只是一个规则集,一切都运行得很好。我一直在尝试更新插件以允许任意数量的规则并通过 ArrayList 处理规则集。这就是问题开始的地方。我已经包含了相关的代码片段,但我可能错过了一些试图将其精简为可读部分的地方。 BurpPlugin 类是 BurpSuite 的入口点。 MainWindow 包含我的 JTabbedPane,基本上是中央站。
public class BurpPlugin implements BurpExtension {
private MontoyaApi api;
@Override
public void initialize(MontoyaApi api)
{
this.api = api;
api.extension().setName("Burp Plugin");
// list of tabs
ArrayList<Tab> tabs = new ArrayList<>();
// will need to create a new main tab that will have the tabbed top with a button for new tab
// the rest of the window will contain replacer tabs
Tab aTab = new Tab(this.api);
tabs.add(firstTab);
MainWindow mainWindow = new MainWindow(this.api, tabs);
this.api.userInterface().registerSuiteTab("Burp Plugin", mainWindow.getTabUI());
this.api.http().registerHttpHandler(new MyHttpHandler(this.api, tabs)); // <--- Tabs here does not get updated arraylist, only the first tab created during init.
}
}
public class MainWindow {
private final MontoyaApi api;
private final Logging logging;
private ArrayList<Tab> tabs;
...
public MainWindow(MontoyaApi api, ArrayList<Tab> allTabs) {
this.api = api;
this.logging = api.logging();
this.tabs = allTabs;
logging.logToOutput("mw init: " + String.valueOf(allTabs.size()));
}
public Component getTabUI()
{
...
for(int i = 0; i < this.tabs.size(); i++) {
String title = Integer.toString(i+1);
tabPanel.addTab(title, this.tabs.get(i).getTabUI());
this.createCloseButton(this, tabPanel, i);
}
this.createAddButton(this, tabPanel);
return tabPanel;
}
private void createAddButton(MainWindow main, JTabbedPane tabPanel) {
...
ActionListener listener = e -> {
Tab newTab = new Tab(main.api);
main.tabs.add(newTab);
logging.logToOutput("cab: " + String.valueOf(main.tabs.size())); // <--- This counts as expected
String title = String.valueOf(main.tabs.size());
tabPanel.insertTab(title, null, newTab.getTabUI(), null, tabPanel.getTabCount()-1);
// logging.logToOutput("getTabCount-1: " + String.valueOf(tabPanel.getTabCount()-1));
// logging.logToOutput("tabs.size: " + String.valueOf(main.tabs.size()));
main.createCloseButton(main, tabPanel, main.tabs.size()-POSTTABS);
};
addTab.addActionListener (listener);
tabPanel.addTab ("addbutton", null);
tabPanel.setTabComponentAt (tabPanel.getTabCount() - 1, pnlTab);
}
private JPanel getHelpSettingsPaneUI() {
GridBagConstraints tpc = new GridBagConstraints();
JPanel toolPanel = new JPanel();
toolPanel.setLayout(new GridBagLayout());
toolPanel.setBorder(BorderFactory.createTitledBorder(SETTINGS_PANE_TITLE));
...
// add btnExportCfg
btnExportCfg.addActionListener(new ExportActionListener(this.api, this));
tpc.fill = GridBagConstraints.HORIZONTAL;
tpc.gridwidth = 2;
tpc.gridx = 0;
tpc.gridy += 1;
tpc.insets = new Insets(0, 10, 10, 10);
toolPanel.add(btnExportCfg, tpc);
return toolPanel;
}
}
public class ExportActionListener implements ActionListener {
private MontoyaApi api;
private ArrayList<ReplacerTab> tabs;
private MainConfig config;
public ExportActionListener(MontoyaApi api, MainWindow main) {
// this.tabs = tabs;
this.api = api;
// this.config = cfg;
Logging logging = api.logging();
logging.logToOutput("eal:"+String.valueOf(main.getReplacerTabs().size())); <---- This is always 1 from when it is first initialized
}
public void actionPerformed(ActionEvent e) {
this.saveConfiguration(); // <--- This converts most of the items from the main window into a json config for export.
}
}
我尝试过传递各种类,例如 MainWindow 或只是选项卡或设置静态类变量,但即使这样似乎也会遭受相同的结果,因为在导出或拦截期间仅存在 init 期间的第一个添加的规则。我不确定这是否是我对 Java 缺乏了解,因为我对它不起作用或者在 Burp 中作为插件使用是否是一个因素或两者的组合并不感到惊讶。
现在,因为我理解 Java 本质上是传递指针,所以我的期望是可以在入口点创建 ArrayList,然后在整个应用程序中传递指针。当前的问题是当使用 JTabbedPane 使用 MainWindow 中 createAddButton 方法中的内联 ActionListener 创建更多规则选项卡时。选项卡已创建并完美编号,关闭按钮将关闭相应的选项卡,并且 UI 通常按预期工作。但是,当尝试使用 MyHttpHandler 或 btnExportCfg 的 ActionListener 时,动态添加的规则选项卡均不可用,并且选项卡仅包含插件初始化期间添加的第一个选项卡。
已更新可建造
构建.gradle
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compileOnly 'net.portswigger.burp.extensions:montoya-api:latest.release'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.2'
}
test {
useJUnitPlatform()
}
jar {
from {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
测试规则.java
package org.example;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import java.util.ArrayList;
public class TestRules implements BurpExtension {
private MontoyaApi api;
public void initialize(MontoyaApi api)
{
this.api = api;
api.extension().setName("Test Rules");
// list of tabs
ArrayList<Tab> tabs = new ArrayList<>();
// will need to create a new main tab that will have the tabbed top with a button for new tab
// the rest of the window will contain replacer tabs
Tab firstTab = new Tab(this.api);
tabs.add(firstTab);
MainWindow mainWindow = new MainWindow(this.api, tabs);
this.api.userInterface().registerSuiteTab("Test Rules", mainWindow.getTabUI());
this.api.http().registerHttpHandler(new MyHttpHandler(this.api, tabs));
}
}
主窗口.java
package org.example;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.logging.Logging;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
public class MainWindow {
private final MontoyaApi api;
private final Logging logging;
private ArrayList<Tab> tabs;
private final int PRETABS = 1; // num of tabs before first replacer
private final int POSTTABS = 1; //num of tabs after last replacer
//matchPane Controls
private JButton btnImportCfg = new JButton("Import Config");
private JButton btnExportCfg = new JButton("Export Config");
public MainWindow(MontoyaApi api, ArrayList<Tab> allTabs) {
this.api = api;
this.logging = api.logging();
this.tabs = allTabs;
logging.logToOutput("mw init: " + String.valueOf(allTabs.size()));
}
public ArrayList<Tab> getTabs() { return this.tabs; }
public Component getTabUI()
{
MainWindow that = this;
JTabbedPane tabPanel = new JTabbedPane();
tabPanel.addTab("help", this.getSettingsTabUI());
for(int i = 0; i < this.tabs.size(); i++) {
String title = Integer.toString(i+1);
tabPanel.addTab(title, this.tabs.get(i).getTabUI());
this.createCloseButton(this, tabPanel, i);
}
this.createAddButton(this, tabPanel);
return tabPanel;
}
private void createCloseButton(MainWindow main, JTabbedPane tabPanel, int i) {
String title = Integer.toString(i+1);
JPanel pnlTab = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2));
pnlTab.setOpaque(false);
JLabel lblTitle = new JLabel(title);
lblTitle.setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4));
JButton btnClose = new JButton("x");
btnClose.setBorder(null);
btnClose.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
btnClose.setContentAreaFilled(false);
// btnClose.setBorderPainted(false);
// btnClose.setMargin(new Insets(2,2,2,2));
ActionListener listener = e -> {
// logging.logToOutput("remove i: " + String.valueOf(i));
if (i >= 0) {
main.tabs.remove(i);
tabPanel.removeTabAt(i+PRETABS);
for(int j = i; j < main.tabs.size(); j++) {
this.createCloseButton(main, tabPanel, j);
}
}
};
btnClose.addActionListener(listener);
pnlTab.add(lblTitle);
pnlTab.add(btnClose);
tabPanel.setTabComponentAt(i+1, pnlTab);
}
private void createAddButton(MainWindow main, JTabbedPane tabPanel) {
FlowLayout f = new FlowLayout(FlowLayout.CENTER, 5, 0);
// Make a small JPanel with the layout and make it non-opaque
JPanel pnlTab = new JPanel (f);
pnlTab.setOpaque (false);
// Create a JButton for adding the tabs
JButton addTab = new JButton ("+");
addTab.setOpaque (false); //
addTab.setBorder (null);
addTab.setContentAreaFilled (false);
addTab.setFocusPainted (false);
addTab.setFocusable (false);
pnlTab.add (addTab);
ActionListener listener = e -> {
Tab newTab = new Tab(main.api);
main.tabs.add(newTab);
logging.logToOutput("cab: " + String.valueOf(main.tabs.size()));
String title = String.valueOf(main.tabs.size());
tabPanel.insertTab(title, null, newTab.getTabUI(), null, tabPanel.getTabCount()-1);
// logging.logToOutput("getTabCount-1: " + String.valueOf(tabPanel.getTabCount()-1));
// logging.logToOutput("tabs.size: " + String.valueOf(main.tabs.size()));
main.createCloseButton(main, tabPanel, main.tabs.size()-POSTTABS);
};
addTab.addActionListener (listener);
tabPanel.addTab ("addbutton", null);
tabPanel.setTabComponentAt (tabPanel.getTabCount() - 1, pnlTab);
}
private JPanel getSettingsTabUI() {
GridBagConstraints tpc = new GridBagConstraints();
JPanel toolPanel = new JPanel();
toolPanel.setLayout(new GridBagLayout());
// add btnImportCfg
// btnImportCfg.addActionListener(new ImportActionListener(this.api, new MainConfig(this.api, this)));
tpc.fill = GridBagConstraints.HORIZONTAL;
tpc.weightx = 1.0;
tpc.gridwidth = 2;
tpc.gridx = 0;
tpc.gridy += 1;
tpc.insets = new Insets(0, 10, 10, 10);
toolPanel.add(btnImportCfg, tpc);
// add btnExportCfg
btnExportCfg.addActionListener(new ExportActionListener(this.api, this));
tpc.fill = GridBagConstraints.HORIZONTAL;
tpc.gridwidth = 2;
tpc.gridx = 0;
tpc.gridy += 1;
tpc.insets = new Insets(0, 10, 10, 10);
toolPanel.add(btnExportCfg, tpc);
return toolPanel;
}
}
Tab.java
package org.example;
import burp.api.montoya.MontoyaApi;
import javax.swing.*;
import java.awt.*;
public class Tab {
private MontoyaApi api;
//matchPane Controls
private JCheckBox chkEnabled= new JCheckBox("Enabled");
public Tab(MontoyaApi api)
{
this.api = api;
}
public JPanel getTabUI()
{
JPanel matchPanel = new JPanel();
matchPanel.setLayout(new GridBagLayout());
matchPanel.setBorder(BorderFactory.createTitledBorder("Stuff"));
GridBagConstraints tpc = new GridBagConstraints();
//add chkEnabled
tpc.fill = GridBagConstraints.HORIZONTAL;
tpc.weightx = 1.0;
tpc.gridwidth = 2;
tpc.gridx = 0;
tpc.gridy = 0;
tpc.insets = new Insets(0,10,20,10);
matchPanel.add(chkEnabled, tpc);
return matchPanel;
}
}
MyHttpHandler.java
package org.example;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Annotations;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.logging.Logging;
import java.util.ArrayList;
public class MyHttpHandler implements HttpHandler
{
private final MontoyaApi api;
private final Logging logging;
private final ArrayList<Tab> tabs;
public MyHttpHandler(MontoyaApi api, ArrayList<Tab> allTabs)
{
this.api = api;
this.logging = api.logging();
this.tabs = allTabs;
}
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent)
{
HttpRequest modifiedRequest = requestToBeSent;
Annotations annotations = requestToBeSent.annotations();
boolean was_modified = false;
// for tab in this.tabs
logging.logToOutput("handleRequest size:" + String.valueOf(this.tabs.size()));
if (was_modified) {
annotations = annotations.withNotes("Request was modified with Super Replacer");
}
return RequestToBeSentAction.continueWith(modifiedRequest, annotations);
}
@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived)
{
Annotations annotations = responseReceived.annotations();
String bodyAsStr = responseReceived.bodyToString();
boolean was_modified = false;
logging.logToOutput("handleResponse size:" + String.valueOf(this.tabs.size()));
if (was_modified) {
annotations = annotations.withNotes("Request was modified with Super Replacer");
}
return ResponseReceivedAction.continueWith(responseReceived.withBody(bodyAsStr), annotations);
}
}
导出ActionListener.java
package org.example;
import javax.swing.*;
import java.awt.event.*;
import java.io.FileWriter;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.ArrayList;
import burp.api.montoya.MontoyaApi;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.core.JsonProcessingException;
import burp.api.montoya.logging.Logging;
public class ExportActionListener implements ActionListener {
private MontoyaApi api;
private ArrayList<Tab> tabs;
Logging logging;
public ExportActionListener(MontoyaApi api, MainWindow main) {
this.tabs = main.getTabs();
this.api = api;
this.logging = api.logging();
logging.logToOutput("eal:"+String.valueOf(main.getTabs().size()));
}
public void actionPerformed(ActionEvent e) {
logging.logToOutput("eal performed:"+String.valueOf(this.tabs.size()));
this.saveConfiguration();
}
private Boolean saveConfiguration()
{
return true;
}
}
因此,在发布(工作)最小构建之后,喝了一杯好喝的威士忌,并清理和构建了该项目几次。
你瞧,它也起作用了。我不知道它为什么坏了或者是什么修复了它。如果我发现了,我会更新。