Version: 3.2.1 Update

This commit is contained in:
gh0stkey
2024-05-30 14:37:01 +08:00
parent 97172fab45
commit 6d4abae898
15 changed files with 695 additions and 239 deletions

View File

@@ -54,4 +54,6 @@ public class Config {
public static Map<String, Object[][]> globalRules = new HashMap<>(); public static Map<String, Object[][]> globalRules = new HashMap<>();
public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>(); public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Map<String, Object>> globalHostHashMap = new ConcurrentHashMap<>();
} }

View File

@@ -16,13 +16,13 @@ public class HaE implements BurpExtension {
@Override @Override
public void initialize(MontoyaApi api) { public void initialize(MontoyaApi api) {
// 设置扩展名称 // 设置扩展名称
String version = "3.2"; String version = "3.2.1";
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version)); api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 加载扩展后输出的项目信息 // 加载扩展后输出的项目信息
Logging logging = api.logging(); Logging logging = api.logging();
logging.logToOutput("[ HACK THE WORLD - TO DO IT ]"); logging.logToOutput("[ HACK THE WORLD - TO DO IT ]");
logging.logToOutput("[#] Author: EvilChen && 0chencc"); logging.logToOutput("[#] Author: EvilChen && 0chencc && vaycore");
logging.logToOutput("[#] Github: https://github.com/gh0stkey/HaE"); logging.logToOutput("[#] Github: https://github.com/gh0stkey/HaE");
// 配置文件加载 // 配置文件加载

View File

@@ -1,10 +1,6 @@
package hae.component.board; package hae.component.board;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.HttpService;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import hae.Config; import hae.Config;
import hae.component.board.message.MessageEntry; import hae.component.board.message.MessageEntry;
import hae.component.board.message.MessageTableModel; import hae.component.board.message.MessageTableModel;
@@ -27,7 +23,8 @@ import java.awt.event.*;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Databoard extends JPanel { public class Databoard extends JPanel {
@@ -35,15 +32,19 @@ public class Databoard extends JPanel {
private final ConfigLoader configLoader; private final ConfigLoader configLoader;
private final ProjectProcessor projectProcessor; private final ProjectProcessor projectProcessor;
private final MessageTableModel messageTableModel; private final MessageTableModel messageTableModel;
private JTextField hostTextField; private JTextField hostTextField;
private JTabbedPane dataTabbedPane; private JTabbedPane dataTabbedPane;
private JSplitPane splitPane; private JSplitPane splitPane;
private MessageTable messageTable; private MessageTable messageTable;
private JProgressBar progressBar;
private static Boolean isMatchHost = false; private static Boolean isMatchHost = false;
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
private final JComboBox hostComboBox = new JComboBox(comboBoxModel); private final JComboBox hostComboBox = new JComboBox(comboBoxModel);
private SwingWorker<Boolean, Void> currentWorker;
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) { public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api; this.api = api;
this.configLoader = configLoader; this.configLoader = configLoader;
@@ -56,9 +57,9 @@ public class Databoard extends JPanel {
private void initComponents() { private void initComponents() {
setLayout(new GridBagLayout()); setLayout(new GridBagLayout());
((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0}; ((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0}; ((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 25, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4}; ((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 1.0E-4}; ((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 0.0, 1.0E-4};
JLabel hostLabel = new JLabel("Host:"); JLabel hostLabel = new JLabel("Host:");
@@ -78,6 +79,10 @@ public class Databoard extends JPanel {
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP); dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
dataTabbedPane.setPreferredSize(new Dimension(500, 0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPane);
actionButton.addActionListener(e -> { actionButton.addActionListener(e -> {
int x = 0; int x = 0;
int y = actionButton.getHeight(); int y = actionButton.getHeight();
@@ -88,6 +93,8 @@ public class Databoard extends JPanel {
exportButton.addActionListener(this::exportActionPerformed); exportButton.addActionListener(this::exportActionPerformed);
importButton.addActionListener(this::importActionPerformed); importButton.addActionListener(this::importActionPerformed);
progressBar = new JProgressBar();
splitPane.addComponentListener(new ComponentAdapter() { splitPane.addComponentListener(new ComponentAdapter() {
@Override @Override
public void componentResized(ComponentEvent e) { public void componentResized(ComponentEvent e) {
@@ -96,6 +103,7 @@ public class Databoard extends JPanel {
}); });
splitPane.setVisible(false); splitPane.setVisible(false);
progressBar.setVisible(false);
add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
@@ -103,9 +111,13 @@ public class Databoard extends JPanel {
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
add(splitPane, new GridBagConstraints(1, 1, 3, 3, 0.0, 0.0,
add(splitPane, new GridBagConstraints(1, 1, 3, 1, 0.0, 1.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(0, 5, 0, 5), 0, 0));
add(progressBar, new GridBagConstraints(1, 3, 3, 1, 1.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
new Insets(0, 5, 0, 5), 0, 0));
hostComboBox.setMaximumRowCount(5); hostComboBox.setMaximumRowCount(5);
add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
@@ -125,6 +137,19 @@ public class Databoard extends JPanel {
columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1)); columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1));
} }
private void setProgressBar(boolean status) {
progressBar.setIndeterminate(status);
if (!status) {
progressBar.setMaximum(100);
progressBar.setString("OK");
progressBar.setStringPainted(true);
progressBar.setValue(progressBar.getMaximum());
} else {
progressBar.setString("Loading...");
progressBar.setStringPainted(true);
}
}
private void setAutoMatch() { private void setAutoMatch() {
hostComboBox.setSelectedItem(null); hostComboBox.setSelectedItem(null);
hostComboBox.addActionListener(this::handleComboBoxAction); hostComboBox.addActionListener(this::handleComboBoxAction);
@@ -157,9 +182,45 @@ public class Databoard extends JPanel {
private void handleComboBoxAction(ActionEvent e) { private void handleComboBoxAction(ActionEvent e) {
if (!isMatchHost && hostComboBox.getSelectedItem() != null) { if (!isMatchHost && hostComboBox.getSelectedItem() != null) {
progressBar.setVisible(true);
setProgressBar(true);
String selectedHost = hostComboBox.getSelectedItem().toString(); String selectedHost = hostComboBox.getSelectedItem().toString();
hostTextField.setText(selectedHost); hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
currentWorker = new SwingWorker<Boolean, Void>() {
@Override
protected Boolean doInBackground() {
return populateTabbedPaneByHost(selectedHost);
}
@Override
protected void done() {
if (!isCancelled()) {
try {
boolean status = get();
if (status) {
JSplitPane messageSplitPane = messageTableModel.getSplitPane();
splitPane.setRightComponent(messageSplitPane);
messageTable = messageTableModel.getMessageTable();
resizePanel();
splitPane.setVisible(true);
hostTextField.setText(selectedHost);
hostComboBox.setPopupVisible(false);
applyHostFilter(selectedHost);
}
} catch (Exception ignored) {
}
}
}
};
currentWorker.execute();
} }
} }
@@ -178,7 +239,6 @@ public class Databoard extends JPanel {
if (keyCode == KeyEvent.VK_ENTER) { if (keyCode == KeyEvent.VK_ENTER) {
isMatchHost = false; isMatchHost = false;
handleComboBoxAction(null); handleComboBoxAction(null);
hostComboBox.setPopupVisible(false);
} }
if (keyCode == KeyEvent.VK_ESCAPE) { if (keyCode == KeyEvent.VK_ESCAPE) {
@@ -188,10 +248,52 @@ public class Databoard extends JPanel {
isMatchHost = false; isMatchHost = false;
} }
private boolean populateTabbedPaneByHost(String selectedHost) {
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
Map<String, List<String>> selectedDataMap;
if (selectedHost.contains("*")) {
selectedDataMap = new HashMap<>();
dataMap.keySet().parallelStream().forEach(key -> {
if ((StringProcessor.matchesHostPattern(key, selectedHost) || selectedHost.equals("*")) && !key.contains("*")) {
Map<String, List<String>> ruleMap = dataMap.get(key);
for (String ruleKey : ruleMap.keySet()) {
List<String> dataList = ruleMap.get(ruleKey);
if (selectedDataMap.containsKey(ruleKey)) {
List<String> mergedList = new ArrayList<>(selectedDataMap.get(ruleKey));
mergedList.addAll(dataList);
HashSet<String> uniqueSet = new HashSet<>(mergedList);
selectedDataMap.put(ruleKey, new ArrayList<>(uniqueSet));
} else {
selectedDataMap.put(ruleKey, dataList);
}
}
}
});
} else {
selectedDataMap = dataMap.get(selectedHost);
}
if (!selectedDataMap.isEmpty()) {
dataTabbedPane.removeAll();
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
Datatable datatablePanel = new Datatable(api, entry.getKey(), entry.getValue());
datatablePanel.setTableListener(messageTableModel);
dataTabbedPane.addTab(tabTitle, datatablePanel);
}
return true;
}
return false;
}
private void filterComboBoxList() { private void filterComboBoxList() {
isMatchHost = true; isMatchHost = true;
comboBoxModel.removeAllElements(); comboBoxModel.removeAllElements();
String input = hostTextField.getText().toLowerCase(); String input = hostTextField.getText().toLowerCase();
if (!input.isEmpty()) { if (!input.isEmpty()) {
for (String host : getHostByList()) { for (String host : getHostByList()) {
String lowerCaseHost = host.toLowerCase(); String lowerCaseHost = host.toLowerCase();
@@ -210,83 +312,40 @@ public class Databoard extends JPanel {
isMatchHost = false; isMatchHost = false;
} }
private void populateTabbedPaneByHost(String selectedHost) {
if (!Objects.equals(selectedHost, "")) {
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
Map<String, List<String>> selectedDataMap;
dataTabbedPane.removeAll();
dataTabbedPane.setPreferredSize(new Dimension(500, 0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPane);
if (selectedHost.contains("*")) {
// 通配符数据
selectedDataMap = new HashMap<>();
for (String key : dataMap.keySet()) {
if ((StringProcessor.matchesHostPattern(key, selectedHost) || selectedHost.equals("*")) && !key.contains("*")) {
Map<String, List<String>> ruleMap = dataMap.get(key);
for (String ruleKey : ruleMap.keySet()) {
List<String> dataList = ruleMap.get(ruleKey);
if (selectedDataMap.containsKey(ruleKey)) {
List<String> mergedList = new ArrayList<>(selectedDataMap.get(ruleKey));
mergedList.addAll(dataList);
HashSet<String> uniqueSet = new HashSet<>(mergedList);
selectedDataMap.put(ruleKey, new ArrayList<>(uniqueSet));
} else {
selectedDataMap.put(ruleKey, dataList);
}
}
}
}
} else {
selectedDataMap = dataMap.get(selectedHost);
}
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
Datatable datatablePanel = new Datatable(api, entry.getKey(), entry.getValue());
datatablePanel.setTableListener(messageTableModel);
dataTabbedPane.addTab(tabTitle, datatablePanel);
}
// 展示请求消息表单
JSplitPane messageSplitPane = messageTableModel.getSplitPane();
this.splitPane.setRightComponent(messageSplitPane);
messageTable = messageTableModel.getMessageTable();
resizePanel();
splitPane.setVisible(true);
applyHostFilter(selectedHost);
hostTextField.setText(selectedHost);
}
}
private void applyHostFilter(String filterText) { private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) messageTable.getRowSorter(); TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) messageTable.getRowSorter();
String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", ""); String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", "");
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() { new SwingWorker<Void, Void>() {
public boolean include(Entry<?, ?> entry) { @Override
if (cleanedText.equals("*")) { protected Void doInBackground() throws Exception {
return true; RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
} else { public boolean include(Entry<?, ?> entry) {
String host = StringProcessor.getHostByUrl((String) entry.getValue(1)); if (cleanedText.equals("*")) {
return true;
} else {
String host = StringProcessor.getHostByUrl((String) entry.getValue(1));
return StringProcessor.matchesHostPattern(host, filterText);
}
}
};
return StringProcessor.matchesHostPattern(host, filterText); sorter.setRowFilter(rowFilter);
} messageTableModel.applyHostFilter(filterText);
return null;
} }
};
sorter.setRowFilter(rowFilter); @Override
protected void done() {
setProgressBar(false);
}
}.execute();
messageTableModel.applyHostFilter(filterText);
} }
private List<String> getHostByList() { private List<String> getHostByList() {
if (!(Config.globalDataMap.keySet().size() == 1 && Config.globalDataMap.keySet().stream().anyMatch(key -> key.contains("*")))) { if (!Config.globalDataMap.keySet().isEmpty()) {
return new ArrayList<>(Config.globalDataMap.keySet()); return new ArrayList<>(Config.globalDataMap.keySet());
} }
return new ArrayList<>(); return new ArrayList<>();
@@ -305,13 +364,25 @@ public class Databoard extends JPanel {
return; return;
} }
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap; new SwingWorker<List<String>, Void>() {
List<String> taskStatusList = exportData(selectedHost, exportDir, dataMap); @Override
protected List<String> doInBackground() {
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
return exportData(selectedHost, exportDir, dataMap);
}
if (!taskStatusList.isEmpty()) { @Override
String exportStatusMessage = String.format("Exported File List Status:\n%s", String.join("\n", taskStatusList)); protected void done() {
JOptionPane.showConfirmDialog(null, exportStatusMessage, "Info", JOptionPane.YES_OPTION); try {
} List<String> taskStatusList = get();
if (!taskStatusList.isEmpty()) {
String exportStatusMessage = String.format("Exported File List Status:\n%s", String.join("\n", taskStatusList));
JOptionPane.showConfirmDialog(Databoard.this, exportStatusMessage, "Info", JOptionPane.YES_OPTION);
}
} catch (Exception ignored) {
}
}
}.execute();
} }
private List<String> exportData(String selectedHost, String exportDir, Map<String, Map<String, List<String>>> dataMap) { private List<String> exportData(String selectedHost, String exportDir, Map<String, Map<String, List<String>>> dataMap) {
@@ -332,28 +403,63 @@ public class Databoard extends JPanel {
} }
List<MessageEntry> messageEntryList = messageTableModel.getLogs(); List<MessageEntry> messageEntryList = messageTableModel.getLogs();
Map<String, Map<String, String>> httpMap = messageEntryList.stream()
.filter(messageEntry -> !StringProcessor.getHostByUrl(messageEntry.getUrl()).isEmpty()) Map<MessageEntry, String> entryUUIDMap = messageEntryList.stream()
.filter(messageEntry -> StringProcessor.getHostByUrl(messageEntry.getUrl()).equals(key))
.collect(Collectors.toMap( .collect(Collectors.toMap(
MessageEntry::getUrl, messageEntry -> messageEntry,
this::createHttpItemMap, messageEntry -> StringProcessor.getRandomUUID(),
(existing, replacement) -> existing (existing, replacement) -> existing // 在冲突时保留现有的映射
)); ));
Map<String, Map<String, Object>> httpMap = processEntries(
messageEntryList,
key,
entryUUIDMap,
this::createHttpItemMap
);
Map<String, Map<String, Object>> urlMap = processEntries(
messageEntryList,
key,
entryUUIDMap,
this::creteUrlItemMap
);
String hostName = key.replace(":", "_"); String hostName = key.replace(":", "_");
String filename = String.format("%s/%s.hae", exportDir, hostName); String filename = String.format("%s/%s-%s.hae", exportDir, StringProcessor.getCurrentTime(), hostName);
boolean createdStatus = projectProcessor.createHaeFile(filename, key, ruleMap, httpMap); boolean createdStatus = projectProcessor.createHaeFile(filename, key, ruleMap, urlMap, httpMap);
return String.format("Filename: %s, Status: %s", filename, createdStatus); return String.format("Filename: %s, Status: %s", filename, createdStatus);
} }
private Map<String, String> createHttpItemMap(MessageEntry entry) {
Map<String, String> httpItemMap = new HashMap<>(); private Map<String, Map<String, Object>> processEntries(List<MessageEntry> messageEntryList, String key, Map<MessageEntry, String> entryUUIDMap, Function<MessageEntry, Map<String, Object>> mapFunction) {
httpItemMap.put("comment", entry.getComment()); return messageEntryList.stream()
httpItemMap.put("color", entry.getColor()); .filter(messageEntry -> !StringProcessor.getHostByUrl(messageEntry.getUrl()).isEmpty())
httpItemMap.put("request", entry.getRequestResponse().request().toString()); .filter(messageEntry -> StringProcessor.getHostByUrl(messageEntry.getUrl()).equals(key))
httpItemMap.put("response", entry.getRequestResponse().response().toString()); .collect(Collectors.toMap(
entryUUIDMap::get,
mapFunction,
(existing, replacement) -> existing
));
}
private Map<String, Object> creteUrlItemMap(MessageEntry entry) {
Map<String, Object> urlItemMap = new LinkedHashMap<>();
urlItemMap.put("url", entry.getUrl());
urlItemMap.put("method", entry.getMethod());
urlItemMap.put("status", entry.getStatus());
urlItemMap.put("length", entry.getLength());
urlItemMap.put("comment", entry.getComment());
urlItemMap.put("color", entry.getColor());
urlItemMap.put("size", String.valueOf(entry.getRequestResponse().request().toByteArray().length()));
return urlItemMap;
}
private Map<String, Object> createHttpItemMap(MessageEntry entry) {
Map<String, Object> httpItemMap = new LinkedHashMap<>();
httpItemMap.put("request", entry.getRequestResponse().request().toByteArray().getBytes());
httpItemMap.put("response", entry.getRequestResponse().response().toByteArray().getBytes());
return httpItemMap; return httpItemMap;
} }
@@ -363,43 +469,74 @@ public class Databoard extends JPanel {
return; return;
} }
List<String> filesWithExtension = findFilesWithExtension(new File(exportDir), ".hae"); new SwingWorker<List<String>, Void>() {
List<String> taskStatusList = filesWithExtension.stream() @Override
.map(this::importData) protected List<String> doInBackground() {
.collect(Collectors.toList()); List<String> filesWithExtension = findFilesWithExtension(new File(exportDir), ".hae");
return filesWithExtension.stream()
.map(Databoard.this::importData)
.collect(Collectors.toList());
}
if (!taskStatusList.isEmpty()) { @Override
String importStatusMessage = "Imported File List Status:\n" + String.join("\n", taskStatusList); protected void done() {
JOptionPane.showConfirmDialog(null, importStatusMessage, "Info", JOptionPane.YES_OPTION); try {
} List<String> taskStatusList = get();
if (!taskStatusList.isEmpty()) {
String importStatusMessage = "Imported File List Status:\n" + String.join("\n", taskStatusList);
JOptionPane.showConfirmDialog(Databoard.this, importStatusMessage, "Info", JOptionPane.YES_OPTION);
}
} catch (Exception ignored) {
}
}
}.execute();
} }
private String importData(String filename) { private String importData(String filename) {
HaeFileContent haeFileContent = projectProcessor.readHaeFile(filename); HaeFileContent haeFileContent = projectProcessor.readHaeFile(filename);
boolean readStatus = haeFileContent != null; boolean readStatus = haeFileContent != null;
if (readStatus) { ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
String host = haeFileContent.getHost(); List<Future<?>> futures = new ArrayList<>();
haeFileContent.getDataMap().forEach((key, value) -> RegularMatcher.putDataToGlobalMap(host, key, value));
haeFileContent.getHttpMap().forEach((key, httpItemMap) -> { if (readStatus) {
String comment = httpItemMap.get("comment"); try {
String color = httpItemMap.get("color"); String host = haeFileContent.getHost();
HttpRequestResponse httpRequestResponse = createHttpRequestResponse(key, httpItemMap); haeFileContent.getDataMap().forEach((key, value) -> RegularMatcher.putDataToGlobalMap(host, key, value));
messageTableModel.add(httpRequestResponse, comment, color);
}); haeFileContent.getUrlMap().forEach((key, urlItemMap) -> {
Future<?> future = executor.submit(() -> {
String url = urlItemMap.get("url");
String comment = urlItemMap.get("comment");
String color = urlItemMap.get("color");
String length = urlItemMap.get("length");
String method = urlItemMap.get("method");
String status = urlItemMap.get("status");
String path = haeFileContent.getHttpPath();
messageTableModel.add(null, url, method, status, length, comment, color, key, path);
});
futures.add(future);
});
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
}
}
} catch (Exception e) {
api.logging().logToError("importData: " + e.getMessage());
} finally {
executor.shutdown();
}
} }
return String.format("Filename: %s, Status: %s", filename, readStatus); return String.format("Filename: %s, Status: %s", filename, readStatus);
} }
private HttpRequestResponse createHttpRequestResponse(String key, Map<String, String> httpItemMap) {
HttpService httpService = HttpService.httpService(key);
HttpRequest httpRequest = HttpRequest.httpRequest(httpService, httpItemMap.get("request"));
HttpResponse httpResponse = HttpResponse.httpResponse(httpItemMap.get("response"));
return HttpRequestResponse.httpRequestResponse(httpRequest, httpResponse);
}
private List<String> findFilesWithExtension(File directory, String extension) { private List<String> findFilesWithExtension(File directory, String extension) {
List<String> filePaths = new ArrayList<>(); List<String> filePaths = new ArrayList<>();
if (directory.isDirectory()) { if (directory.isDirectory()) {
@@ -413,8 +550,9 @@ public class Databoard extends JPanel {
} }
} }
} }
} else {
filePaths.add(directory.getAbsolutePath());
} }
filePaths.add(directory.getAbsolutePath());
return filePaths; return filePaths;
} }
@@ -438,19 +576,35 @@ public class Databoard extends JPanel {
} }
private void clearActionPerformed(ActionEvent e) { private void clearActionPerformed(ActionEvent e) {
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to clear data?", "Info", int retCode = JOptionPane.showConfirmDialog(this, "Do you want to clear data?", "Info",
JOptionPane.YES_NO_OPTION); JOptionPane.YES_NO_OPTION);
String host = hostTextField.getText(); String host = hostTextField.getText();
if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) { if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) {
dataTabbedPane.removeAll(); dataTabbedPane.removeAll();
splitPane.setVisible(false); splitPane.setVisible(false);
progressBar.setVisible(false);
String cleanedHost = StringProcessor.replaceFirstOccurrence(host, "*.", ""); Config.globalDataMap.keySet().parallelStream().forEach(key -> {
if (StringProcessor.matchesHostPattern(key, host) || host.equals("*")) {
Config.globalDataMap.remove(key);
}
});
if (host.contains("*")) { if (!StringProcessor.matchHostIsIp(host) && !host.contains("*.")) {
Config.globalDataMap.keySet().removeIf(i -> i.contains(cleanedHost) || cleanedHost.contains("*")); String baseDomain = StringProcessor.getBaseDomain(StringProcessor.extractHostname(host));
} else {
Config.globalDataMap.remove(host); long count = Config.globalDataMap.keySet().stream()
.filter(k -> !k.equals("*." + baseDomain))
.filter(k -> StringProcessor.matchFromEnd(k, baseDomain))
.count();
if (count == 0) {
Config.globalDataMap.remove("*." + baseDomain);
}
}
if (Config.globalDataMap.keySet().size() == 1 && Config.globalDataMap.keySet().stream().anyMatch(key -> key.contains("*"))) {
Config.globalDataMap.keySet().remove("*");
} }
messageTableModel.deleteByHost(host); messageTableModel.deleteByHost(host);

View File

@@ -11,8 +11,10 @@ public class MessageEntry {
private final String status; private final String status;
private final String color; private final String color;
private final String method; private final String method;
private final String hash;
private final String path;
MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status) { MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status, String hash, String path) {
this.requestResponse = requestResponse; this.requestResponse = requestResponse;
this.method = method; this.method = method;
this.url = url; this.url = url;
@@ -20,6 +22,8 @@ public class MessageEntry {
this.length = length; this.length = length;
this.color = color; this.color = color;
this.status = status; this.status = status;
this.hash = hash;
this.path = path;
} }
public String getColor() { public String getColor() {
@@ -49,4 +53,12 @@ public class MessageEntry {
public HttpRequestResponse getRequestResponse() { public HttpRequestResponse getRequestResponse() {
return this.requestResponse; return this.requestResponse;
} }
public String getHash() {
return this.hash;
}
public String getPath() {
return this.path;
}
} }

View File

@@ -11,6 +11,7 @@ import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor; import burp.api.montoya.ui.editor.HttpResponseEditor;
import hae.Config; import hae.Config;
import hae.cache.CachePool; import hae.cache.CachePool;
import hae.utils.project.FileProcessor;
import hae.utils.string.HashCalculator; import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor; import hae.utils.string.StringProcessor;
@@ -22,6 +23,10 @@ import javax.swing.table.TableRowSorter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -32,7 +37,7 @@ public class MessageTableModel extends AbstractTableModel {
private final MessageTable messageTable; private final MessageTable messageTable;
private final JTabbedPane messageTab; private final JTabbedPane messageTab;
private final JSplitPane splitPane; private final JSplitPane splitPane;
private final List<MessageEntry> log = new ArrayList<MessageEntry>(); private final LinkedList<MessageEntry> log = new LinkedList<>();
private final LinkedList<MessageEntry> filteredLog; private final LinkedList<MessageEntry> filteredLog;
public MessageTableModel(MontoyaApi api) { public MessageTableModel(MontoyaApi api) {
@@ -92,25 +97,25 @@ public class MessageTableModel extends AbstractTableModel {
splitPane.setRightComponent(messageTab); splitPane.setRightComponent(messageTab);
} }
public void add(HttpRequestResponse messageInfo, String comment, String color) { public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, String hash, String path) {
synchronized (log) { synchronized (log) {
HttpRequest httpRequest = messageInfo.request(); boolean isDuplicate = false;
String url = httpRequest.url(); MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status, hash, path);
String method = httpRequest.method();
HttpResponse httpResponse = messageInfo.response(); byte[] reqByteA = new byte[0];
String status = String.valueOf(httpResponse.statusCode()); byte[] resByteA = new byte[0];
String length = String.valueOf(httpResponse.body().length());
MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status); if (messageInfo != null) {
HttpRequest httpRequest = messageInfo.request();
HttpResponse httpResponse = messageInfo.response();
reqByteA = httpRequest.toByteArray().getBytes();
resByteA = httpResponse.toByteArray().getBytes();
}
// 比较Hash如若存在重复的请求或响应则不放入消息内容里
try { try {
// 比较Hash如若存在重复的请求或响应则不放入消息内容里 if (!log.isEmpty()) {
byte[] reqByteA = httpRequest.toByteArray().getBytes();
byte[] resByteA = httpResponse.toByteArray().getBytes();
boolean isDuplicate = false;
if (log.size() > 0) {
for (MessageEntry entry : log) { for (MessageEntry entry : log) {
HttpRequestResponse reqResMessage = entry.getRequestResponse(); HttpRequestResponse reqResMessage = entry.getRequestResponse();
byte[] reqByteB = reqResMessage.request().toByteArray().getBytes(); byte[] reqByteB = reqResMessage.request().toByteArray().getBytes();
@@ -125,12 +130,12 @@ public class MessageTableModel extends AbstractTableModel {
} }
} }
} }
if (!isDuplicate) {
log.add(logEntry);
}
} catch (Exception ignored) { } catch (Exception ignored) {
} }
if (!isDuplicate) {
log.add(logEntry);
}
} }
} }
@@ -138,52 +143,113 @@ public class MessageTableModel extends AbstractTableModel {
public void deleteByHost(String filterText) { public void deleteByHost(String filterText) {
filteredLog.clear(); filteredLog.clear();
List<Integer> rowsToRemove = new ArrayList<>(); List<Integer> rowsToRemove = new ArrayList<>();
for (int i = 0; i < log.size(); i++) {
MessageEntry entry = log.get(i); new SwingWorker<Void, Void>() {
String host = StringProcessor.getHostByUrl(entry.getUrl()); @Override
if (!host.isEmpty()) { protected Void doInBackground() {
if (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*")) { for (int i = 0; i < log.size(); i++) {
rowsToRemove.add(i); MessageEntry entry = log.get(i);
String host = StringProcessor.getHostByUrl(entry.getUrl());
if (!host.isEmpty()) {
if (StringProcessor.matchesHostPattern(host, filterText) || filterText.equals("*")) {
rowsToRemove.add(i);
}
}
}
for (int i = rowsToRemove.size() - 1; i >= 0; i--) {
int row = rowsToRemove.get(i);
log.remove(row);
}
return null;
}
@Override
protected void done() {
if (!rowsToRemove.isEmpty()) {
int[] rows = rowsToRemove.stream().mapToInt(Integer::intValue).toArray();
fireTableRowsDeleted(rows[0], rows[rows.length - 1]);
} }
} }
} }.execute();
for (int i = rowsToRemove.size() - 1; i >= 0; i--) {
int row = rowsToRemove.get(i);
log.remove(row);
}
if (!rowsToRemove.isEmpty()) {
int[] rows = rowsToRemove.stream().mapToInt(Integer::intValue).toArray();
fireTableRowsDeleted(rows[0], rows[rows.length - 1]);
}
} }
public void applyHostFilter(String filterText) { public void applyHostFilter(String filterText) {
filteredLog.clear(); filteredLog.clear();
fireTableDataChanged();
String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", "");
for (MessageEntry entry : log) { ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
String host = StringProcessor.getHostByUrl(entry.getUrl()); List<Future<?>> futures = new ArrayList<>();
if (!host.isEmpty()) { try {
if (filterText.contains("*.") && StringProcessor.matchFromEnd(StringProcessor.extractHostname(host), cleanedText)) { log.parallelStream().forEach(entry -> {
filteredLog.add(entry); Future<?> future = executor.submit(() -> {
} else if (host.equals(filterText) || filterText.contains("*")) { MessageEntry finalEntry = getEntryByFile(entry);
filteredLog.add(entry); String host = StringProcessor.getHostByUrl(finalEntry.getUrl());
if (!host.isEmpty()) {
synchronized (filteredLog) {
if (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*")) {
filteredLog.add(finalEntry);
}
}
}
});
futures.add(future);
});
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
} }
} }
} catch (Exception e) {
api.logging().logToError("applyHostFilter: " + e.getMessage());
} finally {
executor.shutdown();
fireTableDataChanged();
}
}
private MessageEntry getEntryByFile(MessageEntry entry) {
HttpRequestResponse requestResponse = entry.getRequestResponse();
if (requestResponse == null) {
String url = entry.getUrl();
String method = entry.getMethod();
String status = entry.getStatus();
String comment = entry.getComment();
String color = entry.getColor();
String path = entry.getPath();
String hash = entry.getHash();
int length = Integer.parseInt(entry.getLength());
byte[] contents = FileProcessor.readFileContent(path, hash);
if (contents.length > length) {
byte[] response = Arrays.copyOf(contents, length);
byte[] request = Arrays.copyOfRange(contents, length, contents.length);
requestResponse = StringProcessor.createHttpRequestResponse(url, request, response);
int index = log.indexOf(entry);
entry = new MessageEntry(requestResponse, method, url, comment, String.valueOf(length), color, status, "", "");
log.set(index, entry);
}
} }
fireTableDataChanged(); return entry;
} }
public void applyMessageFilter(String tableName, String filterText) { public void applyMessageFilter(String tableName, String filterText) {
filteredLog.clear(); filteredLog.clear();
for (MessageEntry entry : log) { for (MessageEntry entry : log) {
HttpRequestResponse requestResponse = entry.getRequestResponse(); HttpRequestResponse requestResponse = entry.getRequestResponse();
HttpRequest httpRequest = requestResponse.request(); // 标志变量,表示是否满足过滤条件
HttpResponse httpResponse = requestResponse.response(); AtomicBoolean isMatched = new AtomicBoolean(false);
HttpRequest httpRequest = entry.getRequestResponse().request();
HttpResponse httpResponse = entry.getRequestResponse().response();
String requestString = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8); String requestString = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8);
String requestBody = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8); String requestBody = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8);
@@ -197,9 +263,7 @@ public class MessageTableModel extends AbstractTableModel {
.map(HttpHeader::toString) .map(HttpHeader::toString)
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
// 标志变量,表示是否满足过滤条件 MessageEntry finalEntry = entry;
AtomicBoolean isMatched = new AtomicBoolean(false);
Config.globalRules.keySet().forEach(i -> { Config.globalRules.keySet().forEach(i -> {
for (Object[] objects : Config.globalRules.get(i)) { for (Object[] objects : Config.globalRules.get(i)) {
String name = objects[1].toString(); String name = objects[1].toString();
@@ -207,7 +271,7 @@ public class MessageTableModel extends AbstractTableModel {
String scope = objects[6].toString(); String scope = objects[6].toString();
// 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间 // 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间
if (entry.getComment().contains(name)) { if (finalEntry.getComment().contains(name)) {
if (name.equals(tableName)) { if (name.equals(tableName)) {
// 标志变量,表示当前规则是否匹配 // 标志变量,表示当前规则是否匹配
boolean isMatch = false; boolean isMatch = false;
@@ -305,7 +369,7 @@ public class MessageTableModel extends AbstractTableModel {
if (!map2.containsKey(key)) { if (!map2.containsKey(key)) {
return false; return false;
} }
if (!areInnerMapsEqual(map1.get(key), map2.get(key))) { if (areInnerMapsEqual(map1.get(key), map2.get(key))) {
return false; return false;
} }
} }
@@ -315,30 +379,29 @@ public class MessageTableModel extends AbstractTableModel {
private boolean areInnerMapsEqual(Map<String, Object> innerMap1, Map<String, Object> innerMap2) { private boolean areInnerMapsEqual(Map<String, Object> innerMap1, Map<String, Object> innerMap2) {
if (innerMap1.size() != innerMap2.size()) { if (innerMap1.size() != innerMap2.size()) {
return false; return true;
} }
for (String key : innerMap1.keySet()) { for (String key : innerMap1.keySet()) {
if (!innerMap2.containsKey(key)) { if (!innerMap2.containsKey(key)) {
return false; return true;
} }
Object value1 = innerMap1.get(key); Object value1 = innerMap1.get(key);
Object value2 = innerMap2.get(key); Object value2 = innerMap2.get(key);
// 如果值是Map则递归对比 // 如果值是Map则递归对比
if (value1 instanceof Map && value2 instanceof Map) { if (value1 instanceof Map && value2 instanceof Map) {
if (!areInnerMapsEqual((Map<String, Object>) value1, (Map<String, Object>) value2)) { if (areInnerMapsEqual((Map<String, Object>) value1, (Map<String, Object>) value2)) {
return false; return true;
} }
} else if (!value1.equals(value2)) { } else if (!value1.equals(value2)) {
return false; return true;
} }
} }
return true; return false;
} }
public JSplitPane getSplitPane() { public JSplitPane getSplitPane() {
return splitPane; return splitPane;
} }
@@ -366,6 +429,7 @@ public class MessageTableModel extends AbstractTableModel {
if (filteredLog.isEmpty()) { if (filteredLog.isEmpty()) {
return ""; return "";
} }
MessageEntry messageEntry = filteredLog.get(rowIndex); MessageEntry messageEntry = filteredLog.get(rowIndex);
return switch (columnIndex) { return switch (columnIndex) {
@@ -393,7 +457,7 @@ public class MessageTableModel extends AbstractTableModel {
} }
public class MessageTable extends JTable { public class MessageTable extends JTable {
private MessageEntry MessageEntry; private MessageEntry messageEntry;
private SwingWorker<Object, Void> currentWorker; private SwingWorker<Object, Void> currentWorker;
// 设置响应报文返回的最大长度 // 设置响应报文返回的最大长度
private final int MAX_LENGTH = 5242880; private final int MAX_LENGTH = 5242880;
@@ -413,7 +477,7 @@ public class MessageTableModel extends AbstractTableModel {
int selectedIndex = convertRowIndexToModel(row); int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) { if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex; lastSelectedIndex = selectedIndex;
MessageEntry = filteredLog.get(selectedIndex); messageEntry = filteredLog.get(selectedIndex);
requestEditor.setRequest(HttpRequest.httpRequest("Loading...")); requestEditor.setRequest(HttpRequest.httpRequest("Loading..."));
responseEditor.setResponse(HttpResponse.httpResponse("Loading...")); responseEditor.setResponse(HttpResponse.httpResponse("Loading..."));
@@ -425,8 +489,10 @@ public class MessageTableModel extends AbstractTableModel {
currentWorker = new SwingWorker<>() { currentWorker = new SwingWorker<>() {
@Override @Override
protected ByteArray[] doInBackground() { protected ByteArray[] doInBackground() {
ByteArray requestByte = MessageEntry.getRequestResponse().request().toByteArray(); HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
ByteArray responseByte = MessageEntry.getRequestResponse().response().toByteArray();
ByteArray requestByte = messageEntry.getRequestResponse().request().toByteArray();
ByteArray responseByte = messageEntry.getRequestResponse().response().toByteArray();
if (responseByte.length() > MAX_LENGTH) { if (responseByte.length() > MAX_LENGTH) {
String ellipsis = "\r\n......"; String ellipsis = "\r\n......";
@@ -441,7 +507,7 @@ public class MessageTableModel extends AbstractTableModel {
if (!isCancelled()) { if (!isCancelled()) {
try { try {
ByteArray[] result = (ByteArray[]) get(); ByteArray[] result = (ByteArray[]) get();
requestEditor.setRequest(HttpRequest.httpRequest(MessageEntry.getRequestResponse().httpService(), result[0])); requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), result[0]));
responseEditor.setResponse(HttpResponse.httpResponse(result[1])); responseEditor.setResponse(HttpResponse.httpResponse(result[1]));
} catch (Exception ignored) { } catch (Exception ignored) {
} }

View File

@@ -250,7 +250,7 @@ public class Config extends JPanel {
private void onlineUpdateActionPerformed(ActionEvent e) { private void onlineUpdateActionPerformed(ActionEvent e) {
// 添加提示框防止用户误触导致配置更新 // 添加提示框防止用户误触导致配置更新
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION); int retCode = JOptionPane.showConfirmDialog(this, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) { if (retCode == JOptionPane.YES_OPTION) {
configLoader.initRulesByNet(); configLoader.initRulesByNet();
reloadActionPerformed(null); reloadActionPerformed(null);

View File

@@ -97,7 +97,7 @@ public class Rule extends JPanel {
Display ruleDisplay = new Display(); Display ruleDisplay = new Display();
ruleDisplay.formatTextField.setText("{0}"); ruleDisplay.formatTextField.setText("{0}");
int showState = JOptionPane.showConfirmDialog(null, ruleDisplay, "Add Rule", JOptionPane.OK_OPTION); int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Add Rule", JOptionPane.OK_OPTION);
if (showState == YES_OPTION) { if (showState == YES_OPTION) {
Vector<Object> ruleData = new Vector<>(); Vector<Object> ruleData = new Vector<>();
ruleData.add(false); ruleData.add(false);
@@ -132,7 +132,7 @@ public class Rule extends JPanel {
ruleDisplay.formatTextField.setEnabled(ruleDisplay.engineComboBox.getSelectedItem().toString().equals("nfa")); ruleDisplay.formatTextField.setEnabled(ruleDisplay.engineComboBox.getSelectedItem().toString().equals("nfa"));
int showState = JOptionPane.showConfirmDialog(null, ruleDisplay, "Edit Rule", JOptionPane.OK_OPTION); int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Edit Rule", JOptionPane.OK_OPTION);
if (showState == 0) { if (showState == 0) {
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
model.setValueAt(ruleDisplay.ruleNameTextField.getText(), select, 1); model.setValueAt(ruleDisplay.ruleNameTextField.getText(), select, 1);
@@ -151,7 +151,7 @@ public class Rule extends JPanel {
private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) { private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
if (ruleTable.getSelectedRowCount() >= 1) { if (ruleTable.getSelectedRowCount() >= 1) {
if (JOptionPane.showConfirmDialog(null, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) { if (JOptionPane.showConfirmDialog(this, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) {
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel(); DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());

View File

@@ -109,7 +109,7 @@ public class Rules extends JTabbedPane {
private void deleteRuleGroupActionPerformed(ActionEvent e) { private void deleteRuleGroupActionPerformed(ActionEvent e) {
if (getTabCount() > 2) { if (getTabCount() > 2) {
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to delete this rule group?", "Info", int retCode = JOptionPane.showConfirmDialog(this, "Do you want to delete this rule group?", "Info",
JOptionPane.YES_NO_OPTION); JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) { if (retCode == JOptionPane.YES_OPTION) {
String title = getTitleAt(getSelectedIndex()); String title = getTitleAt(getSelectedIndex());

View File

@@ -122,7 +122,9 @@ public class RequestEditor implements HttpRequestEditorProvider {
boolean isBlockHost = false; boolean isBlockHost = false;
for (String hostName : hostList) { for (String hostName : hostList) {
String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", ""); String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", "");
if (StringProcessor.matchFromEnd(host, cleanedHost)) { if (hostName.contains("*.") && StringProcessor.matchFromEnd(host, cleanedHost)) {
isBlockHost = true;
} else if (host.equals(hostName) || hostName.equals("*")) {
isBlockHost = true; isBlockHost = true;
} }
} }

View File

@@ -80,7 +80,12 @@ public class HttpMessageHandler implements HttpHandler {
HttpRequestResponse httpRequestResponse = HttpRequestResponse.httpRequestResponse(httpRequest.get(), httpResponseReceived); HttpRequestResponse httpRequestResponse = HttpRequestResponse.httpRequestResponse(httpRequest.get(), httpResponseReceived);
// 添加到Databoard // 添加到Databoard
messageTableModel.add(httpRequestResponse, comment, color); String method = httpRequest.get().method();
String url = httpRequest.get().url();
String status = String.valueOf(httpResponseReceived.statusCode());
String length = String.valueOf(httpResponseReceived.toByteArray().length());
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", "");
} }
} }

View File

@@ -126,7 +126,7 @@ public class RegularMatcher {
String[] splitHost = host.split("\\."); String[] splitHost = host.split("\\.");
String onlyHost = host.split(":")[0]; String onlyHost = host.split(":")[0];
String anyHost = (splitHost.length > 2 && !onlyHost.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b")) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : ""; String anyHost = (splitHost.length > 2 && !StringProcessor.matchHostIsIp(onlyHost)) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : "";
if (!Config.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) { if (!Config.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) {
// 添加通配符Host实际数据从查询哪里将所有数据提取 // 添加通配符Host实际数据从查询哪里将所有数据提取

View File

@@ -0,0 +1,47 @@
package hae.utils.project;
import java.io.File;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
public class FileProcessor {
public static void deleteDirectoryWithContents(Path pathToBeDeleted) {
if (pathToBeDeleted != null) {
try {
Files.walk(pathToBeDeleted)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (Exception ignored) {
}
}
}
public static byte[] readFileContent(String basePath, String fileName) {
Path filePath = Paths.get(basePath, fileName);
Path path = Paths.get(basePath);
try {
byte[] fileContent = Files.readAllBytes(filePath);
Files.deleteIfExists(filePath);
boolean isEmpty = isDirectoryEmpty(path);
if (isEmpty) {
Files.deleteIfExists(path);
}
return fileContent;
} catch (Exception e) {
return new byte[0];
}
}
private static boolean isDirectoryEmpty(Path directory) throws Exception {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(directory)) {
return !dirStream.iterator().hasNext();
}
}
}

View File

@@ -2,14 +2,21 @@ package hae.utils.project;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import hae.utils.project.model.HaeFileContent; import hae.utils.project.model.HaeFileContent;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.nio.file.Files;
import java.util.Map; import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
public class ProjectProcessor { public class ProjectProcessor {
@@ -19,59 +26,169 @@ public class ProjectProcessor {
this.api = api; this.api = api;
} }
public boolean createHaeFile(String haeFilePath, String host, Map<String, List<String>> dataMap, Map<String, Map<String, String>> httpMap) { public boolean createHaeFile(String haeFilePath, String host, Map<String, List<String>> dataMap, Map<String, Map<String, Object>> urlMap, Map<String, Map<String, Object>> httpMap) {
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<Future<?>> futures = new ArrayList<>();
ByteArrayOutputStream dataYamlStream = new ByteArrayOutputStream(); ByteArrayOutputStream dataYamlStream = new ByteArrayOutputStream();
ByteArrayOutputStream httpYamlStream = new ByteArrayOutputStream(); ByteArrayOutputStream urlYamlStream = new ByteArrayOutputStream();
Yaml yaml = new Yaml(); Yaml yaml = new Yaml();
yaml.dump(dataMap, new OutputStreamWriter(dataYamlStream, StandardCharsets.UTF_8)); yaml.dump(dataMap, new OutputStreamWriter(dataYamlStream, StandardCharsets.UTF_8));
yaml.dump(httpMap, new OutputStreamWriter(httpYamlStream, StandardCharsets.UTF_8)); yaml.dump(urlMap, new OutputStreamWriter(urlYamlStream, StandardCharsets.UTF_8));
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(haeFilePath))) { try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(haeFilePath))) {
zipOut.putNextEntry(new ZipEntry("info")); zipOut.putNextEntry(new ZipEntry("info"));
zipOut.write(host.getBytes(StandardCharsets.UTF_8)); zipOut.write(host.getBytes(StandardCharsets.UTF_8));
zipOut.closeEntry(); zipOut.closeEntry();
zipOut.putNextEntry(new ZipEntry("data.yml")); zipOut.putNextEntry(new ZipEntry("data"));
zipOut.write(dataYamlStream.toByteArray()); zipOut.write(dataYamlStream.toByteArray());
zipOut.closeEntry(); zipOut.closeEntry();
zipOut.putNextEntry(new ZipEntry("http.yml")); zipOut.putNextEntry(new ZipEntry("url"));
zipOut.write(httpYamlStream.toByteArray()); zipOut.write(urlYamlStream.toByteArray());
zipOut.closeEntry(); zipOut.closeEntry();
for (String httpHash : httpMap.keySet()) {
Map<String, Object> httpItem = httpMap.get(httpHash);
futures.add(executorService.submit(() -> {
try {
ByteArrayOutputStream httpOutStream = new ByteArrayOutputStream();
byte[] request = (byte[]) httpItem.get("request");
byte[] response = (byte[]) httpItem.get("response");
httpOutStream.write(response);
httpOutStream.write(request);
synchronized (zipOut) {
zipOut.putNextEntry(new ZipEntry(String.format("http/%s", httpHash)));
zipOut.write(httpOutStream.toByteArray());
zipOut.closeEntry();
}
} catch (Exception e) {
api.logging().logToError("createHaeFile: " + e.getMessage());
}
}));
}
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
}
}
} catch (Exception e) { } catch (Exception e) {
api.logging().logToOutput(e.getMessage()); api.logging().logToError("createHaeFile: " + e.getMessage());
return false; return false;
} finally {
executorService.shutdown();
} }
return true; return true;
} }
public HaeFileContent readHaeFile(String haeFilePath) { public HaeFileContent readHaeFile(String haeFilePath) {
HaeFileContent haeFileContent = new HaeFileContent(api); ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Yaml yaml = new Yaml(); List<Future<?>> futures = new ArrayList<>();
try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(haeFilePath))) { HaeFileContent haeFileContent = new HaeFileContent(api); // 假设api是正确的
ZipEntry entry; LoaderOptions loaderOptions = new LoaderOptions();
while ((entry = zipIn.getNextEntry()) != null) { loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
switch (entry.getName()) { loaderOptions.setCodePointLimit(Integer.MAX_VALUE);
case "info": Yaml yaml = new Yaml(loaderOptions);
haeFileContent.setHost(new String(zipIn.readAllBytes(), StandardCharsets.UTF_8)); Path tempDirectory = null;
break;
case "data.yml": try {
haeFileContent.setDataMap(yaml.load(new InputStreamReader(zipIn, StandardCharsets.UTF_8))); if (hasValidStructure(haeFilePath)) {
break; tempDirectory = Files.createTempDirectory("hae");
case "http.yml": haeFileContent.setHttpPath(tempDirectory.toString());
haeFileContent.setHttpMap(yaml.load(new InputStreamReader(zipIn, StandardCharsets.UTF_8)));
break; try (ZipFile zipFile = new ZipFile(haeFilePath)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String fileName = entry.getName();
if (fileName.startsWith("http/")) {
Path filePath = tempDirectory.resolve(fileName.substring("http/".length()));
futures.add(executorService.submit(() -> {
try (InputStream in = zipFile.getInputStream(entry)) {
Files.copy(in, filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
api.logging().logToError("readHaeFile: " + e.getMessage());
}
}));
} else {
try (InputStream in = zipFile.getInputStream(entry)) {
switch (fileName) {
case "info" ->
haeFileContent.setHost(new String(in.readAllBytes(), StandardCharsets.UTF_8));
case "data" ->
haeFileContent.setDataMap(yaml.load(new InputStreamReader(in, StandardCharsets.UTF_8)));
case "url" ->
haeFileContent.setUrlMap(yaml.load(new InputStreamReader(in, StandardCharsets.UTF_8)));
}
}
}
}
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
}
}
} }
zipIn.closeEntry();
} }
} catch (Exception e) { } catch (Exception e) {
api.logging().logToOutput(e.getMessage()); api.logging().logToError("readHaeFile: " + e.getMessage());
return null; if (tempDirectory != null) {
FileProcessor.deleteDirectoryWithContents(tempDirectory);
}
haeFileContent = null;
} finally {
executorService.shutdown();
} }
return haeFileContent; return haeFileContent;
} }
private boolean hasValidStructure(String zipFilePath) {
Set<String> requiredRootEntries = new HashSet<>();
requiredRootEntries.add("info");
requiredRootEntries.add("data");
requiredRootEntries.add("url");
boolean hasHttpDirectoryWithFiles = false;
try {
ZipFile zipFile = new ZipFile(zipFilePath);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (!entry.isDirectory() && !name.contains("/")) {
requiredRootEntries.remove(name);
}
if (name.startsWith("http/") && !entry.isDirectory()) {
hasHttpDirectoryWithFiles = true;
}
if (requiredRootEntries.isEmpty() && hasHttpDirectoryWithFiles) {
break;
}
}
zipFile.close();
} catch (Exception ignored) {
}
return requiredRootEntries.isEmpty() && hasHttpDirectoryWithFiles;
}
} }

View File

@@ -11,13 +11,14 @@ import java.util.Map;
public class HaeFileContent { public class HaeFileContent {
private final MontoyaApi api; private final MontoyaApi api;
private String host; private String host;
private String httpPath;
private final Map<String, List<String>> dataMap; private final Map<String, List<String>> dataMap;
private final Map<String, Map<String, String>> httpMap; private final Map<String, Map<String, String>> urlMap;
public HaeFileContent(MontoyaApi api) { public HaeFileContent(MontoyaApi api) {
this.api = api; this.api = api;
this.dataMap = new HashMap<>(); this.dataMap = new HashMap<>();
this.httpMap = new HashMap<>(); this.urlMap = new HashMap<>();
} }
public String getHost() { public String getHost() {
@@ -28,14 +29,22 @@ public class HaeFileContent {
return dataMap; return dataMap;
} }
public Map<String, Map<String, String>> getHttpMap() { public Map<String, Map<String, String>> getUrlMap() {
return httpMap; return urlMap;
}
public String getHttpPath() {
return httpPath;
} }
public void setHost(String host) { public void setHost(String host) {
this.host = host; this.host = host;
} }
public void setHttpPath(String path) {
this.httpPath = path;
}
public void setDataMap(Map<String, List<Object>> dataMap) { public void setDataMap(Map<String, List<Object>> dataMap) {
for (Map.Entry<String, List<Object>> entry : dataMap.entrySet()) { for (Map.Entry<String, List<Object>> entry : dataMap.entrySet()) {
List<String> values = new ArrayList<>(); List<String> values = new ArrayList<>();
@@ -50,8 +59,8 @@ public class HaeFileContent {
} }
} }
public void setHttpMap(Map<String, Map<String, Object>> httpMap) { public void setUrlMap(Map<String, Map<String, Object>> urlMap) {
for (Map.Entry<String, Map<String, Object>> entry : httpMap.entrySet()) { for (Map.Entry<String, Map<String, Object>> entry : urlMap.entrySet()) {
Map<String, String> newValues = new HashMap<>(); Map<String, String> newValues = new HashMap<>();
Map<String, Object> values = entry.getValue(); Map<String, Object> values = entry.getValue();
for (String key : values.keySet()) { for (String key : values.keySet()) {
@@ -61,7 +70,7 @@ public class HaeFileContent {
newValues.put(key, values.get(key).toString()); newValues.put(key, values.get(key).toString());
} }
} }
this.httpMap.put(entry.getKey(), newValues); this.urlMap.put(entry.getKey(), newValues);
} }
} }
} }

View File

@@ -1,8 +1,17 @@
package hae.utils.string; package hae.utils.string;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.http.HttpService;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import java.net.URL; import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
public class StringProcessor { public class StringProcessor {
public static String replaceFirstOccurrence(String original, String find, String replace) { public static String replaceFirstOccurrence(String original, String find, String replace) {
@@ -55,6 +64,24 @@ public class StringProcessor {
return matchesDirectly || matchesPattern; return matchesDirectly || matchesPattern;
} }
public static HttpRequestResponse createHttpRequestResponse(String url, byte[] request, byte[] response) {
HttpService httpService = HttpService.httpService(url);
HttpRequest httpRequest = HttpRequest.httpRequest(httpService, ByteArray.byteArray(request));
HttpResponse httpResponse = HttpResponse.httpResponse(ByteArray.byteArray(response));
return HttpRequestResponse.httpRequestResponse(httpRequest, httpResponse);
}
public static String getCurrentTime() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
LocalDateTime now = LocalDateTime.now();
return now.format(formatter);
}
public static String getRandomUUID() {
UUID uuid = UUID.randomUUID();
return uuid.toString();
}
public static String mergeComment(String comment) { public static String mergeComment(String comment) {
if (!comment.contains(",")) { if (!comment.contains(",")) {
return comment; return comment;
@@ -92,6 +119,21 @@ public class StringProcessor {
return host; return host;
} }
public static String getBaseDomain(String host) {
int lastIndex = host.lastIndexOf('.');
if (lastIndex > 0) {
int secondLastIndex = host.substring(0, lastIndex).lastIndexOf('.');
if (secondLastIndex >= 0) {
return host.substring(secondLastIndex + 1);
}
}
return host;
}
public static boolean matchHostIsIp(String host) {
return host.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b");
}
private static Map<String, Integer> getStringIntegerMap(String comment) { private static Map<String, Integer> getStringIntegerMap(String comment) {
Map<String, Integer> itemCounts = new HashMap<>(); Map<String, Integer> itemCounts = new HashMap<>();
String[] items = comment.split(", "); String[] items = comment.split(", ");