Files
HaE/src/main/java/hae/component/board/message/MessageTableModel.java

522 lines
21 KiB
Java
Raw Normal View History

2024-05-06 12:56:56 +08:00
package hae.component.board.message;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.http.message.HttpHeader;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.ui.UserInterface;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor;
import hae.Config;
import hae.cache.CachePool;
2024-05-30 14:37:01 +08:00
import hae.utils.project.FileProcessor;
2024-05-06 12:56:56 +08:00
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
2024-05-12 19:02:38 +08:00
import javax.swing.*;
2023-10-12 21:38:27 +08:00
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
2024-05-12 19:02:38 +08:00
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
2024-05-30 14:37:01 +08:00
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
2024-05-06 12:56:56 +08:00
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static burp.api.montoya.ui.editor.EditorOptions.READ_ONLY;
public class MessageTableModel extends AbstractTableModel {
private final MontoyaApi api;
private final MessageTable messageTable;
private final JTabbedPane messageTab;
private final JSplitPane splitPane;
2024-05-30 14:37:01 +08:00
private final LinkedList<MessageEntry> log = new LinkedList<>();
2024-05-12 19:02:38 +08:00
private final LinkedList<MessageEntry> filteredLog;
2024-05-06 12:56:56 +08:00
public MessageTableModel(MontoyaApi api) {
this.filteredLog = new LinkedList<>();
this.api = api;
messageTab = new JTabbedPane();
UserInterface userInterface = api.userInterface();
HttpRequestEditor requestViewer = userInterface.createHttpRequestEditor(READ_ONLY);
HttpResponseEditor responseViewer = userInterface.createHttpResponseEditor(READ_ONLY);
messageTab.addTab("Request", requestViewer.uiComponent());
messageTab.addTab("Response", responseViewer.uiComponent());
// 请求条目表格
messageTable = new MessageTable(MessageTableModel.this, requestViewer, responseViewer);
messageTable.setDefaultRenderer(Object.class, new MessageRenderer(filteredLog, messageTable));
messageTable.setAutoCreateRowSorter(true);
2023-10-12 21:38:27 +08:00
// Length字段根据大小进行排序
2024-05-06 12:56:56 +08:00
TableRowSorter<DefaultTableModel> sorter = (TableRowSorter<DefaultTableModel>) messageTable.getRowSorter();
2023-10-26 14:17:56 +08:00
sorter.setComparator(4, new Comparator<String>() {
2023-10-12 21:38:27 +08:00
@Override
public int compare(String s1, String s2) {
Integer age1 = Integer.parseInt(s1);
Integer age2 = Integer.parseInt(s2);
return age1.compareTo(age2);
}
});
2024-01-18 12:07:20 +08:00
2023-10-12 21:38:27 +08:00
// Color字段根据颜色顺序进行排序
2023-10-26 14:17:56 +08:00
sorter.setComparator(5, new Comparator<String>() {
2023-10-12 21:38:27 +08:00
@Override
public int compare(String s1, String s2) {
int index1 = getIndex(s1);
int index2 = getIndex(s2);
return Integer.compare(index1, index2);
}
2024-05-12 19:02:38 +08:00
2023-10-12 21:38:27 +08:00
private int getIndex(String color) {
2024-05-06 12:56:56 +08:00
for (int i = 0; i < Config.color.length; i++) {
if (Config.color[i].equals(color)) {
2023-10-12 21:38:27 +08:00
return i;
}
}
return -1;
}
});
2024-05-06 12:56:56 +08:00
messageTable.setRowSorter(sorter);
messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
2023-10-12 21:38:27 +08:00
2024-05-06 12:56:56 +08:00
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
// 请求/相应文本框
JScrollPane scrollPane = new JScrollPane(messageTable);
2023-10-12 21:38:27 +08:00
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
splitPane.setLeftComponent(scrollPane);
2024-05-06 12:56:56 +08:00
splitPane.setRightComponent(messageTab);
2023-10-12 21:38:27 +08:00
}
2024-05-30 14:37:01 +08:00
public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, String hash, String path) {
2024-05-12 19:02:38 +08:00
synchronized (log) {
2024-05-30 14:37:01 +08:00
boolean isDuplicate = false;
MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status, hash, path);
2023-10-12 21:38:27 +08:00
2024-05-30 14:37:01 +08:00
byte[] reqByteA = new byte[0];
byte[] resByteA = new byte[0];
2023-10-12 21:38:27 +08:00
2024-05-30 14:37:01 +08:00
if (messageInfo != null) {
HttpRequest httpRequest = messageInfo.request();
HttpResponse httpResponse = messageInfo.response();
2023-10-12 21:38:27 +08:00
2024-05-30 14:37:01 +08:00
reqByteA = httpRequest.toByteArray().getBytes();
resByteA = httpResponse.toByteArray().getBytes();
}
2023-10-12 21:38:27 +08:00
2024-05-30 14:37:01 +08:00
// 比较Hash如若存在重复的请求或响应则不放入消息内容里
try {
if (!log.isEmpty()) {
2024-05-06 12:56:56 +08:00
for (MessageEntry entry : log) {
HttpRequestResponse reqResMessage = entry.getRequestResponse();
byte[] reqByteB = reqResMessage.request().toByteArray().getBytes();
byte[] resByteB = reqResMessage.response().toByteArray().getBytes();
try {
// 通过URL、请求和响应报文、匹配数据内容多维度进行对比
2024-05-12 19:02:38 +08:00
if ((entry.getUrl().equals(url) || (Arrays.equals(reqByteB, reqByteA) || Arrays.equals(resByteB, resByteA))) && (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA)))) {
2024-05-06 12:56:56 +08:00
isDuplicate = true;
break;
}
} catch (Exception ignored) {
}
}
}
} catch (Exception ignored) {
}
2024-05-30 14:37:01 +08:00
if (!isDuplicate) {
log.add(logEntry);
}
2023-10-12 21:38:27 +08:00
}
}
2024-05-06 12:56:56 +08:00
public void deleteByHost(String filterText) {
filteredLog.clear();
List<Integer> rowsToRemove = new ArrayList<>();
2024-05-30 14:37:01 +08:00
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
for (int i = 0; i < log.size(); 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);
}
}
2024-05-06 12:56:56 +08:00
}
2024-05-30 14:37:01 +08:00
for (int i = rowsToRemove.size() - 1; i >= 0; i--) {
int row = rowsToRemove.get(i);
log.remove(row);
}
2024-05-06 12:56:56 +08:00
2024-05-30 14:37:01 +08:00
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();
2023-10-12 21:38:27 +08:00
}
public void applyHostFilter(String filterText) {
filteredLog.clear();
2024-05-06 12:56:56 +08:00
2024-05-30 14:37:01 +08:00
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
List<Future<?>> futures = new ArrayList<>();
try {
log.parallelStream().forEach(entry -> {
Future<?> future = executor.submit(() -> {
MessageEntry finalEntry = getEntryByFile(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();
2024-05-06 12:56:56 +08:00
}
2023-10-12 21:38:27 +08:00
}
2024-05-30 14:37:01 +08:00
} catch (Exception e) {
api.logging().logToError("applyHostFilter: " + e.getMessage());
} finally {
executor.shutdown();
fireTableDataChanged();
2023-10-12 21:38:27 +08:00
}
2024-05-30 14:37:01 +08:00
}
2024-05-06 12:56:56 +08:00
2024-05-30 14:37:01 +08:00
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);
}
}
return entry;
2023-10-12 21:38:27 +08:00
}
public void applyMessageFilter(String tableName, String filterText) {
filteredLog.clear();
2024-05-06 12:56:56 +08:00
for (MessageEntry entry : log) {
HttpRequestResponse requestResponse = entry.getRequestResponse();
2024-05-30 14:37:01 +08:00
// 标志变量,表示是否满足过滤条件
AtomicBoolean isMatched = new AtomicBoolean(false);
HttpRequest httpRequest = entry.getRequestResponse().request();
HttpResponse httpResponse = entry.getRequestResponse().response();
2024-05-06 12:56:56 +08:00
String requestString = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8);
String requestBody = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8);
String requestHeaders = httpRequest.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
String responseString = new String(httpResponse.toByteArray().getBytes(), StandardCharsets.UTF_8);
String responseBody = new String(httpResponse.body().getBytes(), StandardCharsets.UTF_8);
String responseHeaders = httpResponse.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
2023-10-12 21:38:27 +08:00
2024-05-30 14:37:01 +08:00
MessageEntry finalEntry = entry;
2024-05-06 12:56:56 +08:00
Config.globalRules.keySet().forEach(i -> {
for (Object[] objects : Config.globalRules.get(i)) {
2023-10-12 21:38:27 +08:00
String name = objects[1].toString();
2024-02-02 19:07:03 +08:00
String format = objects[4].toString();
String scope = objects[6].toString();
// 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间
2024-05-30 14:37:01 +08:00
if (finalEntry.getComment().contains(name)) {
2024-02-02 19:07:03 +08:00
if (name.equals(tableName)) {
// 标志变量,表示当前规则是否匹配
boolean isMatch = false;
switch (scope) {
case "any":
isMatch = matchingString(format, filterText, requestString) || matchingString(format, filterText, responseString);
break;
case "request":
isMatch = matchingString(format, filterText, requestString);
break;
case "response":
isMatch = matchingString(format, filterText, responseString);
break;
case "any header":
isMatch = matchingString(format, filterText, requestHeaders) || matchingString(format, filterText, responseHeaders);
break;
case "request header":
isMatch = matchingString(format, filterText, requestHeaders);
break;
case "response header":
isMatch = matchingString(format, filterText, responseHeaders);
break;
case "any body":
isMatch = matchingString(format, filterText, requestBody) || matchingString(format, filterText, responseBody);
break;
case "request body":
isMatch = matchingString(format, filterText, requestBody);
break;
case "response body":
isMatch = matchingString(format, filterText, responseBody);
break;
2024-05-12 19:02:38 +08:00
case "request line":
String requestLine = requestString.split("\\r?\\n", 2)[0];
isMatch = matchingString(format, filterText, requestLine);
break;
case "response line":
String responseLine = responseString.split("\\r?\\n", 2)[0];
isMatch = matchingString(format, filterText, responseLine);
break;
2024-02-02 19:07:03 +08:00
default:
break;
}
isMatched.set(isMatch);
2023-10-12 21:38:27 +08:00
break;
}
}
}
});
2024-02-02 19:07:03 +08:00
if (isMatched.get()) {
2023-10-12 21:38:27 +08:00
filteredLog.add(entry);
}
}
2024-05-06 12:56:56 +08:00
2023-10-12 21:38:27 +08:00
fireTableDataChanged();
2024-05-06 12:56:56 +08:00
messageTable.lastSelectedIndex = -1;
2023-10-12 21:38:27 +08:00
}
2024-02-02 19:07:03 +08:00
private boolean matchingString(String format, String filterText, String target) {
boolean isMatch = true;
try {
MessageFormat mf = new MessageFormat(format);
Object[] parsedObjects = mf.parse(filterText);
for (Object parsedObject : parsedObjects) {
if (!target.contains(parsedObject.toString())) {
isMatch = false;
break;
}
}
} catch (Exception e) {
isMatch = false;
}
return isMatch;
}
2024-05-06 12:56:56 +08:00
private Map<String, Map<String, Object>> getCacheData(byte[] content) {
2023-10-24 17:51:21 +08:00
String hashIndex = HashCalculator.calculateHash(content);
2024-05-23 12:00:13 +08:00
return CachePool.get(hashIndex);
2023-10-24 17:51:21 +08:00
}
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
2023-11-07 11:15:20 +08:00
if (map1 == null || map2 == null) {
return false;
}
2023-10-24 17:51:21 +08:00
if (map1.size() != map2.size()) {
return false;
}
for (String key : map1.keySet()) {
if (!map2.containsKey(key)) {
return false;
}
2024-05-30 14:37:01 +08:00
if (areInnerMapsEqual(map1.get(key), map2.get(key))) {
2023-10-24 17:51:21 +08:00
return false;
}
}
return true;
}
private boolean areInnerMapsEqual(Map<String, Object> innerMap1, Map<String, Object> innerMap2) {
if (innerMap1.size() != innerMap2.size()) {
2024-05-30 14:37:01 +08:00
return true;
2023-10-24 17:51:21 +08:00
}
for (String key : innerMap1.keySet()) {
if (!innerMap2.containsKey(key)) {
2024-05-30 14:37:01 +08:00
return true;
2023-10-24 17:51:21 +08:00
}
Object value1 = innerMap1.get(key);
Object value2 = innerMap2.get(key);
// 如果值是Map则递归对比
if (value1 instanceof Map && value2 instanceof Map) {
2024-05-30 14:37:01 +08:00
if (areInnerMapsEqual((Map<String, Object>) value1, (Map<String, Object>) value2)) {
return true;
2023-10-24 17:51:21 +08:00
}
} else if (!value1.equals(value2)) {
2024-05-30 14:37:01 +08:00
return true;
2023-10-24 17:51:21 +08:00
}
2023-10-23 21:51:12 +08:00
}
2024-05-30 14:37:01 +08:00
return false;
2023-10-12 21:38:27 +08:00
}
2024-05-12 19:02:38 +08:00
public JSplitPane getSplitPane() {
2024-05-06 12:56:56 +08:00
return splitPane;
}
2024-05-12 19:02:38 +08:00
public MessageTable getMessageTable() {
2024-05-06 12:56:56 +08:00
return messageTable;
}
public List<MessageEntry> getLogs() {
return log;
}
@Override
public int getRowCount() {
return filteredLog.size();
}
@Override
public int getColumnCount() {
return 6;
}
@Override
2024-05-12 19:02:38 +08:00
public Object getValueAt(int rowIndex, int columnIndex) {
2024-05-06 12:56:56 +08:00
if (filteredLog.isEmpty()) {
return "";
}
2024-05-30 14:37:01 +08:00
2024-05-06 12:56:56 +08:00
MessageEntry messageEntry = filteredLog.get(rowIndex);
return switch (columnIndex) {
case 0 -> messageEntry.getMethod();
case 1 -> messageEntry.getUrl();
case 2 -> messageEntry.getComment();
case 3 -> messageEntry.getStatus();
case 4 -> messageEntry.getLength();
case 5 -> messageEntry.getColor();
default -> "";
};
}
@Override
2024-05-12 19:02:38 +08:00
public String getColumnName(int columnIndex) {
2024-05-06 12:56:56 +08:00
return switch (columnIndex) {
case 0 -> "Method";
case 1 -> "URL";
case 2 -> "Comment";
case 3 -> "Status";
case 4 -> "Length";
case 5 -> "Color";
default -> "";
};
}
public class MessageTable extends JTable {
2024-05-30 14:37:01 +08:00
private MessageEntry messageEntry;
2023-11-07 11:15:20 +08:00
private SwingWorker<Object, Void> currentWorker;
2024-05-23 12:00:13 +08:00
// 设置响应报文返回的最大长度
private final int MAX_LENGTH = 5242880;
2023-11-07 11:15:20 +08:00
private int lastSelectedIndex = -1;
2024-05-06 12:56:56 +08:00
private final HttpRequestEditor requestEditor;
private final HttpResponseEditor responseEditor;
2023-10-12 21:38:27 +08:00
2024-05-06 12:56:56 +08:00
public MessageTable(TableModel messageTableModel, HttpRequestEditor requestEditor, HttpResponseEditor responseEditor) {
super(messageTableModel);
this.requestEditor = requestEditor;
this.responseEditor = responseEditor;
2023-10-12 21:38:27 +08:00
}
@Override
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
2023-10-23 21:51:12 +08:00
super.changeSelection(row, col, toggle, extend);
2023-11-07 11:15:20 +08:00
int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex;
2024-05-30 14:37:01 +08:00
messageEntry = filteredLog.get(selectedIndex);
2023-10-23 21:51:12 +08:00
2024-05-06 12:56:56 +08:00
requestEditor.setRequest(HttpRequest.httpRequest("Loading..."));
responseEditor.setResponse(HttpResponse.httpResponse("Loading..."));
2023-10-12 21:38:27 +08:00
2023-11-07 11:15:20 +08:00
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
2023-10-12 21:38:27 +08:00
}
2024-05-06 12:56:56 +08:00
currentWorker = new SwingWorker<>() {
2023-11-07 11:15:20 +08:00
@Override
2024-05-06 12:56:56 +08:00
protected ByteArray[] doInBackground() {
2024-05-30 14:37:01 +08:00
HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
ByteArray requestByte = messageEntry.getRequestResponse().request().toByteArray();
ByteArray responseByte = messageEntry.getRequestResponse().response().toByteArray();
2023-11-07 11:15:20 +08:00
2024-05-06 12:56:56 +08:00
if (responseByte.length() > MAX_LENGTH) {
2023-11-07 11:15:20 +08:00
String ellipsis = "\r\n......";
2024-05-06 12:56:56 +08:00
responseByte = responseByte.subArray(0, MAX_LENGTH).withAppended(ellipsis);
2023-11-07 11:15:20 +08:00
}
2024-05-06 12:56:56 +08:00
return new ByteArray[]{requestByte, responseByte};
2023-11-07 11:15:20 +08:00
}
@Override
protected void done() {
if (!isCancelled()) {
try {
2024-05-06 12:56:56 +08:00
ByteArray[] result = (ByteArray[]) get();
2024-05-30 14:37:01 +08:00
requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), result[0]));
2024-05-06 12:56:56 +08:00
responseEditor.setResponse(HttpResponse.httpResponse(result[1]));
2024-05-07 16:08:46 +08:00
} catch (Exception ignored) {
2023-11-07 11:15:20 +08:00
}
}
}
};
currentWorker.execute();
}
2023-10-12 21:38:27 +08:00
}
}
2024-05-06 12:56:56 +08:00
}