Version: 2.5.6 Update

This commit is contained in:
gh0stkey
2023-11-07 11:15:20 +08:00
parent 0225c00f69
commit bcb5177b54
6 changed files with 520 additions and 360 deletions

View File

@@ -4,6 +4,7 @@ import burp.config.ConfigLoader;
import burp.core.processor.ColorProcessor; import burp.core.processor.ColorProcessor;
import burp.core.processor.MessageProcessor; import burp.core.processor.MessageProcessor;
import burp.ui.MainUI; import burp.ui.MainUI;
import burp.ui.board.DatatablePanel;
import burp.ui.board.MessagePanel; import burp.ui.board.MessagePanel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
@@ -38,7 +39,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
new ConfigLoader(); new ConfigLoader();
String version = "2.5.5"; String version = "2.5.6";
callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version)); callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 定义输出 // 定义输出
@@ -217,7 +218,8 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
class MarkInfoTab implements IMessageEditorTab { class MarkInfoTab implements IMessageEditorTab {
private final JTabbedPane jTabbedPane = new JTabbedPane(); private final JTabbedPane jTabbedPane = new JTabbedPane();
private JTable jTable = new JTable(); private DatatablePanel dataPanel;
private JTable dataTable;
private final IMessageEditorController controller; private final IMessageEditorController controller;
private Map<String, String> extractRequestMap; private Map<String, String> extractRequestMap;
private Map<String, String> extractResponseMap; private Map<String, String> extractResponseMap;
@@ -237,10 +239,10 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
jTabbedPane.addChangeListener(new ChangeListener() { jTabbedPane.addChangeListener(new ChangeListener() {
@Override @Override
public void stateChanged(ChangeEvent arg0) { public void stateChanged(ChangeEvent arg0) {
jTable = (JTable) ((JScrollPane)jTabbedPane.getSelectedComponent()).getViewport().getView(); dataTable = ((DatatablePanel)jTabbedPane.getSelectedComponent()).getTable();
} }
}); });
return this.jTabbedPane; return jTabbedPane;
} }
@Override @Override
@@ -280,10 +282,10 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
*/ */
@Override @Override
public byte[] getSelectedData() { public byte[] getSelectedData() {
int[] selectRows = jTable.getSelectedRows(); int[] selectRows = dataTable.getSelectedRows();
StringBuilder selectData = new StringBuilder(); StringBuilder selectData = new StringBuilder();
for (int row : selectRows) { for (int row : selectRows) {
selectData.append(jTable.getValueAt(row, 0).toString()).append("\n"); selectData.append(dataTable.getValueAt(row, 0).toString()).append("\n");
} }
// 便于单行复制,去除最后一个换行符 // 便于单行复制,去除最后一个换行符
String revData = selectData.reverse().toString().replaceFirst("\n", ""); String revData = selectData.reverse().toString().replaceFirst("\n", "");
@@ -310,18 +312,12 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
*/ */
public void makeTable(Map<String, String> dataMap) { public void makeTable(Map<String, String> dataMap) {
ArrayList<String> lTitleList = new ArrayList<>(); ArrayList<String> lTitleList = new ArrayList<>();
dataMap.keySet().forEach(i->{ dataMap.keySet().forEach(i->{
String[] extractData = dataMap.get(i).split("\n"); String[] extractData = dataMap.get(i).split("\n");
Object[][] data = new Object[extractData.length][1];
for (int x = 0; x < extractData.length; x++) {
data[x][0] = extractData[x];
}
JTable infoTable = new JTable(data, new Object[]{"Information"});
infoTable.setAutoCreateRowSorter(true);
JScrollPane jScrollPane = new JScrollPane(infoTable);
lTitleList.add(i); lTitleList.add(i);
this.jTabbedPane.addTab(i, jScrollPane); dataPanel = new DatatablePanel(i, Arrays.asList(extractData));
jTabbedPane.addTab(i, dataPanel);
}); });
/* /*
@@ -329,9 +325,9 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
* 采用全局ArrayList的方式遍历删除Tab以此应对BurpSuite缓存机制导致的MarkInfo UI错误展示。 * 采用全局ArrayList的方式遍历删除Tab以此应对BurpSuite缓存机制导致的MarkInfo UI错误展示。
*/ */
titleList.forEach(t->{ titleList.forEach(t->{
int indexOfTab = this.jTabbedPane.indexOfTab(t); int indexOfTab = jTabbedPane.indexOfTab(t);
if (indexOfTab != -1) { if (indexOfTab != -1) {
this.jTabbedPane.removeTabAt(indexOfTab); jTabbedPane.removeTabAt(indexOfTab);
} }
}); });

View File

@@ -1,6 +1,5 @@
package burp.core.processor; package burp.core.processor;
import burp.BurpExtender;
import burp.core.GlobalCachePool; import burp.core.GlobalCachePool;
import burp.core.utils.HashCalculator; import burp.core.utils.HashCalculator;
import burp.core.utils.MatchTool; import burp.core.utils.MatchTool;
@@ -57,136 +56,127 @@ public class DataProcessingUnit {
} else { } else {
// 最终返回的结果 // 最终返回的结果
Map<String, Map<String, Object>> finalMap = new HashMap<>(); Map<String, Map<String, Object>> finalMap = new HashMap<>();
ConfigEntry.globalRules.keySet().forEach(i -> { ConfigEntry.globalRules.keySet().parallelStream().forEach(i -> {
for (Object[] objects : ConfigEntry.globalRules.get(i)) { for (Object[] objects : ConfigEntry.globalRules.get(i)) {
// 多线程执行,一定程度上减少阻塞现象 // 多线程执行,一定程度上减少阻塞现象
Thread t = new Thread(() -> { String matchContent = "";
String matchContent = ""; // 遍历获取规则
// 遍历获取规则 List<String> result = new ArrayList<>();
List<String> result = new ArrayList<>(); Map<String, Object> tmpMap = new HashMap<>();
Map<String, Object> tmpMap = new HashMap<>();
String name = objects[1].toString(); String name = objects[1].toString();
boolean loaded = (Boolean) objects[0]; boolean loaded = (Boolean) objects[0];
String regex = objects[2].toString(); String regex = objects[2].toString();
String color = objects[3].toString(); String color = objects[3].toString();
String scope = objects[4].toString(); String scope = objects[4].toString();
String engine = objects[5].toString(); String engine = objects[5].toString();
boolean sensitive = (Boolean) objects[6]; boolean sensitive = (Boolean) objects[6];
// 判断规则是否开启与作用域 // 判断规则是否开启与作用域
if (loaded && (scope.contains(scopeString) || scope.contains("any"))) { if (loaded && (scope.contains(scopeString) || scope.contains("any"))) {
switch (scope) { switch (scope) {
case "any": case "any":
case "request": case "request":
case "response": case "response":
matchContent = new String(content, StandardCharsets.UTF_8); matchContent = new String(content, StandardCharsets.UTF_8);
break; break;
case "any header": case "any header":
case "request header": case "request header":
case "response header": case "response header":
matchContent = headers; matchContent = headers;
break; break;
case "any body": case "any body":
case "request body": case "request body":
case "response body": case "response body":
matchContent = new String(body, StandardCharsets.UTF_8); matchContent = new String(body, StandardCharsets.UTF_8);
break; break;
default: default:
break; break;
} }
if ("nfa".equals(engine)) { if ("nfa".equals(engine)) {
Pattern pattern; Pattern pattern;
// 判断规则是否大小写敏感 // 判断规则是否大小写敏感
if (sensitive) { if (sensitive) {
pattern = new Pattern(regex); pattern = new Pattern(regex);
} else {
pattern = new Pattern(regex, Pattern.IGNORE_CASE);
}
Matcher matcher = pattern.matcher(matchContent);
while (matcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(matcher.group(1));
}
} else { } else {
RegExp regexp = new RegExp(regex); pattern = new Pattern(regex, Pattern.IGNORE_CASE);
Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
AutomatonMatcher autoMatcher = runAuto.newMatcher(matchContent);
while (autoMatcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(autoMatcher.group());
}
} }
// 去除重复内容 Matcher matcher = pattern.matcher(matchContent);
HashSet tmpList = new HashSet(result); while (matcher.find()) {
result.clear(); // 添加匹配数据至list
result.addAll(tmpList); // 强制用户使用()包裹正则
result.add(matcher.group(1));
}
} else {
RegExp regexp = new RegExp(regex);
Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
AutomatonMatcher autoMatcher = runAuto.newMatcher(matchContent);
while (autoMatcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(autoMatcher.group());
}
}
String nameAndSize = String.format("%s (%s)", name, result.size()); // 去除重复内容
if (!result.isEmpty()) { HashSet tmpList = new HashSet(result);
tmpMap.put("color", color); result.clear();
String dataStr = String.join("\n", result); result.addAll(tmpList);
tmpMap.put("data", dataStr);
finalMap.put(nameAndSize, tmpMap); String nameAndSize = String.format("%s (%s)", name, result.size());
// 添加到全局变量中便于Databoard检索 if (!result.isEmpty()) {
if (!Objects.equals(host, "")) { tmpMap.put("color", color);
List<String> dataList = Arrays.asList(dataStr.split("\n")); String dataStr = String.join("\n", result);
if (ConfigEntry.globalDataMap.containsKey(host)) { tmpMap.put("data", dataStr);
Map<String, List<String>> gRuleMap = new HashMap<>(ConfigEntry.globalDataMap.get(host)); finalMap.put(nameAndSize, tmpMap);
if (gRuleMap.containsKey(name)) { // 添加到全局变量中便于Databoard检索
// gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法 if (!Objects.equals(host, "")) {
List<String> gDataList = gRuleMap.get(name); List<String> dataList = Arrays.asList(dataStr.split("\n"));
List<String> newDataList = new ArrayList<>(gDataList); if (ConfigEntry.globalDataMap.containsKey(host)) {
newDataList.addAll(dataList); Map<String, List<String>> gRuleMap = new HashMap<>(ConfigEntry.globalDataMap.get(host));
newDataList = new ArrayList<>(new HashSet<>(newDataList)); if (gRuleMap.containsKey(name)) {
gRuleMap.remove(name); // gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法
gRuleMap.put(name, newDataList); List<String> gDataList = gRuleMap.get(name);
} else { List<String> newDataList = new ArrayList<>(gDataList);
gRuleMap.put(name, dataList); newDataList.addAll(dataList);
} newDataList = new ArrayList<>(new HashSet<>(newDataList));
ConfigEntry.globalDataMap.remove(host); gRuleMap.remove(name);
ConfigEntry.globalDataMap.put(host, gRuleMap); gRuleMap.put(name, newDataList);
} else { } else {
Map<String, List<String>> ruleMap = new HashMap<>(); gRuleMap.put(name, dataList);
ruleMap.put(name, dataList);
// 添加单一Host
ConfigEntry.globalDataMap.put(host, ruleMap);
} }
ConfigEntry.globalDataMap.remove(host);
ConfigEntry.globalDataMap.put(host, gRuleMap);
} else {
Map<String, List<String>> ruleMap = new HashMap<>();
ruleMap.put(name, dataList);
// 添加单一Host
ConfigEntry.globalDataMap.put(host, ruleMap);
}
String[] splitHost = host.split("\\."); String[] splitHost = host.split("\\.");
String anyHost = (splitHost.length > 2 && !MatchTool.matchIP(host)) ? StringHelper.replaceFirstOccurrence(host, splitHost[0], "*") : ""; String anyHost = (splitHost.length > 2 && !MatchTool.matchIP(host)) ? StringHelper.replaceFirstOccurrence(host, splitHost[0], "*") : "";
if (!ConfigEntry.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) { if (!ConfigEntry.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) {
// 添加通配符Host实际数据从查询哪里将所有数据提取 // 添加通配符Host实际数据从查询哪里将所有数据提取
ConfigEntry.globalDataMap.put(anyHost, new HashMap<>()); ConfigEntry.globalDataMap.put(anyHost, new HashMap<>());
} }
if (!ConfigEntry.globalDataMap.containsKey("*")) { if (!ConfigEntry.globalDataMap.containsKey("*")) {
// 添加通配符全匹配,同上 // 添加通配符全匹配,同上
ConfigEntry.globalDataMap.put("*", new HashMap<>()); ConfigEntry.globalDataMap.put("*", new HashMap<>());
} }
if (!ConfigEntry.globalDataMap.containsKey("**")) { if (!ConfigEntry.globalDataMap.containsKey("**")) {
// 添加通配符全匹配,同上 // 添加通配符全匹配,同上
ConfigEntry.globalDataMap.put("**", new HashMap<>()); ConfigEntry.globalDataMap.put("**", new HashMap<>());
}
} }
} }
} }
});
t.start();
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
} }
} }
}); });
GlobalCachePool.addToCache(messageIndex, finalMap); GlobalCachePool.addToCache(messageIndex, finalMap);

View File

@@ -1,6 +1,8 @@
package burp.core.processor; package burp.core.processor;
import burp.IExtensionHelpers; import burp.IExtensionHelpers;
import burp.IRequestInfo;
import burp.IResponseInfo;
import burp.core.utils.MatchTool; import burp.core.utils.MatchTool;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -20,7 +22,8 @@ public class MessageProcessor {
Map<String, Map<String, Object>> obj; Map<String, Map<String, Object>> obj;
if (isRequest) { if (isRequest) {
List<String> requestTmpHeaders = helpers.analyzeRequest(content).getHeaders(); IRequestInfo requestInfo = helpers.analyzeRequest(content);
List<String> requestTmpHeaders = requestInfo.getHeaders();
String requestHeaders = String.join("\n", requestTmpHeaders); String requestHeaders = String.join("\n", requestTmpHeaders);
try { try {
@@ -33,22 +36,23 @@ public class MessageProcessor {
return result; return result;
} }
int requestBodyOffset = helpers.analyzeRequest(content).getBodyOffset(); int requestBodyOffset = requestInfo.getBodyOffset();
byte[] requestBody = Arrays.copyOfRange(content, requestBodyOffset, content.length); byte[] requestBody = Arrays.copyOfRange(content, requestBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, requestHeaders, requestBody, "request", host); obj = dataProcessingUnit.matchContentByRegex(content, requestHeaders, requestBody, "request", host);
} else { } else {
IResponseInfo responseInfo = helpers.analyzeResponse(content);
try { try {
String inferredMimeType = String.format("hae.%s", helpers.analyzeResponse(content).getInferredMimeType().toLowerCase()); String inferredMimeType = String.format("hae.%s", responseInfo.getInferredMimeType().toLowerCase());
String statedMimeType = String.format("hae.%s", helpers.analyzeResponse(content).getStatedMimeType().toLowerCase()); String statedMimeType = String.format("hae.%s", responseInfo.getStatedMimeType().toLowerCase());
if (matcher.matchUrlSuffix(statedMimeType) || matcher.matchUrlSuffix(inferredMimeType)) { if (matcher.matchUrlSuffix(statedMimeType) || matcher.matchUrlSuffix(inferredMimeType)) {
return result; return result;
} }
} catch (Exception e) { } catch (Exception e) {
return result; return result;
} }
List<String> responseTmpHeaders = helpers.analyzeResponse(content).getHeaders(); List<String> responseTmpHeaders = responseInfo.getHeaders();
String responseHeaders = String.join("\n", responseTmpHeaders); String responseHeaders = String.join("\n", responseTmpHeaders);
int responseBodyOffset = helpers.analyzeResponse(content).getBodyOffset(); int responseBodyOffset = responseInfo.getBodyOffset();
byte[] responseBody = Arrays.copyOfRange(content, responseBodyOffset, content.length); byte[] responseBody = Arrays.copyOfRange(content, responseBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, responseHeaders, responseBody, "response", host); obj = dataProcessingUnit.matchContentByRegex(content, responseHeaders, responseBody, "response", host);
} }

View File

@@ -7,7 +7,6 @@ import burp.ui.board.MessagePanel.Table;
import java.util.*; import java.util.*;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
@@ -27,14 +26,28 @@ public class Databoard extends JPanel {
private static Boolean isMatchHost = false; private static Boolean isMatchHost = false;
private JLabel hostLabel; private JLabel hostLabel;
private JTextField hostTextField; private JTextField hostTextField;
private JTabbedPane dataTabbedPaneA; private JTabbedPane dataTabbedPane;
private JTabbedPane dataTabbedPaneB;
private JButton clearButton; private JButton clearButton;
private JSplitPane splitPane; private JSplitPane splitPane;
private MessagePanel messagePanel; private MessagePanel messagePanel;
private Table table; private Table table;
DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); private SwingWorker<Object, Void> currentWorker;
JComboBox hostComboBox = new JComboBox(comboBoxModel); private DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
private JComboBox hostComboBox = new JComboBox(comboBoxModel);
private ChangeListener changeListenerInstance = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
int selectedIndex = dataTabbedPane.getSelectedIndex();
String selectedTitle = "";
if (selectedIndex != -1) {
selectedTitle = dataTabbedPane.getTitleAt(selectedIndex);
}
applyHostFilter(selectedTitle);
}
};
public Databoard(MessagePanel messagePanel) { public Databoard(MessagePanel messagePanel) {
this.messagePanel = messagePanel; this.messagePanel = messagePanel;
@@ -42,8 +55,7 @@ public class Databoard extends JPanel {
} }
private void cleanUI() { private void cleanUI() {
dataTabbedPaneA.removeAll(); dataTabbedPane.removeAll();
dataTabbedPaneB.removeAll();
splitPane.setVisible(false); splitPane.setVisible(false);
} }
@@ -70,8 +82,7 @@ public class Databoard extends JPanel {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
hostLabel = new JLabel(); hostLabel = new JLabel();
hostTextField = new JTextField(); hostTextField = new JTextField();
dataTabbedPaneA = new JTabbedPane(JTabbedPane.TOP); dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
dataTabbedPaneB = new JTabbedPane(JTabbedPane.TOP);
clearButton = new JButton(); clearButton = new JButton();
//======== this ======== //======== this ========
@@ -130,102 +141,120 @@ public class Databoard extends JPanel {
* 设置输入自动匹配 * 设置输入自动匹配
*/ */
private void setAutoMatch() { private void setAutoMatch() {
isMatchHost = false; populateComboBoxModel();
for (String host : getHostByList()) {
comboBoxModel.addElement(host);
}
hostComboBox.setSelectedItem(null); hostComboBox.setSelectedItem(null);
hostComboBox.addActionListener(this::handleComboBoxAction);
hostComboBox.addActionListener(e -> {
if (!isMatchHost) {
if (hostComboBox.getSelectedItem() != null) {
String selectedHost = hostComboBox.getSelectedItem().toString();
hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
}
}
});
// 事件监听
hostTextField.addKeyListener(new KeyAdapter() { hostTextField.addKeyListener(new KeyAdapter() {
@Override @Override
public void keyPressed(KeyEvent e) { public void keyPressed(KeyEvent e) {
isMatchHost = true; handleKeyEvents(e);
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE && hostComboBox.isPopupVisible()) {
e.setKeyCode(KeyEvent.VK_ENTER);
}
if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) {
e.setSource(hostComboBox);
hostComboBox.dispatchEvent(e);
if (keyCode == KeyEvent.VK_ENTER) {
String selectedItem = hostComboBox.getSelectedItem().toString();
hostTextField.setText(selectedItem);
populateTabbedPaneByHost(selectedItem);
hostComboBox.setPopupVisible(false);
return;
}
}
if (keyCode == KeyEvent.VK_ESCAPE) {
hostComboBox.setPopupVisible(false);
}
isMatchHost = false;
} }
}); });
hostTextField.getDocument().addDocumentListener(new DocumentListener() { hostTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
updateList(); update(e);
} }
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
updateList(); update(e);
} }
@Override @Override
public void changedUpdate(DocumentEvent e) { public void changedUpdate(DocumentEvent e) {
updateList(); update(e);
} }
private void updateList() { public void update(DocumentEvent e) {
isMatchHost = true; filterComboBoxList();
comboBoxModel.removeAllElements();
String input = hostTextField.getText().toLowerCase();
if (!input.isEmpty()){
for (String host : getHostByList()) {
String lowerCaseHost = host.toLowerCase();
if (lowerCaseHost.contains(input)) {
if (lowerCaseHost.equals(input)) {
comboBoxModel.insertElementAt(lowerCaseHost, 0);
comboBoxModel.setSelectedItem(lowerCaseHost);
} else {
comboBoxModel.addElement(host);
}
}
}
}
hostComboBox.setPopupVisible(comboBoxModel.getSize() > 0);
isMatchHost = false;
} }
}); });
} }
private void populateComboBoxModel() {
for (String host : getHostByList()) {
comboBoxModel.addElement(host);
}
}
private void handleComboBoxAction(ActionEvent e) {
if (!isMatchHost && hostComboBox.getSelectedItem() != null) {
String selectedHost = hostComboBox.getSelectedItem().toString();
hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
}
}
private void handleKeyEvents(KeyEvent e) {
isMatchHost = true;
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE && hostComboBox.isPopupVisible()) {
e.setKeyCode(KeyEvent.VK_ENTER);
}
if (Arrays.asList(KeyEvent.VK_ENTER, KeyEvent.VK_UP, KeyEvent.VK_DOWN).contains(keyCode)) {
e.setSource(hostComboBox);
hostComboBox.dispatchEvent(e);
if (keyCode == KeyEvent.VK_ENTER) {
updateTextFieldFromComboBox();
hostComboBox.setPopupVisible(false);
e.consume();
}
}
if (keyCode == KeyEvent.VK_ESCAPE) {
hostComboBox.setPopupVisible(false);
}
isMatchHost = false;
}
private void updateTextFieldFromComboBox() {
Object selectedItem = hostComboBox.getSelectedItem();
if (selectedItem != null) {
String selectedHost = selectedItem.toString();
hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
}
}
private void filterComboBoxList() {
isMatchHost = true;
comboBoxModel.removeAllElements();
String input = hostTextField.getText().toLowerCase();
if (!input.isEmpty()) {
for (String host : getHostByList()) {
String lowerCaseHost = host.toLowerCase();
if (lowerCaseHost.contains(input)) {
if (lowerCaseHost.equals(input)) {
comboBoxModel.insertElementAt(lowerCaseHost, 0);
comboBoxModel.setSelectedItem(lowerCaseHost);
} else {
comboBoxModel.addElement(host);
}
}
}
}
hostComboBox.setPopupVisible(comboBoxModel.getSize() > 0);
isMatchHost = false;
}
private void applyHostFilter(String filterText) { private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter(); TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter();
if (filterText.contains("*.")) { if (filterText.contains("*.")) {
filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", ""); filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
} else if (filterText.contains("*")) { } else if (filterText.contains("*")) {
filterText = ""; filterText = "";
} }
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1); RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1);
sorter.setRowFilter(filter); sorter.setRowFilter(filter);
filterText = filterText.isEmpty() ? "*" : filterText; filterText = filterText.isEmpty() ? "*" : filterText;
@@ -262,51 +291,58 @@ public class Databoard extends JPanel {
selectedDataMap = dataMap.get(selectedHost); selectedDataMap = dataMap.get(selectedHost);
} }
// 由于removeChangeListener不知什么原因不生效因此建立两个tabbedPane dataTabbedPane.removeAll();
dataTabbedPaneA.removeAll();
dataTabbedPaneB.removeAll();
ChangeListener changeListenerInstance = new ChangeListener() { dataTabbedPane.setPreferredSize(new Dimension(500,0));
@Override dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
public void stateChanged(ChangeEvent e) { splitPane.setLeftComponent(dataTabbedPane);
int selectedIndex = dataTabbedPaneA.getSelectedIndex();
String selectedTitle = "";
if (selectedIndex != -1) {
selectedTitle = dataTabbedPaneA.getTitleAt(selectedIndex);
}
applyHostFilter(selectedTitle);
}
};
if (selectedHost.equals("**")) { if (selectedHost.equals("**")) {
dataTabbedPaneA.setPreferredSize(new Dimension(500,0));
dataTabbedPaneA.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPaneA);
for (Map.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) { for (Map.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) {
JTabbedPane newTabbedPane = new JTabbedPane(); JTabbedPane newTabbedPane = new JTabbedPane();
newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) { for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) {
Thread t = new Thread(() -> { currentWorker = new SwingWorker<Object, Void>() {
String tabTitle = String.format("%s (%s)", entrySet.getKey(), entrySet.getValue().size()); @Override
newTabbedPane.addTab(tabTitle, new DataTable(entrySet.getKey(), entrySet.getValue())); protected Object[] doInBackground() throws Exception {
dataTabbedPaneA.addTab(entry.getKey(), newTabbedPane); String tabTitle = String.format("%s (%s)", entrySet.getKey(),
}); entrySet.getValue().size());
t.start(); DatatablePanel datatablePanel = new DatatablePanel(entrySet.getKey(),
try { entrySet.getValue());
t.join(); datatablePanel.setTableListener(messagePanel);
} catch (Exception e) { return new Object[] {tabTitle, datatablePanel};
e.printStackTrace(); }
}
@Override
protected void done() {
if (!isCancelled()) {
try {
Object[] result = (Object[]) get();
newTabbedPane.addTab(result[0].toString(), (DatatablePanel) result[1]);
dataTabbedPane.addTab(entry.getKey(), newTabbedPane);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
currentWorker.execute();
} }
} }
dataTabbedPaneA.addChangeListener(changeListenerInstance);
dataTabbedPane.addChangeListener(changeListenerInstance);
} else { } else {
dataTabbedPaneB.setPreferredSize(new Dimension(500,0)); dataTabbedPane.removeChangeListener(changeListenerInstance);
dataTabbedPaneB.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPaneB);
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) { for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size()); String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
dataTabbedPaneB.addTab(tabTitle, new DataTable(entry.getKey(), entry.getValue())); DatatablePanel datatablePanel = new DatatablePanel(entry.getKey(), entry.getValue());
datatablePanel.setTableListener(messagePanel);
dataTabbedPane.addTab(tabTitle, datatablePanel);
} }
} }
@@ -332,103 +368,21 @@ public class Databoard extends JPanel {
} }
hostTextField.setText(selectedHost); hostTextField.setText(selectedHost);
}
}
class DataTable extends JPanel { ChangeListener changeListener = new ChangeListener() {
private final JTable table; public void stateChanged(ChangeEvent e) {
private final DefaultTableModel model; JTabbedPane tabSource = (JTabbedPane) e.getSource();
private final JTextField searchField; int index = tabSource.getSelectedIndex();
private TableRowSorter<DefaultTableModel> sorter; if (index != -1) {
Component selectedComponent = tabSource.getComponentAt(index);
if (selectedComponent instanceof DatatablePanel) {
public DataTable(String tableName, List<String> list) { ((DatatablePanel) selectedComponent).updatePageSize();
model = new DefaultTableModel();
table = new JTable(model);
sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
table.setDefaultEditor(Object.class, null);
// 表格内容双击事件
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
String rowData = table.getValueAt(selectedRow, 0).toString();
messagePanel.applyMessageFilter(tableName, rowData);
} }
} }
} }
}); };
model.addColumn("Information"); dataTabbedPane.addChangeListener(changeListener);
for (String item : list) {
model.addRow(new Object[]{item});
}
String defaultText = "Search";
searchField = new JTextField(defaultText);
// 设置灰色默认文本Search
searchField.setForeground(Color.GRAY);
searchField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
if (searchField.getText().equals(defaultText)) {
searchField.setText("");
searchField.setForeground(Color.BLACK);
}
}
@Override
public void focusLost(FocusEvent e) {
if (searchField.getText().isEmpty()) {
searchField.setForeground(Color.GRAY);
searchField.setText(defaultText);
}
}
});
// 监听输入框内容输入、更新、删除
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void removeUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void changedUpdate(DocumentEvent e) {
performSearch();
}
private void performSearch() {
// 通过字体颜色来判断是否可以进行过滤
if (searchField.getForeground() == Color.BLACK) {
String searchText = searchField.getText();
if (sorter == null) {
sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
}
RowFilter<DefaultTableModel, Object> rowFilter = RowFilter.regexFilter(String.format("%s%s", "(?i)", searchText), 0);
sorter.setRowFilter(rowFilter);
}
}
});
// 设置布局
JScrollPane scrollPane = new JScrollPane(table);
setLayout(new BorderLayout(0, 5));
add(scrollPane, BorderLayout.CENTER);
add(searchField, BorderLayout.SOUTH);
} }
} }
} }

View File

@@ -0,0 +1,193 @@
package burp.ui.board;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
public class DatatablePanel extends JPanel {
private final JTable table;
private final DefaultTableModel model;
private final JTextField searchField;
private TableRowSorter<DefaultTableModel> sorter;
private int pageSize; // 动态计算的,每页显示多少条记录
private int currentPage; // 当前页码
private List<String> fullList; // 假设这是一个包含所有数据的列表
private JScrollPane scrollPane;
private String tableName;
private final int SHOW_LENGTH = 3000;
public DatatablePanel(String tableName, List<String> list) {
fullList = list;
currentPage = 0;
pageSize = 10;
this.tableName = tableName;
model = new DefaultTableModel();
table = new JTable(model);
sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
model.addColumn("Information");
String defaultText = "Search";
searchField = new JTextField(defaultText);
// 设置灰色默认文本Search
searchField.setForeground(Color.GRAY);
searchField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
if (searchField.getText().equals(defaultText)) {
searchField.setText("");
searchField.setForeground(Color.BLACK);
}
}
@Override
public void focusLost(FocusEvent e) {
if (searchField.getText().isEmpty()) {
searchField.setForeground(Color.GRAY);
searchField.setText(defaultText);
}
}
});
// 监听输入框内容输入、更新、删除
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void removeUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void changedUpdate(DocumentEvent e) {
performSearch();
}
private void performSearch() {
// 通过字体颜色来判断是否可以进行过滤
if (searchField.getForeground() == Color.BLACK) {
String searchText = searchField.getText();
if (sorter == null) {
sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
}
RowFilter<DefaultTableModel, Object> rowFilter = RowFilter.regexFilter(String.format("%s%s", "(?i)", searchText), 0);
sorter.setRowFilter(rowFilter);
}
}
});
// 设置布局
scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
updatePageSize();
}
});
// 添加滚动监听器,以加载更多数据
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if (fullList.size() > SHOW_LENGTH) {
if (!e.getValueIsAdjusting() && !scrollPane.getVerticalScrollBar().getValueIsAdjusting()) {
if (scrollPane.getVerticalScrollBar().getValue() == scrollPane.getVerticalScrollBar().getMaximum() - scrollPane.getVerticalScrollBar().getVisibleAmount()) {
if ((currentPage + 1) * pageSize < fullList.size()) {
currentPage++;
loadPageData();
}
}
}
}
}
});
setLayout(new BorderLayout(0, 5));
add(scrollPane, BorderLayout.CENTER);
add(searchField, BorderLayout.SOUTH);
loadPageData();
}
// 加载指定页的数据
private void loadPageData() {
if (fullList.size() > SHOW_LENGTH) {
int start = currentPage * pageSize;
int end = Math.min((currentPage + 1) * pageSize, fullList.size());
int lastRow = model.getRowCount();
start = Math.max(start, lastRow);
for (int i = start; i < end; i++) {
model.addRow(new Object[]{fullList.get(i)});
}
} else {
for (String item : fullList) {
model.addRow(new Object[]{item});
}
}
}
public void updatePageSize() {
if (fullList.size() > SHOW_LENGTH && isShowing()) {
int oldPageSize = pageSize;
pageSize = getDynamicSize();
if (oldPageSize != pageSize) {
currentPage = 0;
loadPageData();
}
}
}
private int getDynamicSize() {
int visibleHeight = scrollPane.getViewport().getViewRect().height;
int rowHeight = table.getRowHeight();
return Math.max(1, visibleHeight / rowHeight + 2);
}
public void setTableListener(MessagePanel messagePanel) {
table.setDefaultEditor(Object.class, null);
// 表格内容双击事件
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
String rowData = table.getValueAt(selectedRow, 0).toString();
messagePanel.applyMessageFilter(tableName, rowData);
}
}
}
});
}
public JTable getTable() {
return this.table;
}
}

View File

@@ -21,12 +21,10 @@ import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
@@ -158,6 +156,9 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override @Override
public Object getValueAt(int rowIndex, int columnIndex) public Object getValueAt(int rowIndex, int columnIndex)
{ {
if (filteredLog.isEmpty()) {
return "";
}
LogEntry logEntry = filteredLog.get(rowIndex); LogEntry logEntry = filteredLog.get(rowIndex);
switch (columnIndex) switch (columnIndex)
{ {
@@ -264,6 +265,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
} }
} }
fireTableDataChanged(); fireTableDataChanged();
logTable.lastSelectedIndex = -1;
} }
public void deleteByHost(String filterText) { public void deleteByHost(String filterText) {
@@ -355,6 +357,9 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
} }
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) { private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
if (map1 == null || map2 == null) {
return false;
}
if (map1.size() != map2.size()) { if (map1.size() != map2.size()) {
return false; return false;
} }
@@ -398,7 +403,10 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
public class Table extends JTable { public class Table extends JTable {
LogEntry logEntry; LogEntry logEntry;
private SwingWorker<Void, Void> currentWorker; private SwingWorker<Object, Void> currentWorker;
// 设置响应报文返回的最大长度为3MB
private final int MAX_LENGTH = 3145728;
private int lastSelectedIndex = -1;
public Table(TableModel tableModel) { public Table(TableModel tableModel) {
super(tableModel); super(tableModel);
@@ -407,35 +415,50 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override @Override
public void changeSelection(int row, int col, boolean toggle, boolean extend) { public void changeSelection(int row, int col, boolean toggle, boolean extend) {
super.changeSelection(row, col, toggle, extend); super.changeSelection(row, col, toggle, extend);
int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex;
logEntry = filteredLog.get(selectedIndex);
logEntry = filteredLog.get(convertRowIndexToModel(row)); requestViewer.setMessage("Loading...".getBytes(), true);
requestViewer.setMessage("Loading...".getBytes(), true); responseViewer.setMessage("Loading...".getBytes(), false);
responseViewer.setMessage("Loading...".getBytes(), false); currentlyDisplayedItem = logEntry.getRequestResponse();
currentlyDisplayedItem = logEntry.getRequestResponse();
// 取消之前的后台任务 if (currentWorker != null && !currentWorker.isDone()) {
if (currentWorker != null && !currentWorker.isDone()) { currentWorker.cancel(true);
currentWorker.cancel(true);
}
// 在后台线程中执行耗时操作
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
refreshMessage();
return null;
} }
};
// 设置当前后台任务
currentWorker = worker;
// 启动后台线程
worker.execute();
}
private synchronized void refreshMessage() { currentWorker = new SwingWorker<Object, Void>() {
SwingUtilities.invokeLater(() -> { @Override
requestViewer.setMessage(logEntry.getRequestResponse().getRequest(), true); protected byte[][] doInBackground() throws Exception {
responseViewer.setMessage(logEntry.getRequestResponse().getResponse(), false); byte[] requestByte = logEntry.getRequestResponse().getRequest();
}); byte[] responseByte = logEntry.getRequestResponse().getResponse();
if (responseByte.length > MAX_LENGTH) {
String ellipsis = "\r\n......";
responseByte = Arrays.copyOf(responseByte, MAX_LENGTH + ellipsis.length());
byte[] ellipsisBytes = ellipsis.getBytes();
System.arraycopy(ellipsisBytes, 0, responseByte, MAX_LENGTH, ellipsisBytes.length);
}
return new byte[][] {requestByte, responseByte};
}
@Override
protected void done() {
if (!isCancelled()) {
try {
byte[][] result = (byte[][]) get();
requestViewer.setMessage(result[0], true);
responseViewer.setMessage(result[1], false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
currentWorker.execute();
}
} }
} }