Compare commits

..

17 Commits

Author SHA1 Message Date
gh0stkey
1c63841140 Version: 2.5.10 Update 2023-12-12 14:19:50 +08:00
gh0stkey
105c506039 Version: 2.5.10 Update 2023-12-12 14:19:28 +08:00
gh0stkey
f1941bccd7 Version: 2.5.9 Update 2023-11-28 15:26:25 +08:00
gh0stkey
d38e70523a Version: 2.5.9 Update 2023-11-28 09:11:56 +08:00
gh0stkey
1f7651c114 Version: 2.5.9 Update 2023-11-27 15:09:31 +08:00
gh0stkey
fc9a253d2b Version: 2.5.9 Update 2023-11-27 14:55:28 +08:00
ᴋᴇʏ
4cbcc1bcc4 Update issue templates 2023-11-27 09:11:52 +08:00
gh0stkey
765807de6e Version: 2.5.8 Update 2023-11-16 19:44:27 +08:00
gh0stkey
548315e163 Version: 2.5.8 Update 2023-11-16 19:33:38 +08:00
ᴋᴇʏ
d3ab207825 Update issue templates 2023-11-16 14:31:15 +08:00
ᴋᴇʏ
44260dd4ff Update issue templates 2023-11-16 14:27:15 +08:00
gh0stkey
cf3ac4978f Update README.md 2023-11-15 13:18:50 +08:00
gh0stkey
9c8dad8ac0 Version: 2.5.7 Update 2023-11-13 08:59:53 +08:00
gh0stkey
5cd216e45d Version: 2.5.7 Update 2023-11-13 08:28:44 +08:00
gh0stkey
87c5f713fa Version: 2.5.6 Update 2023-11-07 12:05:55 +08:00
gh0stkey
a0946bb723 Version: 2.5.6 Update 2023-11-07 11:32:44 +08:00
gh0stkey
bcb5177b54 Version: 2.5.6 Update 2023-11-07 11:15:20 +08:00
17 changed files with 810 additions and 580 deletions

28
.github/ISSUE_TEMPLATE/问题反馈.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: 问题反馈
about: 尽可能详细的描述问题并反馈
title: "[BUG] "
labels: bug
assignees: ''
---
## 使用环境
```
HaE版本
是否有自定义的HaE规则
BurpSuite版本
JDK版本
操作系统版本:
```
## 问题详情
问题描述:
出现的场景:
## 解决建议
无。

View File

@@ -23,6 +23,22 @@
除此之外,您也可以选择将配置文件存放在`HaE Jar包`的同级目录下的`/.config/HaE/`中,**以便于离线携带**。
### 规则释义
HaE目前的规则一共有6个字段分别是规则名称、规则正则、规则作用域、正则引擎、规则匹配颜色、规则敏感性。
详细的含义如下所示:
| 字段 | 含义 |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name | 规则名称,主要用于简短概括当前规则的作用。 |
| Regex | 规则正则主要用于填写正则表达式。在HaE中所需提取匹配的内容需要用`(``)`将正则表达式进行包裹。 |
| Scope | 规则作用域主要用于表示当前规则作用于HTTP报文的哪个部分。 |
| Engine | 正则引擎,主要用于表示当前规则的正则表达式所使用的引擎。**DFA引擎**:对于文本串里的每一个字符只需扫描一次,速度快、特性少;**NFA引擎**:要翻来覆去标注字符、取消标注字符,速度慢,但是特性(如:分组、替换、分割)丰富。 |
| Color | 规则匹配颜色主要用于表示当前规则匹配到对应HTTP报文时所需标记的高亮颜色。 |
| Sensitive | 规则敏感性,主要用于表示当前规则对于大小写字母是否敏感,敏感(`True`)则严格按照大小写要求匹配,不敏感(`False`)则反之。 |
## 优势特点
1. 精细配置:高度自由的配置选项,以满足各类精细化场景需求。
@@ -40,16 +56,6 @@
| Config配置信息管理 | <img src="images/config.png" style="width: 80%" /> |
| Databoard数据集合面板 | <img src="images/databoard.png" style="width: 80%" /> |
## 实际使用
使用 RGPerson 生成测试数据,放入网站根目录文件中:
![-w467](images/rgperson.jpg)
访问该地址,在`Proxy - HTTP History`中可以看见高亮请求,响应标签页中含有`MarkInfo`标签,其中将匹配到的信息提取了出来。
![-w1047](images/markinfo.png)
## 文末随笔
正义感是一个不可丢失的东西。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 666 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -3,12 +3,10 @@ package burp;
import burp.config.ConfigLoader;
import burp.core.processor.ColorProcessor;
import burp.core.processor.MessageProcessor;
import burp.core.utils.StringHelper;
import burp.ui.MainUI;
import burp.ui.board.DatatablePanel;
import burp.ui.board.MessagePanel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import javax.swing.*;
import java.awt.*;
@@ -38,7 +36,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
new ConfigLoader();
String version = "2.5.5";
String version = "2.5.10";
callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 定义输出
@@ -74,43 +72,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
@Override
public Component getUiComponent() {
JTabbedPane HaETabbedPane = new JTabbedPane();
HaETabbedPane.addTab("", getImageIcon(false), main);
HaETabbedPane.addTab(" Highlighter and Extractor - Empower ethical hacker for efficient operations ", null);
HaETabbedPane.setEnabledAt(1, false);
HaETabbedPane.addPropertyChangeListener("background", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
boolean isDarkBg = isDarkBg();
HaETabbedPane.setIconAt(0, getImageIcon(isDarkBg));
}
private boolean isDarkBg() {
Color bg = HaETabbedPane.getBackground();
int r = bg.getRed();
int g = bg.getGreen();
int b = bg.getBlue();
int avg = (r + g + b) / 3;
return avg < 128;
}
});
return HaETabbedPane;
}
private ImageIcon getImageIcon(boolean isDark) {
ClassLoader classLoader = getClass().getClassLoader();
URL imageURL;
if (isDark) {
imageURL = classLoader.getResource("logo.png");
} else {
imageURL = classLoader.getResource("logo_black.png");
}
ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage();
Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST);
ImageIcon scaledIcon = new ImageIcon(scaledImage);
return scaledIcon;
return main;
}
/**
@@ -120,104 +82,48 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
// 判断是否是响应且该代码作用域为REPEATER、INTRUDER、PROXY分别对应toolFlag 64、32、4
if (toolFlag == 64 || toolFlag == 32 || toolFlag == 4) {
byte[] content;
if (!messageIsRequest) {
IHttpService iHttpService = messageInfo.getHttpService();
String host = iHttpService.getHost();
if (messageIsRequest) {
content = messageInfo.getRequest();
} else {
content = messageInfo.getResponse();
}
List<Map<String, String>> result = null;
IHttpService iHttpService = null;
String originalColor = messageInfo.getHighlight();
String originalComment = messageInfo.getComment();
String host = "";
try {
result = messageProcessor.processMessage(helpers, messageInfo, host, true);
try {
iHttpService = messageInfo.getHttpService();
host = iHttpService.getHost();
} catch (Exception ignored) {
}
if (result != null && !result.isEmpty() && result.size() > 0) {
List<String> colorList = new ArrayList<>();
if (Objects.equals(host, "")) {
List<String> requestTmpHeaders = helpers.analyzeRequest(content).getHeaders();
host = requestTmpHeaders.get(1).split(":")[1].trim();
}
if (originalColor != null) {
colorList.add(originalColor);
}
List<Map<String, String>> result = null;
colorList.add(result.get(0).get("color"));
String resColor = colorProcessor.retrieveFinalColor(colorProcessor.retrieveColorIndices(colorList));
messageInfo.setHighlight(resColor);
try {
result = messageProcessor.processMessage(helpers, content, messageIsRequest, true, host);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
String addComment = String.join(", ", result.get(1).get("comment"));
String allComment = !Objects.equals(originalComment, "") ? String.format("%s, %s", originalComment, addComment) : addComment;
String resComment = StringHelper.mergeComment(allComment);
messageInfo.setComment(resComment);
String resComment = "";
String resColor = "";
String originalColor = messageInfo.getHighlight();
String originalComment = messageInfo.getComment();
if (result != null && !result.isEmpty() && result.size() > 0) {
List<String> colorList = new ArrayList<>();
if (originalColor != null) {
colorList.add(originalColor);
messagePanel.add(messageInfo, resComment, resColor);
}
} catch (Exception e) {
e.printStackTrace();
}
colorList.add(result.get(0).get("color"));
resColor = colorProcessor.retrieveFinalColor(colorProcessor.retrieveColorIndices(colorList));
messageInfo.setHighlight(resColor);
String addComment = String.join(", ", result.get(1).get("comment"));
String allComment = !Objects.equals(originalComment, "") ? String.format("%s, %s", originalComment, addComment) : addComment;
resComment = mergeComment(allComment);
messageInfo.setComment(resComment);
}
String endComment = resComment.isEmpty() ? originalComment : resComment;
String endColor = resColor.isEmpty() ? originalColor : resColor;
if (!messageIsRequest && !Objects.equals(endComment, "") && !Objects.equals(endColor, "")) {
messagePanel.add(messageInfo, endComment, String.valueOf(content.length), endColor);
}
}
}
private String mergeComment(String comment) {
if (!comment.contains(",")) {
return comment;
}
Map<String, Integer> itemCounts = new HashMap<>();
String[] items = comment.split(", ");
for (String item : items) {
if (item.contains("(") && item.contains(")")) {
int openParenIndex = item.lastIndexOf("(");
int closeParenIndex = item.lastIndexOf(")");
String itemName = item.substring(0, openParenIndex).trim();
int count = Integer.parseInt(item.substring(openParenIndex + 1, closeParenIndex).trim());
itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count);
} else {
itemCounts.put(item, 0);
}
}
StringBuilder mergedItems = new StringBuilder();
for (Map.Entry<String, Integer> entry : itemCounts.entrySet()) {
String itemName = entry.getKey();
int count = entry.getValue();
if (count != 0) {
mergedItems.append(itemName).append(" (").append(count).append("), ");
}
}
return mergedItems.substring(0, mergedItems.length() - 2);
}
class MarkInfoTab implements IMessageEditorTab {
private final JTabbedPane jTabbedPane = new JTabbedPane();
private JTable jTable = new JTable();
private DatatablePanel dataPanel;
private JTable dataTable;
private final IMessageEditorController controller;
private Map<String, String> extractRequestMap;
private Map<String, String> extractResponseMap;
@@ -237,30 +143,35 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
jTabbedPane.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent arg0) {
jTable = (JTable) ((JScrollPane)jTabbedPane.getSelectedComponent()).getViewport().getView();
dataTable = ((DatatablePanel)jTabbedPane.getSelectedComponent()).getTable();
}
});
return this.jTabbedPane;
return jTabbedPane;
}
@Override
public boolean isEnabled(byte[] content, boolean isRequest) {
List<Map<String, String>> result = null;
try {
result = messageProcessor.processMessage(helpers, content, isRequest, false, "");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
if (result != null && !result.isEmpty()) {
Map<String, String> dataMap = result.get(0);
if (isRequest) {
extractRequestMap = dataMap;
} else {
extractResponseMap = dataMap;
if (content.length != 0 && !helpers.bytesToString(content).equals("Loading...")) {
try {
if (isRequest) {
result = messageProcessor.processRequestMessage(helpers, content, "", false);
} else {
result = messageProcessor.processResponseMessage(helpers, content, "", false);
}
} catch (Exception e) {
e.printStackTrace();
}
if (result != null && !result.isEmpty()) {
Map<String, String> dataMap = result.get(0);
if (isRequest) {
extractRequestMap = dataMap;
} else {
extractResponseMap = dataMap;
}
return true;
}
return true;
}
return false;
}
@@ -280,15 +191,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
*/
@Override
public byte[] getSelectedData() {
int[] selectRows = jTable.getSelectedRows();
StringBuilder selectData = new StringBuilder();
for (int row : selectRows) {
selectData.append(jTable.getValueAt(row, 0).toString()).append("\n");
}
// 便于单行复制,去除最后一个换行符
String revData = selectData.reverse().toString().replaceFirst("\n", "");
StringBuilder retData = new StringBuilder(revData).reverse();
return helpers.stringToBytes(retData.toString());
return helpers.stringToBytes(dataPanel.getSelectedData(dataTable));
}
/**
@@ -310,18 +213,12 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
*/
public void makeTable(Map<String, String> dataMap) {
ArrayList<String> lTitleList = new ArrayList<>();
dataMap.keySet().forEach(i->{
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);
this.jTabbedPane.addTab(i, jScrollPane);
dataPanel = new DatatablePanel(i, Arrays.asList(extractData));
jTabbedPane.addTab(i, dataPanel);
});
/*
@@ -329,9 +226,9 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
* 采用全局ArrayList的方式遍历删除Tab以此应对BurpSuite缓存机制导致的MarkInfo UI错误展示。
*/
titleList.forEach(t->{
int indexOfTab = this.jTabbedPane.indexOfTab(t);
int indexOfTab = jTabbedPane.indexOfTab(t);
if (indexOfTab != -1) {
this.jTabbedPane.removeTabAt(indexOfTab);
jTabbedPane.removeTabAt(indexOfTab);
}
});

View File

@@ -3,6 +3,7 @@ package burp.config;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConfigEntry {
public static String excludeSuffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip";
@@ -38,5 +39,5 @@ public class ConfigEntry {
public static Map<String,Object[][]> globalRules = null;
public static Map<String, Map<String, List<String>>> globalDataMap = new HashMap<>();
public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>();
}

View File

@@ -143,7 +143,7 @@ public class ConfigLoader {
Map<String,Object> r = new LinkedHashMap<>();
r.put("excludeSuffix", excludeSuffix);
try{
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(RulesFilePath)), StandardCharsets.UTF_8);
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(ConfigFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws);
ws.close();
}catch (Exception ex){

View File

@@ -14,6 +14,7 @@ import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import jregex.Matcher;
import jregex.Pattern;
@@ -57,44 +58,44 @@ public class DataProcessingUnit {
} else {
// 最终返回的结果
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)) {
// 多线程执行,一定程度上减少阻塞现象
Thread t = new Thread(() -> {
String matchContent = "";
// 遍历获取规则
List<String> result = new ArrayList<>();
Map<String, Object> tmpMap = new HashMap<>();
String matchContent = "";
// 遍历获取规则
List<String> result = new ArrayList<>();
Map<String, Object> tmpMap = new HashMap<>();
String name = objects[1].toString();
boolean loaded = (Boolean) objects[0];
String regex = objects[2].toString();
String color = objects[3].toString();
String scope = objects[4].toString();
String engine = objects[5].toString();
boolean sensitive = (Boolean) objects[6];
// 判断规则是否开启与作用域
if (loaded && (scope.contains(scopeString) || scope.contains("any"))) {
switch (scope) {
case "any":
case "request":
case "response":
matchContent = new String(content, StandardCharsets.UTF_8);
break;
case "any header":
case "request header":
case "response header":
matchContent = headers;
break;
case "any body":
case "request body":
case "response body":
matchContent = new String(body, StandardCharsets.UTF_8);
break;
default:
break;
}
String name = objects[1].toString();
boolean loaded = (Boolean) objects[0];
String regex = objects[2].toString();
String color = objects[3].toString();
String scope = objects[4].toString();
String engine = objects[5].toString();
boolean sensitive = (Boolean) objects[6];
// 判断规则是否开启与作用域
if (loaded && (scope.contains(scopeString) || scope.contains("any"))) {
switch (scope) {
case "any":
case "request":
case "response":
matchContent = new String(content, StandardCharsets.UTF_8);
break;
case "any header":
case "request header":
case "response header":
matchContent = headers;
break;
case "any body":
case "request body":
case "response body":
matchContent = new String(body, StandardCharsets.UTF_8);
break;
default:
break;
}
try {
if ("nfa".equals(engine)) {
Pattern pattern;
// 判断规则是否大小写敏感
@@ -121,72 +122,69 @@ public class DataProcessingUnit {
result.add(autoMatcher.group());
}
}
} catch (Exception e) {
BurpExtender.stdout.println(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, regex));
e.printStackTrace();
continue;
}
// 去除重复内容
HashSet tmpList = new HashSet(result);
result.clear();
result.addAll(tmpList);
// 去除重复内容
HashSet tmpList = new HashSet(result);
result.clear();
result.addAll(tmpList);
String nameAndSize = String.format("%s (%s)", name, result.size());
if (!result.isEmpty()) {
tmpMap.put("color", color);
String dataStr = String.join("\n", result);
tmpMap.put("data", dataStr);
finalMap.put(nameAndSize, tmpMap);
// 添加到全局变量中便于Databoard检索
if (!Objects.equals(host, "")) {
List<String> dataList = Arrays.asList(dataStr.split("\n"));
if (ConfigEntry.globalDataMap.containsKey(host)) {
Map<String, List<String>> gRuleMap = new HashMap<>(ConfigEntry.globalDataMap.get(host));
if (gRuleMap.containsKey(name)) {
// gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法
List<String> gDataList = gRuleMap.get(name);
List<String> newDataList = new ArrayList<>(gDataList);
newDataList.addAll(dataList);
newDataList = new ArrayList<>(new HashSet<>(newDataList));
gRuleMap.remove(name);
gRuleMap.put(name, newDataList);
} else {
gRuleMap.put(name, dataList);
}
ConfigEntry.globalDataMap.remove(host);
ConfigEntry.globalDataMap.put(host, gRuleMap);
String nameAndSize = String.format("%s (%s)", name, result.size());
if (!result.isEmpty()) {
tmpMap.put("color", color);
String dataStr = String.join("\n", result);
tmpMap.put("data", dataStr);
finalMap.put(nameAndSize, tmpMap);
// 添加到全局变量中便于Databoard检索
if (!Objects.equals(host, "") && host != null) {
List<String> dataList = Arrays.asList(dataStr.split("\n"));
if (ConfigEntry.globalDataMap.containsKey(host)) {
ConcurrentHashMap<String, List<String>> gRuleMap = new ConcurrentHashMap<>(ConfigEntry.globalDataMap.get(host));
if (gRuleMap.containsKey(name)) {
// gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法
List<String> gDataList = gRuleMap.get(name);
List<String> newDataList = new ArrayList<>(gDataList);
newDataList.addAll(dataList);
newDataList = new ArrayList<>(new HashSet<>(newDataList));
gRuleMap.remove(name);
gRuleMap.put(name, newDataList);
} else {
Map<String, List<String>> ruleMap = new HashMap<>();
ruleMap.put(name, dataList);
// 添加单一Host
ConfigEntry.globalDataMap.put(host, ruleMap);
gRuleMap.put(name, dataList);
}
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) {
// 添加通配符Host实际数据从查询哪里将所有数据提取
ConfigEntry.globalDataMap.put(anyHost, new HashMap<>());
}
if (!ConfigEntry.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) {
// 添加通配符Host实际数据从查询哪里将所有数据提取
ConfigEntry.globalDataMap.put(anyHost, new HashMap<>());
}
if (!ConfigEntry.globalDataMap.containsKey("*")) {
// 添加通配符全匹配,同上
ConfigEntry.globalDataMap.put("*", new HashMap<>());
}
if (!ConfigEntry.globalDataMap.containsKey("*")) {
// 添加通配符全匹配,同上
ConfigEntry.globalDataMap.put("*", new HashMap<>());
}
if (!ConfigEntry.globalDataMap.containsKey("**")) {
// 添加通配符全匹配,同上
ConfigEntry.globalDataMap.put("**", new HashMap<>());
}
if (!ConfigEntry.globalDataMap.containsKey("**")) {
// 添加通配符全匹配,同上
ConfigEntry.globalDataMap.put("**", new HashMap<>());
}
}
}
});
t.start();
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
}
}
});
GlobalCachePool.addToCache(messageIndex, finalMap);

View File

@@ -1,8 +1,10 @@
package burp.core.processor;
import burp.IExtensionHelpers;
import burp.IHttpRequestResponse;
import burp.IRequestInfo;
import burp.IResponseInfo;
import burp.core.utils.MatchTool;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -10,51 +12,98 @@ import java.util.List;
import java.util.Map;
public class MessageProcessor {
MatchTool matcher = new MatchTool();
DataProcessingUnit dataProcessingUnit = new DataProcessingUnit();
ColorProcessor colorProcessor = new ColorProcessor();
private MatchTool matcher = new MatchTool();
private DataProcessingUnit dataProcessingUnit = new DataProcessingUnit();
private ColorProcessor colorProcessor = new ColorProcessor();
public List<Map<String, String>> processMessage(IExtensionHelpers helpers, byte[] content, boolean isRequest, boolean messageInfo, String host)
throws NoSuchAlgorithmException {
List<Map<String, String>> result = new ArrayList<>();
Map<String, Map<String, Object>> obj;
public List<Map<String, String>> processMessage(IExtensionHelpers helpers, IHttpRequestResponse messageInfo, String host, boolean actionFlag) throws Exception {
if (isRequest) {
List<String> requestTmpHeaders = helpers.analyzeRequest(content).getHeaders();
String requestHeaders = String.join("\n", requestTmpHeaders);
byte[] requestByte = messageInfo.getRequest();
byte[] responseByte = messageInfo.getResponse();
try {
String urlString = requestTmpHeaders.get(0).split(" ")[1];
urlString = urlString.indexOf("?") > 0 ? urlString.substring(0, urlString.indexOf("?")) : urlString;
if (matcher.matchUrlSuffix(urlString)) {
return result;
}
} catch (Exception e) {
return result;
List<Map<String, String>> reqObj = processRequestMessage(helpers, requestByte, host, actionFlag);
List<Map<String, String>> resObj = processResponseMessage(helpers, responseByte, host, actionFlag);
List<Map<String, String>> mergedList = new ArrayList<>();
if (reqObj != null && !reqObj.isEmpty()) {
if (resObj != null && !resObj.isEmpty()) {
List<String> colorList = new ArrayList<>();
colorList.add(reqObj.get(0).get("color"));
colorList.add(resObj.get(0).get("color"));
Map<String, String> colorMap = new HashMap<>();
colorMap.put("color", colorProcessor.retrieveFinalColor(colorProcessor.retrieveColorIndices(colorList)));
Map<String, String> commentMap = new HashMap<>();
String commentList = String.format("%s, %s", reqObj.get(1).get("comment"), resObj.get(1).get("comment"));
commentMap.put("comment", commentList);
mergedList.add(0, colorMap);
mergedList.add(1, commentMap);
} else {
mergedList = new ArrayList<>(reqObj);
}
int requestBodyOffset = helpers.analyzeRequest(content).getBodyOffset();
byte[] requestBody = Arrays.copyOfRange(content, requestBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, requestHeaders, requestBody, "request", host);
} else {
try {
String inferredMimeType = String.format("hae.%s", helpers.analyzeResponse(content).getInferredMimeType().toLowerCase());
String statedMimeType = String.format("hae.%s", helpers.analyzeResponse(content).getStatedMimeType().toLowerCase());
if (matcher.matchUrlSuffix(statedMimeType) || matcher.matchUrlSuffix(inferredMimeType)) {
return result;
}
} catch (Exception e) {
return result;
}
List<String> responseTmpHeaders = helpers.analyzeResponse(content).getHeaders();
String responseHeaders = String.join("\n", responseTmpHeaders);
int responseBodyOffset = helpers.analyzeResponse(content).getBodyOffset();
byte[] responseBody = Arrays.copyOfRange(content, responseBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, responseHeaders, responseBody, "response", host);
} else if (resObj != null && !resObj.isEmpty()){
mergedList = new ArrayList<>(resObj);
}
return mergedList;
}
public List<Map<String, String>> processRequestMessage(IExtensionHelpers helpers, byte[] content, String host, boolean actionFlag) throws Exception {
Map<String, Map<String, Object>> obj;
IRequestInfo requestInfo = helpers.analyzeRequest(content);
List<String> requestTmpHeaders = requestInfo.getHeaders();
String requestHeaders = String.join("\n", requestTmpHeaders);
try {
String urlString = requestTmpHeaders.get(0).split(" ")[1];
urlString = urlString.indexOf("?") > 0 ? urlString.substring(0, urlString.indexOf("?")) : urlString;
if (matcher.matchUrlSuffix(urlString)) {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
int requestBodyOffset = requestInfo.getBodyOffset();
byte[] requestBody = Arrays.copyOfRange(content, requestBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, requestHeaders, requestBody, "request", host);
return getDataList(obj, actionFlag);
}
public List<Map<String, String>> processResponseMessage(IExtensionHelpers helpers, byte[] content, String host, boolean actionFlag) throws Exception {
Map<String, Map<String, Object>> obj;
IResponseInfo responseInfo = helpers.analyzeResponse(content);
try {
String inferredMimeType = String.format("hae.%s", responseInfo.getInferredMimeType().toLowerCase());
String statedMimeType = String.format("hae.%s", responseInfo.getStatedMimeType().toLowerCase());
if (matcher.matchUrlSuffix(statedMimeType) || matcher.matchUrlSuffix(inferredMimeType)) {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
List<String> responseTmpHeaders = responseInfo.getHeaders();
String responseHeaders = String.join("\n", responseTmpHeaders);
int responseBodyOffset = responseInfo.getBodyOffset();
byte[] responseBody = Arrays.copyOfRange(content, responseBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, responseHeaders, responseBody, "response", host);
return getDataList(obj, actionFlag);
}
private List<Map<String, String>> getDataList(Map<String, Map<String, Object>> obj, boolean actionFlag) {
List<Map<String, String>> highlightList = new ArrayList<>();
List<Map<String, String>> extractList = new ArrayList<>();
if (obj.size() > 0) {
if (messageInfo) {
if (actionFlag) {
List<List<String>> resultList = dataProcessingUnit.extractColorsAndComments(obj);
List<String> colorList = resultList.get(0);
List<String> commentList = resultList.get(1);
@@ -66,13 +115,14 @@ public class MessageProcessor {
Map<String, String> commentMap = new HashMap<String, String>() {{
put("comment", String.join(", ", commentList));
}};
result.add(colorMap);
result.add(commentMap);
highlightList.add(colorMap);
highlightList.add(commentMap);
}
} else {
result.add(dataProcessingUnit.extractDataFromMap(obj));
extractList.add(dataProcessingUnit.extractDataFromMap(obj));
}
}
return result;
return actionFlag ? highlightList : extractList;
}
}

View File

@@ -1,5 +1,8 @@
package burp.core.utils;
import java.util.HashMap;
import java.util.Map;
public class StringHelper {
public static String replaceFirstOccurrence(String original, String find, String replace) {
int index = original.indexOf(find);
@@ -27,4 +30,37 @@ public class StringHelper {
// 如果patternIndex为-1表示pattern字符串已经完全匹配
return patternIndex == -1;
}
public static String mergeComment(String comment) {
if (!comment.contains(",")) {
return comment;
}
Map<String, Integer> itemCounts = new HashMap<>();
String[] items = comment.split(", ");
for (String item : items) {
if (item.contains("(") && item.contains(")")) {
int openParenIndex = item.lastIndexOf("(");
int closeParenIndex = item.lastIndexOf(")");
String itemName = item.substring(0, openParenIndex).trim();
int count = Integer.parseInt(item.substring(openParenIndex + 1, closeParenIndex).trim());
itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count);
} else {
itemCounts.put(item, 0);
}
}
StringBuilder mergedItems = new StringBuilder();
for (Map.Entry<String, Integer> entry : itemCounts.entrySet()) {
String itemName = entry.getKey();
int count = entry.getValue();
if (count != 0) {
mergedItems.append(itemName).append(" (").append(count).append("), ");
}
}
return mergedItems.substring(0, mergedItems.length() - 2);
}
}

View File

@@ -1,8 +1,6 @@
package burp.rule.utils;
import burp.*;
import burp.config.ConfigEntry;
import burp.config.ConfigLoader;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.Arrays;

View File

@@ -6,6 +6,9 @@ import burp.rule.RuleProcessor;
import burp.ui.board.Databoard;
import burp.ui.board.MessagePanel;
import burp.ui.rule.RulePane;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
@@ -134,7 +137,31 @@ public class MainUI extends JPanel {
mainTabbedPane.addTab("Config", rulePanel);
mainTabbedPane.addTab("Databoard", this.databoardPanel);
}
add(mainTabbedPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
// 新增Logo
JTabbedPane HaETabbedPane = new JTabbedPane();
HaETabbedPane.addTab("", getImageIcon(false), mainTabbedPane);
HaETabbedPane.addTab(" Highlighter and Extractor - Empower ethical hacker for efficient operations ", null);
HaETabbedPane.setEnabledAt(1, false);
HaETabbedPane.addPropertyChangeListener("background", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
boolean isDarkBg = isDarkBg();
HaETabbedPane.setIconAt(0, getImageIcon(isDarkBg));
}
private boolean isDarkBg() {
Color bg = HaETabbedPane.getBackground();
int r = bg.getRed();
int g = bg.getGreen();
int b = bg.getBlue();
int avg = (r + g + b) / 3;
return avg < 128;
}
});
add(HaETabbedPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
@@ -153,6 +180,21 @@ public class MainUI extends JPanel {
tabMenu.add(deleteMenuItem);
}
private ImageIcon getImageIcon(boolean isDark) {
ClassLoader classLoader = getClass().getClassLoader();
URL imageURL;
if (isDark) {
imageURL = classLoader.getResource("logo.png");
} else {
imageURL = classLoader.getResource("logo_black.png");
}
ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage();
Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST);
ImageIcon scaledIcon = new ImageIcon(scaledImage);
return scaledIcon;
}
private JTabbedPane ruleTabbedPane;
private JTextField rulesPathTextField;
private JTextField excludeSuffixTextField;

View File

@@ -5,9 +5,8 @@ import burp.core.utils.StringHelper;
import burp.ui.board.MessagePanel.Table;
import java.util.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
@@ -16,8 +15,6 @@ import java.awt.*;
import java.awt.event.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* @author LinChen && EvilChen
@@ -27,14 +24,27 @@ public class Databoard extends JPanel {
private static Boolean isMatchHost = false;
private JLabel hostLabel;
private JTextField hostTextField;
private JTabbedPane dataTabbedPaneA;
private JTabbedPane dataTabbedPaneB;
private JTabbedPane dataTabbedPane;
private JButton clearButton;
private JSplitPane splitPane;
private MessagePanel messagePanel;
private Table table;
DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
JComboBox hostComboBox = new JComboBox(comboBoxModel);
private SwingWorker<Object, Void> currentWorker;
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) {
this.messagePanel = messagePanel;
@@ -42,8 +52,7 @@ public class Databoard extends JPanel {
}
private void cleanUI() {
dataTabbedPaneA.removeAll();
dataTabbedPaneB.removeAll();
dataTabbedPane.removeAll();
splitPane.setVisible(false);
}
@@ -57,7 +66,7 @@ public class Databoard extends JPanel {
String cleanedHost = StringHelper.replaceFirstOccurrence(host, "*.", "");
if (host.contains("*")) {
ConfigEntry.globalDataMap.keySet().removeIf(i -> i.contains(cleanedHost) || cleanedHost.equals("**"));
ConfigEntry.globalDataMap.keySet().removeIf(i -> i.contains(cleanedHost) || cleanedHost.contains("*"));
} else {
ConfigEntry.globalDataMap.remove(host);
}
@@ -70,8 +79,7 @@ public class Databoard extends JPanel {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
hostLabel = new JLabel();
hostTextField = new JTextField();
dataTabbedPaneA = new JTabbedPane(JTabbedPane.TOP);
dataTabbedPaneB = new JTabbedPane(JTabbedPane.TOP);
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
clearButton = new JButton();
//======== this ========
@@ -83,42 +91,25 @@ public class Databoard extends JPanel {
//---- hostLabel ----
hostLabel.setText("Host:");
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));
add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
clearButton.setText("Clear");
clearButton.addActionListener(this::clearActionPerformed);
add(clearButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
add(clearButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
hostComboBox.setMaximumRowCount(5);
add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setVisible(false);
add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 0.0,
add(splitPane, new GridBagConstraints(1, 1, 3, 3, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
hostTextField.setLayout(new BorderLayout());
hostTextField.add(hostComboBox, BorderLayout.SOUTH);
hostComboBox.setMaximumRowCount(5);
hostComboBox.setPreferredSize(new Dimension(super.getPreferredSize().width, 0));
// 由于主题切换造成的UI组件重绘而自定义组件没有正确地与之同步因此需要事件监听来进行同步
UIManager.addPropertyChangeListener(evt -> {
if ("lookAndFeel".equals(evt.getPropertyName())) {
SwingUtilities.invokeLater(() -> {
hostTextField.remove(hostComboBox);
hostTextField.add(hostComboBox, BorderLayout.SOUTH);
hostTextField.revalidate();
hostTextField.repaint();
});
}
});
setAutoMatch();
}
@@ -130,114 +121,123 @@ public class Databoard extends JPanel {
* 设置输入自动匹配
*/
private void setAutoMatch() {
isMatchHost = false;
for (String host : getHostByList()) {
comboBoxModel.addElement(host);
}
populateComboBoxModel();
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() {
@Override
public void keyPressed(KeyEvent e) {
isMatchHost = true;
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;
handleKeyEvents(e);
}
});
hostTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
updateList();
filterComboBoxList();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateList();
filterComboBoxList();
}
@Override
public void changedUpdate(DocumentEvent e) {
updateList();
filterComboBoxList();
}
private void updateList() {
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);
}
}
});
}
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_DOWN, KeyEvent.VK_UP).contains(keyCode)) {
hostComboBox.dispatchEvent(e);
}
if (keyCode == KeyEvent.VK_ENTER) {
isMatchHost = false;
handleComboBoxAction(null);
hostComboBox.setPopupVisible(false);
}
if (keyCode == KeyEvent.VK_ESCAPE) {
hostComboBox.setPopupVisible(false);
}
isMatchHost = false;
}
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;
}
});
}
hostComboBox.setPopupVisible(comboBoxModel.getSize() > 0);
isMatchHost = false;
}
private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter();
if (filterText.contains("*.")) {
filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
} else if (filterText.contains("*")) {
filterText = "";
String cleanedText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
if (cleanedText.contains("*")) {
cleanedText = "";
}
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1);
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(cleanedText, 1);
sorter.setRowFilter(filter);
filterText = filterText.isEmpty() ? "*" : filterText;
messagePanel.applyHostFilter(filterText);
}
private void populateTabbedPaneByHost(String selectedHost) {
if (!Objects.equals(selectedHost, "")) {
Map<String, Map<String, List<String>>> dataMap = ConfigEntry.globalDataMap;
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = ConfigEntry.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<>();
@@ -262,51 +262,51 @@ public class Databoard extends JPanel {
selectedDataMap = dataMap.get(selectedHost);
}
// 由于removeChangeListener不知什么原因不生效因此建立两个tabbedPane
dataTabbedPaneA.removeAll();
dataTabbedPaneB.removeAll();
ChangeListener changeListenerInstance = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
int selectedIndex = dataTabbedPaneA.getSelectedIndex();
String selectedTitle = "";
if (selectedIndex != -1) {
selectedTitle = dataTabbedPaneA.getTitleAt(selectedIndex);
}
applyHostFilter(selectedTitle);
}
};
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 (ConcurrentHashMap.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) {
JTabbedPane newTabbedPane = new JTabbedPane();
newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) {
Thread t = new Thread(() -> {
String tabTitle = String.format("%s (%s)", entrySet.getKey(), entrySet.getValue().size());
newTabbedPane.addTab(tabTitle, new DataTable(entrySet.getKey(), entrySet.getValue()));
dataTabbedPaneA.addTab(entry.getKey(), newTabbedPane);
});
t.start();
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
}
currentWorker = new SwingWorker<Object, Void>() {
@Override
protected Object[] doInBackground() throws Exception {
String tabTitle = String.format("%s (%s)", entrySet.getKey(),
entrySet.getValue().size());
DatatablePanel datatablePanel = new DatatablePanel(entrySet.getKey(),
entrySet.getValue());
datatablePanel.setTableListener(messagePanel);
return new Object[] {tabTitle, datatablePanel};
}
@Override
protected void done() {
if (!isCancelled()) {
try {
Object[] result = (Object[]) get();
SwingUtilities.invokeLater(() -> {
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 {
dataTabbedPaneB.setPreferredSize(new Dimension(500,0));
dataTabbedPaneB.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPaneB);
dataTabbedPane.removeChangeListener(changeListenerInstance);
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
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 +332,7 @@ public class Databoard extends JPanel {
}
hostTextField.setText(selectedHost);
}
}
class DataTable extends JPanel {
private final JTable table;
private final DefaultTableModel model;
private final JTextField searchField;
private TableRowSorter<DefaultTableModel> sorter;
public DataTable(String tableName, List<String> list) {
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");
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,243 @@
package burp.ui.board;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import jregex.Pattern;
import jregex.REFlags;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Comparator;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.TransferHandler;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
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 JScrollPane scrollPane;
private String tableName;
private JCheckBox searchMode = new JCheckBox("Reverse search");
public DatatablePanel(String tableName, List<String> list) {
this.tableName = tableName;
String[] columnNames = {"#", "Information"};
model = new DefaultTableModel(columnNames, 0);
table = new JTable(model);
sorter = new TableRowSorter<>(model);
// 设置ID排序
sorter.setComparator(0, new Comparator<Integer>() {
@Override
public int compare(Integer s1, Integer s2) {
return s1.compareTo(s2);
}
});
table.setRowSorter(sorter);
TableColumn idColumn = table.getColumnModel().getColumn(0);
idColumn.setMaxWidth(50);
for (String item : list) {
addRowToTable(model, 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();
}
});
// 设置布局
scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
searchMode.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
performSearch();
}
});
setLayout(new BorderLayout(0, 5));
JPanel optionsPanel = new JPanel();
optionsPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 5));
optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.X_AXIS));
// 新增复选框要在这修改rows
JPanel menuPanel = new JPanel(new GridLayout(1, 1));
menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JPopupMenu menu = new JPopupMenu();
menuPanel.add(searchMode);
menu.add(menuPanel);
JButton settingsButton = new JButton("Settings");
settingsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int x = settingsButton.getX();
int y = settingsButton.getY() - menu.getPreferredSize().height;
menu.show(settingsButton, x, y);
}
});
optionsPanel.add(settingsButton);
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(searchField);
add(scrollPane, BorderLayout.CENTER);
add(optionsPanel, BorderLayout.SOUTH);
}
private static void addRowToTable(DefaultTableModel model, Object[] data) {
// 获取当前ID
int rowCount = model.getRowCount();
int id = rowCount > 0 ? (Integer) model.getValueAt(rowCount - 1, 0) + 1 : 1;
Object[] rowData = new Object[data.length + 1];
rowData[0] = id; // 设置ID列的值
System.arraycopy(data, 0, rowData, 1, data.length); // 拷贝其余数据
model.addRow(rowData); // 添加行
}
private void performSearch() {
// 检查文本字段的字体颜色是否为黑色,表示可以进行搜索
if (searchField.getForeground().equals(Color.BLACK)) {
// 获取搜索文本
String searchText = searchField.getText();
// 创建行过滤器
RowFilter<DefaultTableModel, Object> rowFilter;
// 检查搜索模式是否为选中状态
if (searchMode.isSelected()) {
// 反向搜索:创建一个过滤器以排除与正则表达式匹配的行
rowFilter = new RowFilter<DefaultTableModel, Object>() {
public boolean include(Entry<? extends DefaultTableModel, ? extends Object> entry) {
// 对每一行的第二列进行判断假设第二列的索引是1
String entryValue = (String) entry.getValue(1);
// 如果该列的值不包含搜索文本则返回true否则返回false
Pattern pattern = new Pattern(searchText, REFlags.IGNORE_CASE);
return searchText.isEmpty() || !pattern.matcher(entryValue).find();
}
};
} else {
// 正向搜索:创建一个过滤器以包含与正则表达式匹配的行
rowFilter = RowFilter.regexFilter(String.format("(?i)%s", searchText), 1);
}
// 设置过滤器到排序器
sorter.setRowFilter(rowFilter);
}
}
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, 1).toString();
messagePanel.applyMessageFilter(tableName, rowData);
}
}
}
});
table.setTransferHandler(new TransferHandler() {
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
if (comp instanceof JTable) {
StringSelection stringSelection = new StringSelection(getSelectedData(
(JTable) comp));
clip.setContents(stringSelection, null);
} else {
super.exportToClipboard(comp, clip, action);
}
}
});
}
public String getSelectedData(JTable table) {
int[] selectRows = table.getSelectedRows();
StringBuilder selectData = new StringBuilder();
for (int row : selectRows) {
selectData.append(table.getValueAt(row, 1).toString()).append("\n");
}
// 便于单行复制,去除最后一个换行符
String revData = selectData.reverse().toString().replaceFirst("\n", "");
StringBuilder retData = new StringBuilder(revData).reverse();
return retData.toString();
}
public JTable getTable() {
return this.table;
}
}

View File

@@ -21,12 +21,10 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
@@ -158,6 +156,9 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if (filteredLog.isEmpty()) {
return "";
}
LogEntry logEntry = filteredLog.get(rowIndex);
switch (columnIndex)
{
@@ -181,9 +182,13 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
public void applyHostFilter(String filterText) {
filteredLog.clear();
fireTableDataChanged();
String cleanedText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
for (LogEntry entry : log) {
String host = entry.getUrl().getHost();
if (StringHelper.matchFromEnd(host, filterText) || filterText.contains("*")) {
if (filterText.contains("*.") && StringHelper.matchFromEnd(host, cleanedText)) {
filteredLog.add(entry);
} else if (host.equals(filterText) || filterText.contains("*")) {
filteredLog.add(entry);
}
}
@@ -264,6 +269,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
}
}
fireTableDataChanged();
logTable.lastSelectedIndex = -1;
}
public void deleteByHost(String filterText) {
@@ -306,13 +312,13 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
return currentlyDisplayedItem.getHttpService();
}
public void add(IHttpRequestResponse messageInfo, String comment, String length, String color) {
public void add(IHttpRequestResponse messageInfo, String comment, String color) {
synchronized(log) {
IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo);
URL url = iRequestInfo.getUrl();
String method = iRequestInfo.getMethod();
String status = String.valueOf(helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode());
String length = String.valueOf(messageInfo.getResponse().length);
LogEntry logEntry = new LogEntry(callbacks.saveBuffersToTempFiles(messageInfo), method, url, comment, length, color, status);
try {
@@ -327,8 +333,8 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
byte[] reqByteB = reqResMessage.getRequest();
byte[] resByteB = reqResMessage.getResponse();
try {
// 采用匹配数据结果比对
if (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA))) {
// 通过URL、请求和响应报文、匹配数据内容多维度进行对比
if ((entry.getUrl().toString().equals(url.toString()) || (Arrays.equals(reqByteB, reqByteA) || Arrays.equals(resByteB, resByteA))) && (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA)))) {
isDuplicate = true;
break;
}
@@ -355,6 +361,9 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
}
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()) {
return false;
}
@@ -398,7 +407,10 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
public class Table extends JTable {
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) {
super(tableModel);
@@ -407,35 +419,50 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override
public void changeSelection(int row, int col, boolean toggle, boolean 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);
responseViewer.setMessage("Loading...".getBytes(), false);
currentlyDisplayedItem = logEntry.getRequestResponse();
requestViewer.setMessage("Loading...".getBytes(), true);
responseViewer.setMessage("Loading...".getBytes(), false);
currentlyDisplayedItem = logEntry.getRequestResponse();
// 取消之前的后台任务
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
// 在后台线程中执行耗时操作
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
refreshMessage();
return null;
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
};
// 设置当前后台任务
currentWorker = worker;
// 启动后台线程
worker.execute();
}
private synchronized void refreshMessage() {
SwingUtilities.invokeLater(() -> {
requestViewer.setMessage(logEntry.getRequestResponse().getRequest(), true);
responseViewer.setMessage(logEntry.getRequestResponse().getResponse(), false);
});
currentWorker = new SwingWorker<Object, Void>() {
@Override
protected byte[][] doInBackground() throws Exception {
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();
}
}
}