Compare commits

...

18 Commits
2.5.6 ... 2.6

Author SHA1 Message Date
gh0stkey
3a536a52de Version: 2.6 Update 2024-02-02 19:07:03 +08:00
ᴋᴇʏ
ea87c53958 Update issue templates 2024-01-26 20:05:43 +08:00
gh0stkey
e08b930fb5 Version: 2.5.11 Update 2024-01-18 12:07:20 +08:00
gh0stkey
49647d68d0 Version: 2.5.10 Update 2023-12-12 14:54:16 +08:00
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
21 changed files with 708 additions and 498 deletions

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

@@ -0,0 +1,36 @@
---
name: 问题反馈
about: 尽可能详细的描述问题并反馈
title: "[BUG] "
labels: bug
assignees: ''
---
## 使用环境
```
HaE版本
是否有自定义的HaE规则
BurpSuite版本
JDK版本
操作系统版本:
```
## 问题详情
问题描述:
出现的场景:
## 解决建议
无。
## 赞助
如果你觉得HaE好用可以打赏一下作者给作者持续更新下去的动力
<div align=center>
<img src="https://raw.githubusercontent.com/gh0stkey/HaE/master/images/reward.jpeg" style="width: 30%" />
</div>

View File

@@ -23,6 +23,24 @@
除此之外,您也可以选择将配置文件存放在`HaE Jar包`的同级目录下的`/.config/HaE/`中,**以便于离线携带**。 除此之外,您也可以选择将配置文件存放在`HaE Jar包`的同级目录下的`/.config/HaE/`中,**以便于离线携带**。
### 规则释义
HaE目前的规则一共有8个字段分别是规则名称、规则正则、规则作用域、正则引擎、规则匹配颜色、规则敏感性。
详细的含义如下所示:
| 字段 | 含义 |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name | 规则名称,主要用于简短概括当前规则的作用。 |
| F-Regex | 规则正则主要用于填写正则表达式。在HaE中所需提取匹配的内容需要用`(``)`将正则表达式进行包裹。|
| S-Regex | 规则正则作用及使用同F-Regex。S-Regex为二次正则可以用于对F-Regex匹配的数据结果进行二次的匹配提取如不需要的情况下可以留空。|
| Format | 格式化输出在NFA引擎的正则表达式中我们可以通过`{0}``{1}``{2}`…的方式进行取分组格式化输出。默认情况下使用`{0}`即可。 |
| Scope | 规则作用域主要用于表示当前规则作用于HTTP报文的哪个部分。 |
| Engine | 正则引擎,主要用于表示当前规则的正则表达式所使用的引擎。**DFA引擎**:对于文本串里的每一个字符只需扫描一次,速度快、特性少;**NFA引擎**:要翻来覆去标注字符、取消标注字符,速度慢,但是特性(如:分组、替换、分割)丰富。 |
| Color | 规则匹配颜色主要用于表示当前规则匹配到对应HTTP报文时所需标记的高亮颜色。在HaE中具备颜色升级算法当出现相同颜色时会自动向上升级一个颜色进行标记。 |
| Sensitive | 规则敏感性,主要用于表示当前规则对于大小写字母是否敏感,敏感(`True`)则严格按照大小写要求匹配,不敏感(`False`)则反之。 |
## 优势特点 ## 优势特点
1. 精细配置:高度自由的配置选项,以满足各类精细化场景需求。 1. 精细配置:高度自由的配置选项,以满足各类精细化场景需求。
@@ -40,16 +58,6 @@
| Config配置信息管理 | <img src="images/config.png" style="width: 80%" /> | | Config配置信息管理 | <img src="images/config.png" style="width: 80%" /> |
| Databoard数据集合面板 | <img src="images/databoard.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: 117 KiB

View File

@@ -3,13 +3,10 @@ package burp;
import burp.config.ConfigLoader; import burp.config.ConfigLoader;
import burp.core.processor.ColorProcessor; import burp.core.processor.ColorProcessor;
import burp.core.processor.MessageProcessor; import burp.core.processor.MessageProcessor;
import burp.core.utils.StringHelper;
import burp.ui.MainUI; import burp.ui.MainUI;
import burp.ui.board.DatatablePanel; import burp.ui.board.DatatablePanel;
import burp.ui.board.MessagePanel; 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 java.util.*;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -39,7 +36,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
new ConfigLoader(); new ConfigLoader();
String version = "2.5.6"; String version = "2.6";
callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version)); callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 定义输出 // 定义输出
@@ -75,43 +72,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
@Override @Override
public Component getUiComponent() { public Component getUiComponent() {
JTabbedPane HaETabbedPane = new JTabbedPane(); return main;
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;
} }
/** /**
@@ -121,101 +82,44 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
// 判断是否是响应且该代码作用域为REPEATER、INTRUDER、PROXY分别对应toolFlag 64、32、4 // 判断是否是响应且该代码作用域为REPEATER、INTRUDER、PROXY分别对应toolFlag 64、32、4
if (toolFlag == 64 || toolFlag == 32 || toolFlag == 4) { if (toolFlag == 64 || toolFlag == 32 || toolFlag == 4) {
byte[] content; if (!messageIsRequest) {
IHttpService iHttpService = messageInfo.getHttpService();
String host = iHttpService.getHost();
if (messageIsRequest) { List<Map<String, String>> result = null;
content = messageInfo.getRequest();
} else {
content = messageInfo.getResponse();
}
IHttpService iHttpService = null; String originalColor = messageInfo.getHighlight();
String originalComment = messageInfo.getComment();
String host = ""; try {
result = messageProcessor.processMessage(helpers, messageInfo, host, true);
try { if (result != null && !result.isEmpty() && result.size() > 0) {
iHttpService = messageInfo.getHttpService(); List<String> colorList = new ArrayList<>();
host = iHttpService.getHost();
} catch (Exception ignored) {
}
if (Objects.equals(host, "")) { if (originalColor != null) {
List<String> requestTmpHeaders = helpers.analyzeRequest(content).getHeaders(); colorList.add(originalColor);
host = requestTmpHeaders.get(1).split(":")[1].trim(); }
}
List<Map<String, String>> result = null; colorList.add(result.get(0).get("color"));
String resColor = colorProcessor.retrieveFinalColor(colorProcessor.retrieveColorIndices(colorList));
messageInfo.setHighlight(resColor);
try { String addComment = String.join(", ", result.get(1).get("comment"));
result = messageProcessor.processMessage(helpers, content, messageIsRequest, true, host); String allComment = !Objects.equals(originalComment, "") ? String.format("%s, %s", originalComment, addComment) : addComment;
} catch (NoSuchAlgorithmException e) { String resComment = StringHelper.mergeComment(allComment);
throw new RuntimeException(e); messageInfo.setComment(resComment);
}
String resComment = ""; messagePanel.add(messageInfo, resComment, resColor);
String resColor = ""; }
String originalColor = messageInfo.getHighlight(); } catch (Exception e) {
String originalComment = messageInfo.getComment(); e.printStackTrace();
if (result != null && !result.isEmpty() && result.size() > 0) {
List<String> colorList = new ArrayList<>();
if (originalColor != null) {
colorList.add(originalColor);
} }
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 { class MarkInfoTab implements IMessageEditorTab {
private final JTabbedPane jTabbedPane = new JTabbedPane(); private final JTabbedPane jTabbedPane = new JTabbedPane();
private DatatablePanel dataPanel; private DatatablePanel dataPanel;
@@ -224,6 +128,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
private Map<String, String> extractRequestMap; private Map<String, String> extractRequestMap;
private Map<String, String> extractResponseMap; private Map<String, String> extractResponseMap;
private ArrayList<String> titleList = new ArrayList<>(); private ArrayList<String> titleList = new ArrayList<>();
private byte[] message;
public MarkInfoTab(IMessageEditorController controller, boolean editable) { public MarkInfoTab(IMessageEditorController controller, boolean editable) {
this.controller = controller; this.controller = controller;
@@ -247,29 +152,35 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
@Override @Override
public boolean isEnabled(byte[] content, boolean isRequest) { public boolean isEnabled(byte[] content, boolean isRequest) {
this.message = content;
List<Map<String, String>> result = null; List<Map<String, String>> result = null;
if (content.length != 0 && !helpers.bytesToString(content).equals("Loading...")) {
try { try {
result = messageProcessor.processMessage(helpers, content, isRequest, false, ""); if (isRequest) {
} catch (NoSuchAlgorithmException e) { result = messageProcessor.processRequestMessage(helpers, content, "", false);
throw new RuntimeException(e); } else {
} result = messageProcessor.processResponseMessage(helpers, content, "", false);
}
if (result != null && !result.isEmpty()) { } catch (Exception e) {
Map<String, String> dataMap = result.get(0); e.printStackTrace();
if (isRequest) { }
extractRequestMap = dataMap;
} else { if (result != null && !result.isEmpty()) {
extractResponseMap = dataMap; Map<String, String> dataMap = result.get(0);
if (isRequest) {
extractRequestMap = dataMap;
} else {
extractResponseMap = dataMap;
}
return true;
} }
return true;
} }
return false; return false;
} }
@Override @Override
public byte[] getMessage() { public byte[] getMessage() {
return null; return message;
} }
@Override @Override

View File

@@ -3,6 +3,7 @@ package burp.config;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConfigEntry { 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"; 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,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

@@ -112,7 +112,7 @@ public class ConfigLoader {
public static Map<String, Object[][]> getRules() { public static Map<String, Object[][]> getRules() {
Map<String, Object> rulesMap = YamlTool.loadYaml(getRulesFilePath()); Map<String, Object> rulesMap = YamlTool.loadYaml(getRulesFilePath());
Map<String, Object[][]> resRule = new HashMap<>(); Map<String, Object[][]> resRule = new HashMap<>();
String[] fieldKeys = {"loaded", "name", "regex", "color", "scope", "engine", "sensitive"}; String[] fieldKeys = {"loaded", "name", "f_regex", "s_regex", "format", "color", "scope", "engine", "sensitive"};
Object rulesObj = rulesMap.get("rules"); Object rulesObj = rulesMap.get("rules");
if (rulesObj instanceof List) { if (rulesObj instanceof List) {
@@ -143,7 +143,7 @@ public class ConfigLoader {
Map<String,Object> r = new LinkedHashMap<>(); Map<String,Object> r = new LinkedHashMap<>();
r.put("excludeSuffix", excludeSuffix); r.put("excludeSuffix", excludeSuffix);
try{ 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); yaml.dump(r, ws);
ws.close(); ws.close();
}catch (Exception ex){ }catch (Exception ex){

View File

@@ -1,5 +1,6 @@
package burp.core.processor; package burp.core.processor;
import burp.BurpExtender;
import burp.core.GlobalCachePool; import burp.core.GlobalCachePool;
import burp.core.utils.HashCalculator; import burp.core.utils.HashCalculator;
import burp.core.utils.MatchTool; import burp.core.utils.MatchTool;
@@ -11,8 +12,10 @@ import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton; import dk.brics.automaton.RunAutomaton;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import jregex.Matcher; import jregex.Matcher;
import jregex.Pattern; import jregex.Pattern;
@@ -64,13 +67,16 @@ public class DataProcessingUnit {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
Map<String, Object> tmpMap = new HashMap<>(); Map<String, Object> tmpMap = new HashMap<>();
String name = objects[1].toString();
boolean loaded = (Boolean) objects[0]; boolean loaded = (Boolean) objects[0];
String regex = objects[2].toString(); String name = objects[1].toString();
String color = objects[3].toString(); String f_regex = objects[2].toString();
String scope = objects[4].toString(); String s_regex = objects[3].toString();
String engine = objects[5].toString(); String format = objects[4].toString();
boolean sensitive = (Boolean) objects[6]; String color = objects[5].toString();
String scope = objects[6].toString();
String engine = objects[7].toString();
boolean sensitive = (Boolean) objects[8];
// 判断规则是否开启与作用域 // 判断规则是否开启与作用域
if (loaded && (scope.contains(scopeString) || scope.contains("any"))) { if (loaded && (scope.contains(scopeString) || scope.contains("any"))) {
switch (scope) { switch (scope) {
@@ -93,31 +99,12 @@ public class DataProcessingUnit {
break; break;
} }
if ("nfa".equals(engine)) { try {
Pattern pattern; result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
// 判断规则是否大小写敏感 } catch (Exception e) {
if (sensitive) { BurpExtender.stdout.println(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
pattern = new Pattern(regex); e.printStackTrace();
} else { continue;
pattern = new Pattern(regex, Pattern.IGNORE_CASE);
}
Matcher matcher = pattern.matcher(matchContent);
while (matcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(matcher.group(1));
}
} else {
RegExp regexp = new RegExp(regex);
Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
AutomatonMatcher autoMatcher = runAuto.newMatcher(matchContent);
while (autoMatcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(autoMatcher.group());
}
} }
// 去除重复内容 // 去除重复内容
@@ -132,10 +119,10 @@ public class DataProcessingUnit {
tmpMap.put("data", dataStr); tmpMap.put("data", dataStr);
finalMap.put(nameAndSize, tmpMap); finalMap.put(nameAndSize, tmpMap);
// 添加到全局变量中便于Databoard检索 // 添加到全局变量中便于Databoard检索
if (!Objects.equals(host, "")) { if (!Objects.equals(host, "") && host != null) {
List<String> dataList = Arrays.asList(dataStr.split("\n")); List<String> dataList = Arrays.asList(dataStr.split("\n"));
if (ConfigEntry.globalDataMap.containsKey(host)) { if (ConfigEntry.globalDataMap.containsKey(host)) {
Map<String, List<String>> gRuleMap = new HashMap<>(ConfigEntry.globalDataMap.get(host)); ConcurrentHashMap<String, List<String>> gRuleMap = new ConcurrentHashMap<>(ConfigEntry.globalDataMap.get(host));
if (gRuleMap.containsKey(name)) { if (gRuleMap.containsKey(name)) {
// gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法 // gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法
List<String> gDataList = gRuleMap.get(name); List<String> gDataList = gRuleMap.get(name);
@@ -182,6 +169,122 @@ public class DataProcessingUnit {
GlobalCachePool.addToCache(messageIndex, finalMap); GlobalCachePool.addToCache(messageIndex, finalMap);
return finalMap; return finalMap;
} }
}
private List<String> matchByRegex(String f_regex, String s_regex, String content, String format, String engine, boolean sensitive) {
List<String> retList = new ArrayList<>();
if ("nfa".equals(engine)) {
Matcher matcher = createPatternMatcher(f_regex, content, sensitive);
retList.addAll(extractMatches(s_regex, format, sensitive, matcher));
} else {
String newContent = content;
String newFirstRegex = f_regex;
if (!sensitive) {
newContent = content.toLowerCase();
newFirstRegex = f_regex.toLowerCase();
}
AutomatonMatcher autoMatcher = createAutomatonMatcher(newFirstRegex, newContent);
retList.addAll(extractMatches(s_regex, format, autoMatcher, content));
}
return retList;
}
private List<String> extractMatches(String s_regex, String format, boolean sensitive, Matcher matcher) {
List<String> matches = new ArrayList<>();
if (s_regex.isEmpty()) {
matches.addAll(getFormatString(matcher, format));
} else {
while (matcher.find()) {
matcher = createPatternMatcher(s_regex, matcher.group(1), sensitive);
matches.addAll(getFormatString(matcher, format));
}
}
return matches;
}
private List<String> extractMatches(String s_regex, String format, AutomatonMatcher autoMatcher, String content) {
List<String> matches = new ArrayList<>();
if (s_regex.isEmpty()) {
matches.addAll(getFormatString(autoMatcher, format, content));
} else {
while (autoMatcher.find()) {
autoMatcher = createAutomatonMatcher(s_regex, getSubString(content, autoMatcher.group()));
matches.addAll(getFormatString(autoMatcher, format, content));
}
}
return matches;
}
public List<String> getFormatString(Matcher matcher, String format) {
List<Integer> indexList = parseIndexesFromString(format);
List<String> stringList = new ArrayList<>();
while (matcher.find()) {
Object[] params = indexList.stream().map(i -> {
if (matcher.group(i+1) != null) {
return matcher.group(i+1);
}
return "";
}).toArray();
stringList.add(MessageFormat.format(reorderIndex(format), params));
}
return stringList;
}
public List<String> getFormatString(AutomatonMatcher matcher, String format, String content) {
List<Integer> indexList = parseIndexesFromString(format);
List<String> stringList = new ArrayList<>();
while (matcher.find()) {
Object[] params = indexList.stream().map(i -> getSubString(content, matcher.group(i))).toArray();
stringList.add(MessageFormat.format(reorderIndex(format), params));
}
return stringList;
}
private Matcher createPatternMatcher(String regex, String content, boolean sensitive) {
Pattern pattern = (sensitive) ? new Pattern(regex) : new Pattern(regex, Pattern.IGNORE_CASE);
return pattern.matcher(content);
}
private AutomatonMatcher createAutomatonMatcher(String regex, String content) {
RegExp regexp = new RegExp(regex);
Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
return runAuto.newMatcher(content);
}
private LinkedList<Integer> parseIndexesFromString(String input) {
LinkedList<Integer> indexes = new LinkedList<>();
Pattern pattern = new Pattern("\\{(\\d+)}");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
indexes.add(Integer.valueOf(matcher.group(1)));
}
return indexes;
}
private String getSubString(String content, String s) {
int startIndex = content.toLowerCase().indexOf(s);
int endIndex = startIndex + s.length();
return content.substring(startIndex, endIndex);
}
private String reorderIndex(String format) {
Pattern pattern = new Pattern("\\{(\\d+)}");
Matcher matcher = pattern.matcher(format);
int count = 0;
while (matcher.find()) {
String newStr = String.format("{%s}", count);
String matchStr = matcher.group(0);
format = format.replace(matchStr, newStr);
count++;
}
return format;
} }
} }

View File

@@ -1,10 +1,10 @@
package burp.core.processor; package burp.core.processor;
import burp.IExtensionHelpers; import burp.IExtensionHelpers;
import burp.IHttpRequestResponse;
import burp.IRequestInfo; import burp.IRequestInfo;
import burp.IResponseInfo; import burp.IResponseInfo;
import burp.core.utils.MatchTool; import burp.core.utils.MatchTool;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@@ -12,53 +12,98 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class MessageProcessor { public class MessageProcessor {
MatchTool matcher = new MatchTool(); private MatchTool matcher = new MatchTool();
DataProcessingUnit dataProcessingUnit = new DataProcessingUnit(); private DataProcessingUnit dataProcessingUnit = new DataProcessingUnit();
ColorProcessor colorProcessor = new ColorProcessor(); private ColorProcessor colorProcessor = new ColorProcessor();
public List<Map<String, String>> processMessage(IExtensionHelpers helpers, byte[] content, boolean isRequest, boolean messageInfo, String host) public List<Map<String, String>> processMessage(IExtensionHelpers helpers, IHttpRequestResponse messageInfo, String host, boolean actionFlag) throws Exception {
throws NoSuchAlgorithmException {
List<Map<String, String>> result = new ArrayList<>();
Map<String, Map<String, Object>> obj;
if (isRequest) { byte[] requestByte = messageInfo.getRequest();
IRequestInfo requestInfo = helpers.analyzeRequest(content); byte[] responseByte = messageInfo.getResponse();
List<String> requestTmpHeaders = requestInfo.getHeaders();
String requestHeaders = String.join("\n", requestTmpHeaders);
try { List<Map<String, String>> reqObj = processRequestMessage(helpers, requestByte, host, actionFlag);
String urlString = requestTmpHeaders.get(0).split(" ")[1]; List<Map<String, String>> resObj = processResponseMessage(helpers, responseByte, host, actionFlag);
urlString = urlString.indexOf("?") > 0 ? urlString.substring(0, urlString.indexOf("?")) : urlString; List<Map<String, String>> mergedList = new ArrayList<>();
if (matcher.matchUrlSuffix(urlString)) {
return result; if (reqObj != null && !reqObj.isEmpty()) {
} if (resObj != null && !resObj.isEmpty()) {
} catch (Exception e) { List<String> colorList = new ArrayList<>();
return result;
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);
} }
} else if (resObj != null && !resObj.isEmpty()){
int requestBodyOffset = requestInfo.getBodyOffset(); mergedList = new ArrayList<>(resObj);
byte[] requestBody = Arrays.copyOfRange(content, requestBodyOffset, content.length);
obj = dataProcessingUnit.matchContentByRegex(content, requestHeaders, requestBody, "request", host);
} else {
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 result;
}
} catch (Exception e) {
return result;
}
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 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 (obj.size() > 0) {
if (messageInfo) { if (actionFlag) {
List<List<String>> resultList = dataProcessingUnit.extractColorsAndComments(obj); List<List<String>> resultList = dataProcessingUnit.extractColorsAndComments(obj);
List<String> colorList = resultList.get(0); List<String> colorList = resultList.get(0);
List<String> commentList = resultList.get(1); List<String> commentList = resultList.get(1);
@@ -70,13 +115,14 @@ public class MessageProcessor {
Map<String, String> commentMap = new HashMap<String, String>() {{ Map<String, String> commentMap = new HashMap<String, String>() {{
put("comment", String.join(", ", commentList)); put("comment", String.join(", ", commentList));
}}; }};
result.add(colorMap); highlightList.add(colorMap);
result.add(commentMap); highlightList.add(commentMap);
} }
} else { } 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; package burp.core.utils;
import java.util.HashMap;
import java.util.Map;
public class StringHelper { public class StringHelper {
public static String replaceFirstOccurrence(String original, String find, String replace) { public static String replaceFirstOccurrence(String original, String find, String replace) {
int index = original.indexOf(find); int index = original.indexOf(find);
@@ -27,4 +30,37 @@ public class StringHelper {
// 如果patternIndex为-1表示pattern字符串已经完全匹配 // 如果patternIndex为-1表示pattern字符串已经完全匹配
return patternIndex == -1; 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

@@ -33,7 +33,9 @@ public class RuleProcessor {
(String) objects[3], (String) objects[3],
(String) objects[4], (String) objects[4],
(String) objects[5], (String) objects[5],
(boolean) objects[6])) (String) objects[6],
(String) objects[7],
(boolean) objects[8]))
.collect(Collectors.toList()); .collect(Collectors.toList());
ruleGroupList.add(new RuleGroup(k, ruleList)); ruleGroupList.add(new RuleGroup(k, ruleList));
}); });
@@ -80,17 +82,20 @@ public class RuleProcessor {
ConfigEntry.globalRules.remove(Rules); ConfigEntry.globalRules.remove(Rules);
this.rulesFormatAndSave(); this.rulesFormatAndSave();
} }
public String newRule() { public String newRule() {
int i = 0; int i = 0;
String name = "New "; String name = "New ";
Object[][] data = new Object[][] { Object[][] data = new Object[][] {
{ {
false, "New Name", "(New Regex)", "gray", "any", "nfa", false false, "New Name", "(First Regex)", "(Second Regex)", "{0}", "gray", "any", "nfa", false
} }
}; };
while (ConfigEntry.globalRules.containsKey(name + i)) { while (ConfigEntry.globalRules.containsKey(name + i)) {
i++; i++;
} }
ConfigEntry.globalRules.put(name + i, data); ConfigEntry.globalRules.put(name + i, data);
this.rulesFormatAndSave(); this.rulesFormatAndSave();
return name + i; return name + i;

View File

@@ -10,11 +10,13 @@ import java.util.Map;
public class Rule { public class Rule {
private Map<String, Object> fields; private Map<String, Object> fields;
public Rule(boolean loaded, String name, String regex, String color, String scope, String engine, boolean sensitive) { public Rule(boolean loaded, String name, String f_regex, String s_regex, String format, String color, String scope, String engine, boolean sensitive) {
fields = new LinkedHashMap<>(); fields = new LinkedHashMap<>();
fields.put("name", name); fields.put("name", name);
fields.put("loaded", loaded); fields.put("loaded", loaded);
fields.put("regex", regex); fields.put("f_regex", f_regex);
fields.put("s_regex", s_regex);
fields.put("format", format);
fields.put("color", color); fields.put("color", color);
fields.put("scope", scope); fields.put("scope", scope);
fields.put("engine", engine); fields.put("engine", engine);

View File

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

View File

@@ -6,6 +6,9 @@ import burp.rule.RuleProcessor;
import burp.ui.board.Databoard; import burp.ui.board.Databoard;
import burp.ui.board.MessagePanel; import burp.ui.board.MessagePanel;
import burp.ui.rule.RulePane; import burp.ui.rule.RulePane;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
@@ -134,7 +137,31 @@ public class MainUI extends JPanel {
mainTabbedPane.addTab("Config", rulePanel); mainTabbedPane.addTab("Config", rulePanel);
mainTabbedPane.addTab("Databoard", this.databoardPanel); 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, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0)); new Insets(0, 0, 0, 0), 0, 0));
@@ -153,6 +180,21 @@ public class MainUI extends JPanel {
tabMenu.add(deleteMenuItem); 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 JTabbedPane ruleTabbedPane;
private JTextField rulesPathTextField; private JTextField rulesPathTextField;
private JTextField excludeSuffixTextField; private JTextField excludeSuffixTextField;
@@ -252,7 +294,7 @@ class TabTitleEditListener extends MouseAdapter implements ChangeListener, Docum
} }
public void newTab(){ public void newTab(){
Object[][] data = new Object[][]{{false, "New Name", "(New Regex)", "gray", "any", "nfa", false}}; Object[][] data = new Object[][]{{false, "New Name", "(New Regex)", "", "{0}", "gray", "any", "nfa", false}};
insertTab(ruleEditTabbedPane, ruleProcessor.newRule(),data); insertTab(ruleEditTabbedPane, ruleProcessor.newRule(),data);
} }

View File

@@ -5,8 +5,8 @@ import burp.core.utils.StringHelper;
import burp.ui.board.MessagePanel.Table; import burp.ui.board.MessagePanel.Table;
import java.util.*; import java.util.*;
import javax.swing.event.ChangeEvent; import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.ChangeListener; import javax.swing.event.*;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
@@ -15,8 +15,6 @@ import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import java.util.List; import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/** /**
* @author LinChen && EvilChen * @author LinChen && EvilChen
@@ -48,7 +46,6 @@ public class Databoard extends JPanel {
} }
}; };
public Databoard(MessagePanel messagePanel) { public Databoard(MessagePanel messagePanel) {
this.messagePanel = messagePanel; this.messagePanel = messagePanel;
initComponents(); initComponents();
@@ -69,7 +66,7 @@ public class Databoard extends JPanel {
String cleanedHost = StringHelper.replaceFirstOccurrence(host, "*.", ""); String cleanedHost = StringHelper.replaceFirstOccurrence(host, "*.", "");
if (host.contains("*")) { if (host.contains("*")) {
ConfigEntry.globalDataMap.keySet().removeIf(i -> i.contains(cleanedHost) || cleanedHost.equals("**")); ConfigEntry.globalDataMap.keySet().removeIf(i -> i.contains(cleanedHost) || cleanedHost.contains("*"));
} else { } else {
ConfigEntry.globalDataMap.remove(host); ConfigEntry.globalDataMap.remove(host);
} }
@@ -79,7 +76,6 @@ public class Databoard extends JPanel {
} }
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
hostLabel = new JLabel(); hostLabel = new JLabel();
hostTextField = new JTextField(); hostTextField = new JTextField();
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP); dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
@@ -94,45 +90,47 @@ public class Databoard extends JPanel {
//---- hostLabel ---- //---- hostLabel ----
hostLabel.setText("Host:"); hostLabel.setText("Host:");
add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
clearButton.setText("Clear"); clearButton.setText("Clear");
clearButton.addActionListener(this::clearActionPerformed); clearButton.addActionListener(this::clearActionPerformed);
add(clearButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, add(clearButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
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)); new Insets(8, 0, 5, 5), 0, 0));
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setVisible(false); splitPane.setVisible(false);
add(splitPane, new GridBagConstraints(1, 1, 3, 3, 0.0, 0.0,
add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
hostTextField.setLayout(new BorderLayout()); splitPane.addComponentListener(new ComponentAdapter() {
hostTextField.add(hostComboBox, BorderLayout.SOUTH); @Override
hostComboBox.setMaximumRowCount(5); public void componentResized(ComponentEvent e) {
hostComboBox.setPreferredSize(new Dimension(super.getPreferredSize().width, 0)); resizePanel();
// 由于主题切换造成的UI组件重绘而自定义组件没有正确地与之同步因此需要事件监听来进行同步
UIManager.addPropertyChangeListener(evt -> {
if ("lookAndFeel".equals(evt.getPropertyName())) {
SwingUtilities.invokeLater(() -> {
hostTextField.remove(hostComboBox);
hostTextField.add(hostComboBox, BorderLayout.SOUTH);
hostTextField.revalidate();
hostTextField.repaint();
});
} }
}); });
setAutoMatch(); setAutoMatch();
} }
private void resizePanel() {
splitPane.setDividerLocation(0.4);
TableColumnModel columnModel = table.getColumnModel();
int totalWidth = (int) (getWidth() * 0.6);
columnModel.getColumn(0).setPreferredWidth((int) (totalWidth * 0.1));
columnModel.getColumn(1).setPreferredWidth((int) (totalWidth * 0.3));
columnModel.getColumn(2).setPreferredWidth((int) (totalWidth * 0.3));
columnModel.getColumn(3).setPreferredWidth((int) (totalWidth * 0.1));
columnModel.getColumn(4).setPreferredWidth((int) (totalWidth * 0.1));
columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1));
}
private static List<String> getHostByList() { private static List<String> getHostByList() {
return new ArrayList<>(ConfigEntry.globalDataMap.keySet()); return new ArrayList<>(ConfigEntry.globalDataMap.keySet());
} }
@@ -156,22 +154,19 @@ public class Databoard extends JPanel {
hostTextField.getDocument().addDocumentListener(new DocumentListener() { hostTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
update(e); filterComboBoxList();
} }
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
update(e); filterComboBoxList();
} }
@Override @Override
public void changedUpdate(DocumentEvent e) { public void changedUpdate(DocumentEvent e) {
update(e);
}
public void update(DocumentEvent e) {
filterComboBoxList(); filterComboBoxList();
} }
}); });
} }
@@ -197,14 +192,14 @@ public class Databoard extends JPanel {
e.setKeyCode(KeyEvent.VK_ENTER); e.setKeyCode(KeyEvent.VK_ENTER);
} }
if (Arrays.asList(KeyEvent.VK_ENTER, KeyEvent.VK_UP, KeyEvent.VK_DOWN).contains(keyCode)) { if (Arrays.asList(KeyEvent.VK_DOWN, KeyEvent.VK_UP).contains(keyCode)) {
e.setSource(hostComboBox);
hostComboBox.dispatchEvent(e); hostComboBox.dispatchEvent(e);
if (keyCode == KeyEvent.VK_ENTER) { }
updateTextFieldFromComboBox();
hostComboBox.setPopupVisible(false); if (keyCode == KeyEvent.VK_ENTER) {
e.consume(); isMatchHost = false;
} handleComboBoxAction(null);
hostComboBox.setPopupVisible(false);
} }
if (keyCode == KeyEvent.VK_ESCAPE) { if (keyCode == KeyEvent.VK_ESCAPE) {
@@ -214,15 +209,6 @@ public class Databoard extends JPanel {
isMatchHost = false; isMatchHost = false;
} }
private void updateTextFieldFromComboBox() {
Object selectedItem = hostComboBox.getSelectedItem();
if (selectedItem != null) {
String selectedHost = selectedItem.toString();
hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
}
}
private void filterComboBoxList() { private void filterComboBoxList() {
isMatchHost = true; isMatchHost = true;
comboBoxModel.removeAllElements(); comboBoxModel.removeAllElements();
@@ -249,24 +235,29 @@ public class Databoard extends JPanel {
private void applyHostFilter(String filterText) { private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter(); TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter();
if (filterText.contains("*.")) { String cleanedText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
} else if (filterText.contains("*")) { if (cleanedText.contains("*")) {
filterText = ""; cleanedText = "";
} }
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1); RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(cleanedText, 1);
sorter.setRowFilter(filter); sorter.setRowFilter(filter);
filterText = filterText.isEmpty() ? "*" : filterText;
messagePanel.applyHostFilter(filterText); messagePanel.applyHostFilter(filterText);
} }
private void populateTabbedPaneByHost(String selectedHost) { private void populateTabbedPaneByHost(String selectedHost) {
if (!Objects.equals(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; Map<String, List<String>> selectedDataMap;
dataTabbedPane.removeAll();
dataTabbedPane.setPreferredSize(new Dimension(500,0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
dataTabbedPane.removeChangeListener(changeListenerInstance);
splitPane.setLeftComponent(dataTabbedPane);
if (selectedHost.contains("*")) { if (selectedHost.contains("*")) {
// 通配符数据 // 通配符数据
selectedDataMap = new HashMap<>(); selectedDataMap = new HashMap<>();
@@ -291,20 +282,13 @@ public class Databoard extends JPanel {
selectedDataMap = dataMap.get(selectedHost); selectedDataMap = dataMap.get(selectedHost);
} }
dataTabbedPane.removeAll();
dataTabbedPane.setPreferredSize(new Dimension(500,0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPane);
if (selectedHost.equals("**")) { if (selectedHost.equals("**")) {
for (Map.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) { if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
for (ConcurrentHashMap.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) {
JTabbedPane newTabbedPane = new JTabbedPane(); JTabbedPane newTabbedPane = new JTabbedPane();
newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) { for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) {
currentWorker = new SwingWorker<Object, Void>() { currentWorker = new SwingWorker<Object, Void>() {
@Override @Override
@@ -322,8 +306,10 @@ public class Databoard extends JPanel {
if (!isCancelled()) { if (!isCancelled()) {
try { try {
Object[] result = (Object[]) get(); Object[] result = (Object[]) get();
newTabbedPane.addTab(result[0].toString(), (DatatablePanel) result[1]); SwingUtilities.invokeLater(() -> {
dataTabbedPane.addTab(entry.getKey(), newTabbedPane); newTabbedPane.addTab(result[0].toString(), (DatatablePanel) result[1]);
dataTabbedPane.addTab(entry.getKey(), newTabbedPane);
});
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -336,8 +322,6 @@ public class Databoard extends JPanel {
dataTabbedPane.addChangeListener(changeListenerInstance); dataTabbedPane.addChangeListener(changeListenerInstance);
} else { } else {
dataTabbedPane.removeChangeListener(changeListenerInstance);
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) { for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size()); String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
DatatablePanel datatablePanel = new DatatablePanel(entry.getKey(), entry.getValue()); DatatablePanel datatablePanel = new DatatablePanel(entry.getKey(), entry.getValue());
@@ -349,17 +333,11 @@ public class Databoard extends JPanel {
// 展示请求消息表单 // 展示请求消息表单
JSplitPane messageSplitPane = this.messagePanel.getPanel(); JSplitPane messageSplitPane = this.messagePanel.getPanel();
this.splitPane.setRightComponent(messageSplitPane); this.splitPane.setRightComponent(messageSplitPane);
// 获取字段
table = this.messagePanel.getTable(); table = this.messagePanel.getTable();
// 设置对应字段宽度 resizePanel();
TableColumnModel columnModel = table.getColumnModel();
TableColumn column = columnModel.getColumn(1);
column.setPreferredWidth(300);
column = columnModel.getColumn(2);
column.setPreferredWidth(300);
splitPane.setVisible(true); splitPane.setVisible(true);
applyHostFilter(selectedHost); applyHostFilter(selectedHost);
// 主动调用一次stateChanged使得dataTabbedPane可以精准展示内容 // 主动调用一次stateChanged使得dataTabbedPane可以精准展示内容
@@ -369,20 +347,6 @@ public class Databoard extends JPanel {
hostTextField.setText(selectedHost); hostTextField.setText(selectedHost);
ChangeListener changeListener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabSource = (JTabbedPane) e.getSource();
int index = tabSource.getSelectedIndex();
if (index != -1) {
Component selectedComponent = tabSource.getComponentAt(index);
if (selectedComponent instanceof DatatablePanel) {
((DatatablePanel) selectedComponent).updatePageSize();
}
}
}
};
dataTabbedPane.addChangeListener(changeListener);
} }
} }
} }

View File

@@ -2,19 +2,29 @@ package burp.ui.board;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.GridLayout;
import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.AdjustmentEvent; import java.awt.event.ActionEvent;
import java.awt.event.AdjustmentListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; 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.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Comparator;
import java.util.List; 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.JComponent;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.JTextField; import javax.swing.JTextField;
@@ -32,30 +42,38 @@ public class DatatablePanel extends JPanel {
private final DefaultTableModel model; private final DefaultTableModel model;
private final JTextField searchField; private final JTextField searchField;
private TableRowSorter<DefaultTableModel> sorter; private TableRowSorter<DefaultTableModel> sorter;
private int pageSize; // 动态计算的,每页显示多少条记录
private int currentPage; // 当前页码
private List<String> fullList; // 假设这是一个包含所有数据的列表
private JScrollPane scrollPane; private JScrollPane scrollPane;
private String tableName; private String tableName;
private final int SHOW_LENGTH = 3000; private JCheckBox searchMode = new JCheckBox("Reverse search");
public DatatablePanel(String tableName, List<String> list) { public DatatablePanel(String tableName, List<String> list) {
fullList = list;
currentPage = 0;
pageSize = 10;
this.tableName = tableName; this.tableName = tableName;
String[] columnNames = {"#", "Information"}; String[] columnNames = {"#", "Information"};
model = new DefaultTableModel(columnNames, 0); model = new DefaultTableModel(columnNames, 0);
table = new JTable(model); table = new JTable(model);
sorter = new TableRowSorter<>(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); table.setRowSorter(sorter);
TableColumn idColumn = table.getColumnModel().getColumn(0); TableColumn idColumn = table.getColumnModel().getColumn(0);
idColumn.setMaxWidth(50); idColumn.setMaxWidth(50);
for (String item : list) {
if (!item.isEmpty()) {
addRowToTable(model, new Object[]{item});
}
}
String defaultText = "Search"; String defaultText = "Search";
searchField = new JTextField(defaultText); searchField = new JTextField(defaultText);
// 设置灰色默认文本Search // 设置灰色默认文本Search
searchField.setForeground(Color.GRAY); searchField.setForeground(Color.GRAY);
searchField.addFocusListener(new FocusListener() { searchField.addFocusListener(new FocusListener() {
@@ -93,88 +111,83 @@ public class DatatablePanel extends JPanel {
performSearch(); 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), 1);
sorter.setRowFilter(rowFilter);
}
}
}); });
// 设置布局 // 设置布局
scrollPane = new JScrollPane(table); scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
updatePageSize();
}
});
// 添加滚动监听器,以加载更多数据 searchMode.addItemListener(new ItemListener() {
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { public void itemStateChanged(ItemEvent e) {
@Override performSearch();
public void adjustmentValueChanged(AdjustmentEvent e) {
if (fullList.size() > SHOW_LENGTH) {
if (!e.getValueIsAdjusting() && !scrollPane.getVerticalScrollBar().getValueIsAdjusting()) {
if (scrollPane.getVerticalScrollBar().getValue() == scrollPane.getVerticalScrollBar().getMaximum() - scrollPane.getVerticalScrollBar().getVisibleAmount()) {
if ((currentPage + 1) * pageSize < fullList.size()) {
currentPage++;
loadPageData();
}
}
}
}
} }
}); });
setLayout(new BorderLayout(0, 5)); 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(scrollPane, BorderLayout.CENTER);
add(searchField, BorderLayout.SOUTH); add(optionsPanel, BorderLayout.SOUTH);
loadPageData();
} }
// 加载指定页的数据 private static void addRowToTable(DefaultTableModel model, Object[] data) {
private void loadPageData() { // 获取当前ID
if (fullList.size() > SHOW_LENGTH) { int rowCount = model.getRowCount();
int start = currentPage * pageSize; int id = rowCount > 0 ? (Integer) model.getValueAt(rowCount - 1, 0) + 1 : 1;
int end = Math.min((currentPage + 1) * pageSize, fullList.size()); Object[] rowData = new Object[data.length + 1];
int lastRow = model.getRowCount(); rowData[0] = id; // 设置ID列的值
start = Math.max(start, lastRow); System.arraycopy(data, 0, rowData, 1, data.length); // 拷贝其余数据
model.addRow(rowData); // 添加行
}
for (int i = start; i < end; i++) { private void performSearch() {
addRowToTable(model, new Object[]{fullList.get(i)}); if (searchField.getForeground().equals(Color.BLACK)) {
} RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
} else { public boolean include(Entry<?, ?> entry) {
for (String item : fullList) { String searchFieldTextText = searchField.getText();
addRowToTable(model, new Object[]{item}); Pattern pattern = null;
} try {
pattern = new Pattern(searchFieldTextText, REFlags.IGNORE_CASE);
} catch (Exception ignored) {
}
String entryValue = ((String) entry.getValue(1)).toLowerCase();
searchFieldTextText = searchFieldTextText.toLowerCase();
if (pattern != null) {
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find() != searchMode.isSelected();
} else {
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText) != searchMode.isSelected();
}
}
};
sorter.setRowFilter(rowFilter);
} }
} }
public void updatePageSize() {
if (fullList.size() > SHOW_LENGTH && isShowing()) {
int oldPageSize = pageSize;
pageSize = getDynamicSize();
if (oldPageSize != pageSize) {
currentPage = 0;
loadPageData();
}
}
}
private int getDynamicSize() {
int visibleHeight = scrollPane.getViewport().getViewRect().height;
int rowHeight = table.getRowHeight();
return Math.max(1, visibleHeight / rowHeight + 2);
}
public void setTableListener(MessagePanel messagePanel) { public void setTableListener(MessagePanel messagePanel) {
table.setDefaultEditor(Object.class, null); table.setDefaultEditor(Object.class, null);
@@ -185,7 +198,7 @@ public class DatatablePanel extends JPanel {
if (e.getClickCount() == 2) { if (e.getClickCount() == 2) {
int selectedRow = table.getSelectedRow(); int selectedRow = table.getSelectedRow();
if (selectedRow != -1) { if (selectedRow != -1) {
String rowData = table.getValueAt(selectedRow, 0).toString(); String rowData = table.getValueAt(selectedRow, 1).toString();
messagePanel.applyMessageFilter(tableName, rowData); messagePanel.applyMessageFilter(tableName, rowData);
} }
} }
@@ -212,20 +225,13 @@ public class DatatablePanel extends JPanel {
for (int row : selectRows) { for (int row : selectRows) {
selectData.append(table.getValueAt(row, 1).toString()).append("\n"); 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();
}
private static void addRowToTable(DefaultTableModel model, Object[] data) { // 便于单行复制,去除最后一个换行符
// 获取当前ID if (selectData.length() > 0){
int rowCount = model.getRowCount(); selectData.deleteCharAt(selectData.length() - 1);
int id = rowCount > 0 ? (Integer) model.getValueAt(rowCount - 1, 0) + 1 : 1; }
Object[] rowData = new Object[data.length + 1];
rowData[0] = id; // 设置ID列的值 return selectData.toString();
System.arraycopy(data, 0, rowData, 1, data.length); // 拷贝其余数据
model.addRow(rowData); // 添加行
} }
public JTable getTable() { public JTable getTable() {

View File

@@ -16,11 +16,13 @@ import burp.core.utils.StringHelper;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
@@ -44,7 +46,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
private final List<LogEntry> filteredLog = new ArrayList<LogEntry>(); private final List<LogEntry> filteredLog = new ArrayList<LogEntry>();
private IHttpRequestResponse currentlyDisplayedItem; private IHttpRequestResponse currentlyDisplayedItem;
private final IExtensionHelpers helpers; private final IExtensionHelpers helpers;
private Table logTable; private final Table logTable;
public MessagePanel(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers) { public MessagePanel(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers) {
this.callbacks = callbacks; this.callbacks = callbacks;
@@ -66,6 +68,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
return age1.compareTo(age2); return age1.compareTo(age2);
} }
}); });
// Color字段根据颜色顺序进行排序 // Color字段根据颜色顺序进行排序
sorter.setComparator(5, new Comparator<String>() { sorter.setComparator(5, new Comparator<String>() {
@Override @Override
@@ -182,9 +185,13 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
public void applyHostFilter(String filterText) { public void applyHostFilter(String filterText) {
filteredLog.clear(); filteredLog.clear();
fireTableDataChanged(); fireTableDataChanged();
String cleanedText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
for (LogEntry entry : log) { for (LogEntry entry : log) {
String host = entry.getUrl().getHost(); 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); filteredLog.add(entry);
} }
} }
@@ -211,56 +218,61 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
int responseBodyOffset = helpers.analyzeResponse(responseByte).getBodyOffset(); int responseBodyOffset = helpers.analyzeResponse(responseByte).getBodyOffset();
String responseBody = new String(Arrays.copyOfRange(responseByte, responseBodyOffset, responseByte.length), StandardCharsets.UTF_8); String responseBody = new String(Arrays.copyOfRange(responseByte, responseBodyOffset, responseByte.length), StandardCharsets.UTF_8);
final boolean[] isMatched = {false}; // 标志变量,表示是否满足过滤条件 // 标志变量,表示是否满足过滤条件
AtomicBoolean isMatched = new AtomicBoolean(false);
ConfigEntry.globalRules.keySet().forEach(i -> { ConfigEntry.globalRules.keySet().forEach(i -> {
for (Object[] objects : ConfigEntry.globalRules.get(i)) { for (Object[] objects : ConfigEntry.globalRules.get(i)) {
String name = objects[1].toString(); String name = objects[1].toString();
String scope = objects[4].toString(); String format = objects[4].toString();
if (name.contains(tableName)) { String scope = objects[6].toString();
boolean match = false; // 标志变量,表示当前规则是否匹配
switch (scope) { // 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间
case "any": if (entry.getComment().contains(name)) {
match = requestString.contains(filterText) || responseString.contains(filterText); if (name.equals(tableName)) {
break; // 标志变量,表示当前规则是否匹配
case "request": boolean isMatch = false;
match = requestString.contains(filterText);
break;
case "response":
match = responseString.contains(filterText);
break;
case "any header":
match = requestHeaders.contains(filterText) || responseHeaders.contains(filterText);
break;
case "request header":
match = requestHeaders.contains(filterText);
break;
case "response header":
match = responseHeaders.contains(filterText);
break;
case "any body":
match = requestBody.contains(filterText) || responseBody.contains(filterText);
break;
case "request body":
match = requestBody.contains(filterText);
break;
case "response body":
match = responseBody.contains(filterText);
break;
default:
break;
}
if (match) { switch (scope) {
isMatched[0] = true; 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;
default:
break;
}
isMatched.set(isMatch);
break; break;
} }
} }
} }
}); });
if (isMatched[0]) { if (isMatched.get()) {
filteredLog.add(entry); filteredLog.add(entry);
} }
} }
@@ -268,6 +280,26 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
logTable.lastSelectedIndex = -1; logTable.lastSelectedIndex = -1;
} }
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;
}
public void deleteByHost(String filterText) { public void deleteByHost(String filterText) {
filteredLog.clear(); filteredLog.clear();
List<Integer> rowsToRemove = new ArrayList<>(); List<Integer> rowsToRemove = new ArrayList<>();
@@ -308,13 +340,13 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
return currentlyDisplayedItem.getHttpService(); 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) { synchronized(log) {
IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo); IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo);
URL url = iRequestInfo.getUrl(); URL url = iRequestInfo.getUrl();
String method = iRequestInfo.getMethod(); String method = iRequestInfo.getMethod();
String status = String.valueOf(helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode()); 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); LogEntry logEntry = new LogEntry(callbacks.saveBuffersToTempFiles(messageInfo), method, url, comment, length, color, status);
try { try {
@@ -329,8 +361,8 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
byte[] reqByteB = reqResMessage.getRequest(); byte[] reqByteB = reqResMessage.getRequest();
byte[] resByteB = reqResMessage.getResponse(); byte[] resByteB = reqResMessage.getResponse();
try { try {
// 采用匹配数据结果比对 // 通过URL、请求和响应报文、匹配数据内容多维度进行对比
if (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA))) { 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; isDuplicate = true;
break; break;
} }

View File

@@ -20,7 +20,7 @@ public class RulePane extends JPanel {
private DefaultTableModel model = createModel(); private DefaultTableModel model = createModel();
private static final int YES_OPTION = JOptionPane.YES_OPTION; private static final int YES_OPTION = JOptionPane.YES_OPTION;
private static final String[] TITLE = { private static final String[] TITLE = {
"Loaded", "Name", "Regex", "Color", "Scope", "Engine", "Sensitive" "Loaded", "Name", "F-Regex", "S-Regex", "Format", "Color", "Scope", "Engine", "Sensitive"
}; };
public RulePane(Object[][] data, JTabbedPane pane) { public RulePane(Object[][] data, JTabbedPane pane) {
@@ -44,14 +44,19 @@ public class RulePane extends JPanel {
private void updateModel() { private void updateModel() {
model = (DefaultTableModel) ruleTable.getModel(); model = (DefaultTableModel) ruleTable.getModel();
} }
private void ruleAddActionPerformed(ActionEvent e, JTabbedPane pane) { private void ruleAddActionPerformed(ActionEvent e, JTabbedPane pane) {
RuleSetting ruleSettingPanel = new RuleSetting(); RuleSetting ruleSettingPanel = new RuleSetting();
ruleSettingPanel.formatTextField.setText("{0}");
int showState = JOptionPane.showConfirmDialog(null, ruleSettingPanel, "Add Rule", JOptionPane.OK_OPTION); int showState = JOptionPane.showConfirmDialog(null, ruleSettingPanel, "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);
ruleData.add(ruleSettingPanel.ruleNameTextField.getText()); ruleData.add(ruleSettingPanel.ruleNameTextField.getText());
ruleData.add(ruleSettingPanel.regexTextField.getText()); ruleData.add(ruleSettingPanel.firstRegexTextField.getText());
ruleData.add(ruleSettingPanel.secondRegexTextField.getText());
ruleData.add(ruleSettingPanel.formatTextField.getText());
ruleData.add(ruleSettingPanel.colorComboBox.getSelectedItem().toString()); ruleData.add(ruleSettingPanel.colorComboBox.getSelectedItem().toString());
ruleData.add(ruleSettingPanel.scopeComboBox.getSelectedItem().toString()); ruleData.add(ruleSettingPanel.scopeComboBox.getSelectedItem().toString());
ruleData.add(ruleSettingPanel.engineComboBox.getSelectedItem().toString()); ruleData.add(ruleSettingPanel.engineComboBox.getSelectedItem().toString());
@@ -66,13 +71,15 @@ public class RulePane extends JPanel {
if (ruleTable.getSelectedRowCount() >= 1){ if (ruleTable.getSelectedRowCount() >= 1){
RuleSetting ruleSettingPanel = new RuleSetting(); RuleSetting ruleSettingPanel = new RuleSetting();
ruleSettingPanel.ruleNameTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 1).toString()); ruleSettingPanel.ruleNameTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 1).toString());
ruleSettingPanel.regexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 2).toString()); ruleSettingPanel.firstRegexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 2).toString());
ruleSettingPanel.colorComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 3).toString()); ruleSettingPanel.secondRegexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 3).toString());
ruleSettingPanel.scopeComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 4).toString()); ruleSettingPanel.formatTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 4).toString());
ruleSettingPanel.engineComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 5).toString()); ruleSettingPanel.colorComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 5).toString());
ruleSettingPanel.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(),6)); ruleSettingPanel.scopeComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 6).toString());
ruleSettingPanel.engineComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 7).toString());
ruleSettingPanel.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(),8));
ruleSettingPanel.sensitiveComboBox.setEnabled( ruleSettingPanel.formatTextField.setEnabled(
ruleSettingPanel.engineComboBox.getSelectedItem().toString().equals("nfa") ruleSettingPanel.engineComboBox.getSelectedItem().toString().equals("nfa")
); );
@@ -80,11 +87,13 @@ public class RulePane extends JPanel {
if (showState == 0){ if (showState == 0){
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
model.setValueAt(ruleSettingPanel.ruleNameTextField.getText(), select, 1); model.setValueAt(ruleSettingPanel.ruleNameTextField.getText(), select, 1);
model.setValueAt(ruleSettingPanel.regexTextField.getText(), select, 2); model.setValueAt(ruleSettingPanel.firstRegexTextField.getText(), select, 2);
model.setValueAt(ruleSettingPanel.colorComboBox.getSelectedItem().toString(), select, 3); model.setValueAt(ruleSettingPanel.secondRegexTextField.getText(), select, 3);
model.setValueAt(ruleSettingPanel.scopeComboBox.getSelectedItem().toString(), select, 4); model.setValueAt(ruleSettingPanel.formatTextField.getText(), select, 4);
model.setValueAt(ruleSettingPanel.engineComboBox.getSelectedItem().toString(), select, 5); model.setValueAt(ruleSettingPanel.colorComboBox.getSelectedItem().toString(), select, 5);
model.setValueAt(ruleSettingPanel.sensitiveComboBox.getSelectedItem(), select, 6); model.setValueAt(ruleSettingPanel.scopeComboBox.getSelectedItem().toString(), select, 6);
model.setValueAt(ruleSettingPanel.engineComboBox.getSelectedItem().toString(), select, 7);
model.setValueAt(ruleSettingPanel.sensitiveComboBox.getSelectedItem(), select, 8);
model = (DefaultTableModel) ruleTable.getModel(); model = (DefaultTableModel) ruleTable.getModel();
ruleProcessor.changeRule((Vector) model.getDataVector().get(select), select, pane.getTitleAt(pane.getSelectedIndex())); ruleProcessor.changeRule((Vector) model.getDataVector().get(select), select, pane.getTitleAt(pane.getSelectedIndex()));
} }
@@ -93,7 +102,7 @@ public class RulePane extends JPanel {
private void ruleRemoveActionPerformed(ActionEvent e, JTabbedPane pane){ private void ruleRemoveActionPerformed(ActionEvent e, JTabbedPane pane){
if (ruleTable.getSelectedRowCount() >= 1){ if (ruleTable.getSelectedRowCount() >= 1){
int isOk = JOptionPane.showConfirmDialog(null, "Are your sure?", "Delete Rule", JOptionPane.OK_OPTION); int isOk = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this rule?", "Info", JOptionPane.OK_OPTION);
if (isOk == 0){ if (isOk == 0){
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
model.removeRow(select); model.removeRow(select);

View File

@@ -9,8 +9,9 @@ import burp.config.ConfigEntry;
*/ */
public class RuleSetting extends JPanel { public class RuleSetting extends JPanel {
public JTextField firstRegexTextField;
public JTextField regexTextField; public JTextField secondRegexTextField;
public JTextField formatTextField;
public JTextField ruleNameTextField; public JTextField ruleNameTextField;
public JComboBox<String> scopeComboBox; public JComboBox<String> scopeComboBox;
public JComboBox<String> engineComboBox; public JComboBox<String> engineComboBox;
@@ -29,21 +30,31 @@ public class RuleSetting extends JPanel {
addLabel("Name:", 0, c); addLabel("Name:", 0, c);
ruleNameTextField = addTextField(0, c); ruleNameTextField = addTextField(0, c);
addLabel("Regex:", 1, c); addLabel("F-Regex:", 1, c);
regexTextField = addTextField(1, c); firstRegexTextField = addTextField(1, c);
addLabel("Scope:", 2, c); addLabel("S-Regex:", 2, c);
scopeComboBox = addComboBox(ConfigEntry.scopeArray, 2, c); secondRegexTextField = addTextField(2, c);
addLabel("Engine:", 3, c); addLabel("Format:", 3, c);
engineComboBox = addComboBox(ConfigEntry.engineArray, 3, c); formatTextField = addTextField(3, c);
engineComboBox.addActionListener(e -> sensitiveComboBox.setEnabled("nfa".equals(engineComboBox.getSelectedItem().toString())));
addLabel("Color:", 4, c); addLabel("Scope:", 4, c);
colorComboBox = addComboBox(ConfigEntry.colorArray, 4, c); scopeComboBox = addComboBox(ConfigEntry.scopeArray, 4, c);
addLabel("Sensitive:", 5, c); addLabel("Engine:", 5, c);
sensitiveComboBox = addComboBox(new Boolean[]{true, false}, 5, c); engineComboBox = addComboBox(ConfigEntry.engineArray, 5, c);
engineComboBox.addActionListener(e -> {
boolean isNfa = "nfa".equals(engineComboBox.getSelectedItem().toString());
formatTextField.setEnabled(isNfa);
formatTextField.setText(isNfa ? formatTextField.getText() : "{0}");
});
addLabel("Color:", 6, c);
colorComboBox = addComboBox(ConfigEntry.colorArray, 6, c);
addLabel("Sensitive:", 7, c);
sensitiveComboBox = addComboBox(new Boolean[]{true, false}, 7, c);
} }
private void addLabel(String text, int y, GridBagConstraints c) { private void addLabel(String text, int y, GridBagConstraints c) {