Compare commits

...

19 Commits
2.5.1 ... 2.5.6

Author SHA1 Message Date
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
gh0stkey
0225c00f69 Version: 2.5.5 Update 2023-10-26 14:17:56 +08:00
gh0stkey
eafae602b8 Version: 2.5.4.1 2023-10-25 16:02:07 +08:00
gh0stkey
e56d8eb5d5 Version: 2.5.4 Update 2023-10-24 17:54:44 +08:00
gh0stkey
681cce0644 Version: 2.5.4 Update 2023-10-24 17:51:21 +08:00
gh0stkey
d43809e25f Version: 2.5.3 Update 2023-10-23 21:59:08 +08:00
gh0stkey
567dea6c60 Version: 2.5.3 Update 2023-10-23 21:51:12 +08:00
ᴋᴇʏ
8c388510c5 Version: 2.5.2 Update 2023-10-19 22:57:40 +08:00
ᴋᴇʏ
e22596819b Version: 2.5.2 Update 2023-10-19 22:46:11 +08:00
ᴋᴇʏ
d2cd7a0d03 Version: 2.5.2 Update 2023-10-19 22:45:44 +08:00
ᴋᴇʏ
67afe1f650 Version: 2.5.2 Update 2023-10-19 22:44:34 +08:00
ᴋᴇʏ
0602346249 Version: 2.5.2 Update 2023-10-19 22:43:29 +08:00
ᴋᴇʏ
953b966961 Version: 2.5.2 Update 2023-10-19 22:42:54 +08:00
ᴋᴇʏ
4c23d62576 Version: 2.5.2 Update 2023-10-19 22:41:50 +08:00
ᴋᴇʏ
6e9b8c8f37 Version: 2.5.2 Update 2023-10-19 22:41:14 +08:00
ᴋᴇʏ
ed58d891d5 Version: 2.5.2 Update 2023-10-19 22:40:06 +08:00
ᴋᴇʏ
33f5cab037 Version: 2.5.2 Update 2023-10-19 22:38:50 +08:00
20 changed files with 848 additions and 402 deletions

View File

@@ -21,6 +21,8 @@
1. Linux/Mac用户的配置文件目录`~/.config/HaE/` 1. Linux/Mac用户的配置文件目录`~/.config/HaE/`
2. Windows用户的配置文件目录`%USERPROFILE%/.config/HaE/` 2. Windows用户的配置文件目录`%USERPROFILE%/.config/HaE/`
除此之外,您也可以选择将配置文件存放在`HaE Jar包`的同级目录下的`/.config/HaE/`中,**以便于离线携带**。
## 优势特点 ## 优势特点
1. 精细配置:高度自由的配置选项,以满足各类精细化场景需求。 1. 精细配置:高度自由的配置选项,以满足各类精细化场景需求。

View File

@@ -30,5 +30,4 @@ dependencies {
compile group: 'org.yaml', name: 'snakeyaml', version: '1.28' compile group: 'org.yaml', name: 'snakeyaml', version: '1.28'
compile 'net.sourceforge.jregex:jregex:1.2_01' compile 'net.sourceforge.jregex:jregex:1.2_01'
compile 'dk.brics.automaton:automaton:1.11-8' compile 'dk.brics.automaton:automaton:1.11-8'
compile 'com.squareup.okhttp:okhttp:2.7.5'
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -1,9 +1,14 @@
package burp; package burp;
import burp.config.ConfigLoader;
import burp.core.processor.ColorProcessor; import burp.core.processor.ColorProcessor;
import burp.core.processor.MessageProcessor; import burp.core.processor.MessageProcessor;
import burp.ui.MainUI; import burp.ui.MainUI;
import burp.ui.board.DatatablePanel;
import burp.ui.board.MessagePanel; import burp.ui.board.MessagePanel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.*; import java.util.*;
import javax.swing.*; import javax.swing.*;
@@ -19,10 +24,9 @@ import javax.swing.event.ChangeListener;
public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEditorTabFactory, ITab { public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEditorTabFactory, ITab {
private MainUI main; private MainUI main;
// stdout变成公开属性便于其他类调用输出调试信息
public static PrintWriter stdout; public static PrintWriter stdout;
private IBurpExtenderCallbacks callbacks; public static IBurpExtenderCallbacks callbacks;
private static IExtensionHelpers helpers; public static IExtensionHelpers helpers;
ColorProcessor colorProcessor = new ColorProcessor(); ColorProcessor colorProcessor = new ColorProcessor();
MessageProcessor messageProcessor = new MessageProcessor(); MessageProcessor messageProcessor = new MessageProcessor();
private MessagePanel messagePanel; private MessagePanel messagePanel;
@@ -30,10 +34,12 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
@Override @Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks)
{ {
this.callbacks = callbacks; BurpExtender.callbacks = callbacks;
BurpExtender.helpers = callbacks.getHelpers(); BurpExtender.helpers = callbacks.getHelpers();
String version = "2.5.1"; new ConfigLoader();
String version = "2.5.6";
callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version)); callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 定义输出 // 定义输出
@@ -55,7 +61,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
} }
private void initialize(){ private void initialize() {
messagePanel = new MessagePanel(callbacks, helpers); messagePanel = new MessagePanel(callbacks, helpers);
main = new MainUI(messagePanel); main = new MainUI(messagePanel);
callbacks.customizeUiComponent(main); callbacks.customizeUiComponent(main);
@@ -63,13 +69,49 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
} }
@Override @Override
public String getTabCaption(){ public String getTabCaption() {
return "HaE"; return "HaE";
} }
@Override @Override
public Component getUiComponent() { public Component getUiComponent() {
return main; 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;
} }
/** /**
@@ -103,15 +145,18 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
} }
List<Map<String, String>> result = null; List<Map<String, String>> result = null;
try { try {
result = messageProcessor.processMessage(helpers, content, messageIsRequest, true, host); result = messageProcessor.processMessage(helpers, content, messageIsRequest, true, host);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String resComment = ""; String resComment = "";
String resColor = ""; String resColor = "";
String originalColor = messageInfo.getHighlight(); String originalColor = messageInfo.getHighlight();
String originalComment = messageInfo.getComment(); String originalComment = messageInfo.getComment();
if (result != null && !result.isEmpty() && result.size() > 0) { if (result != null && !result.isEmpty() && result.size() > 0) {
List<String> colorList = new ArrayList<>(); List<String> colorList = new ArrayList<>();
@@ -155,7 +200,6 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count); itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count);
} else { } else {
itemCounts.put(item, 0); itemCounts.put(item, 0);
BurpExtender.stdout.println(String.format("%s: %s", "A", item));
} }
} }
@@ -174,7 +218,8 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
class MarkInfoTab implements IMessageEditorTab { class MarkInfoTab implements IMessageEditorTab {
private final JTabbedPane jTabbedPane = new JTabbedPane(); private final JTabbedPane jTabbedPane = new JTabbedPane();
private JTable jTable = new JTable(); private DatatablePanel dataPanel;
private JTable dataTable;
private final IMessageEditorController controller; private final IMessageEditorController controller;
private Map<String, String> extractRequestMap; private Map<String, String> extractRequestMap;
private Map<String, String> extractResponseMap; private Map<String, String> extractResponseMap;
@@ -194,10 +239,10 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
jTabbedPane.addChangeListener(new ChangeListener() { jTabbedPane.addChangeListener(new ChangeListener() {
@Override @Override
public void stateChanged(ChangeEvent arg0) { public void stateChanged(ChangeEvent arg0) {
jTable = (JTable) ((JScrollPane)jTabbedPane.getSelectedComponent()).getViewport().getView(); dataTable = ((DatatablePanel)jTabbedPane.getSelectedComponent()).getTable();
} }
}); });
return this.jTabbedPane; return jTabbedPane;
} }
@Override @Override
@@ -237,15 +282,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
*/ */
@Override @Override
public byte[] getSelectedData() { public byte[] getSelectedData() {
int[] selectRows = jTable.getSelectedRows(); return helpers.stringToBytes(dataPanel.getSelectedData(dataTable));
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());
} }
/** /**
@@ -267,18 +304,12 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
*/ */
public void makeTable(Map<String, String> dataMap) { public void makeTable(Map<String, String> dataMap) {
ArrayList<String> lTitleList = new ArrayList<>(); ArrayList<String> lTitleList = new ArrayList<>();
dataMap.keySet().forEach(i->{ dataMap.keySet().forEach(i->{
String[] extractData = dataMap.get(i).split("\n"); String[] extractData = dataMap.get(i).split("\n");
Object[][] data = new Object[extractData.length][1];
for (int x = 0; x < extractData.length; x++) {
data[x][0] = extractData[x];
}
JTable infoTable = new JTable(data, new Object[]{"Information"});
infoTable.setAutoCreateRowSorter(true);
JScrollPane jScrollPane = new JScrollPane(infoTable);
lTitleList.add(i); lTitleList.add(i);
this.jTabbedPane.addTab(i, jScrollPane); dataPanel = new DatatablePanel(i, Arrays.asList(extractData));
jTabbedPane.addTab(i, dataPanel);
}); });
/* /*
@@ -286,9 +317,9 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
* 采用全局ArrayList的方式遍历删除Tab以此应对BurpSuite缓存机制导致的MarkInfo UI错误展示。 * 采用全局ArrayList的方式遍历删除Tab以此应对BurpSuite缓存机制导致的MarkInfo UI错误展示。
*/ */
titleList.forEach(t->{ titleList.forEach(t->{
int indexOfTab = this.jTabbedPane.indexOfTab(t); int indexOfTab = jTabbedPane.indexOfTab(t);
if (indexOfTab != -1) { if (indexOfTab != -1) {
this.jTabbedPane.removeTabAt(indexOfTab); jTabbedPane.removeTabAt(indexOfTab);
} }
}); });

View File

@@ -1,5 +1,6 @@
package burp.config; package burp.config;
import burp.BurpExtender;
import burp.rule.utils.RuleTool; import burp.rule.utils.RuleTool;
import burp.rule.utils.YamlTool; import burp.rule.utils.YamlTool;
import java.io.*; import java.io.*;
@@ -19,7 +20,7 @@ import org.yaml.snakeyaml.Yaml;
public class ConfigLoader { public class ConfigLoader {
private static final Yaml yaml = YamlTool.newStandardYaml(); private static final Yaml yaml = YamlTool.newStandardYaml();
private static final String HaEConfigPath = String.format("%s/.config/HaE", System.getProperty("user.home")); private static final String HaEConfigPath = determineConfigPath();
private static final String RulesFilePath = String.format("%s/%s", HaEConfigPath, "Rules.yml"); private static final String RulesFilePath = String.format("%s/%s", HaEConfigPath, "Rules.yml");
private static final String ConfigFilePath = String.format("%s/%s", HaEConfigPath, "Config.yml"); private static final String ConfigFilePath = String.format("%s/%s", HaEConfigPath, "Config.yml");
@@ -31,44 +32,66 @@ public class ConfigLoader {
} }
File configFilePath = new File(ConfigFilePath); File configFilePath = new File(ConfigFilePath);
if (!(configFilePath.exists() && configFilePath.isFile())) { if (!(configFilePath.exists() && configFilePath.isFile())) {
initConfig(); initConfig();
initRules();
}
ConfigEntry.globalRules = ConfigLoader.getRules();
} }
public void initConfig() { File rulesFilePath = new File(RulesFilePath);
if (!(rulesFilePath.exists() && rulesFilePath.isFile())) {
initRules();
}
ConfigEntry.globalRules = getRules();
}
private static String determineConfigPath() {
// 优先级1用户根目录
String userConfigPath = String.format("%s/.config/HaE", System.getProperty("user.home"));
if (isValidConfigPath(userConfigPath)) {
return userConfigPath;
}
// 优先级2Jar包所在目录
String jarPath = BurpExtender.callbacks.getExtensionFilename();
String jarDirectory = new File(jarPath).getParent();
String jarConfigPath = String.format("%s/.config/HaE", jarDirectory);
if (isValidConfigPath(jarConfigPath)) {
return jarConfigPath;
}
return userConfigPath;
}
private static boolean isValidConfigPath(String configPath) {
File configPathFile = new File(configPath);
return configPathFile.exists() && configPathFile.isDirectory();
}
public static void initConfig() {
Map<String, Object> r = new LinkedHashMap<>(); Map<String, Object> r = new LinkedHashMap<>();
r.put("rulesPath", RulesFilePath);
r.put("excludeSuffix", getExcludeSuffix()); r.put("excludeSuffix", getExcludeSuffix());
try { try {
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(ConfigFilePath)), 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();
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
public void initRules() { public static void initRules() {
RuleTool rt = new RuleTool(RulesFilePath); RuleTool rt = new RuleTool(RulesFilePath);
rt.getRulesFromSite(); rt.getRulesFromSite();
} }
public static String getRulesFilePath() { public static String getRulesFilePath() {
try {
Map<String, Object> r = YamlTool.loadYaml(ConfigFilePath);
return r.get("rulesPath").toString();
} catch (Exception e) {
e.printStackTrace();
return RulesFilePath; return RulesFilePath;
} }
}
public String getExcludeSuffix(){ public static String getExcludeSuffix(){
String excludeSuffix = ""; String excludeSuffix = "";
File yamlSetting = new File(ConfigFilePath); File yamlSetting = new File(ConfigFilePath);
if (yamlSetting.exists() && yamlSetting.isFile()) { if (yamlSetting.exists() && yamlSetting.isFile()) {
try { try {
InputStream inorder = Files.newInputStream(Paths.get(ConfigFilePath)); InputStream inorder = Files.newInputStream(Paths.get(ConfigFilePath));
@@ -81,6 +104,7 @@ public class ConfigLoader {
} else { } else {
excludeSuffix = ConfigEntry.excludeSuffix; excludeSuffix = ConfigEntry.excludeSuffix;
} }
return excludeSuffix; return excludeSuffix;
} }
@@ -112,20 +136,18 @@ public class ConfigLoader {
resRule.put(groupFields.get("group").toString(), dataArray); resRule.put(groupFields.get("group").toString(), dataArray);
} }
} }
return resRule; return resRule;
} }
public void setExcludeSuffix(String excludeSuffix){ public static void setExcludeSuffix(String excludeSuffix){
Map<String,Object> r = new LinkedHashMap<>(); Map<String,Object> r = new LinkedHashMap<>();
r.put("rulesPath", getRulesFilePath());
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(RulesFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws); yaml.dump(r, ws);
ws.close();
}catch (Exception ex){ }catch (Exception ex){
ex.printStackTrace(); ex.printStackTrace();
} }
} }
} }

View File

@@ -1,6 +1,5 @@
package burp.core.processor; package burp.core.processor;
import burp.BurpExtender;
import burp.core.GlobalCachePool; import burp.core.GlobalCachePool;
import burp.core.utils.HashCalculator; import burp.core.utils.HashCalculator;
import burp.core.utils.MatchTool; import burp.core.utils.MatchTool;
@@ -49,7 +48,7 @@ public class DataProcessingUnit {
public Map<String, Map<String, Object>> matchContentByRegex(byte[] content, String headers, byte[] body, String scopeString, String host) public Map<String, Map<String, Object>> matchContentByRegex(byte[] content, String headers, byte[] body, String scopeString, String host)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
// 先从池里判断是否有已经匹配好的结果 // 先从缓存池里判断是否有已经匹配好的结果
String messageIndex = HashCalculator.calculateHash(content); String messageIndex = HashCalculator.calculateHash(content);
Map<String, Map<String, Object>> map = GlobalCachePool.getFromCache(messageIndex); Map<String, Map<String, Object>> map = GlobalCachePool.getFromCache(messageIndex);
if (map != null) { if (map != null) {
@@ -57,10 +56,9 @@ public class DataProcessingUnit {
} else { } else {
// 最终返回的结果 // 最终返回的结果
Map<String, Map<String, Object>> finalMap = new HashMap<>(); Map<String, Map<String, Object>> finalMap = new HashMap<>();
ConfigEntry.globalRules.keySet().forEach(i -> { ConfigEntry.globalRules.keySet().parallelStream().forEach(i -> {
for (Object[] objects : ConfigEntry.globalRules.get(i)) { for (Object[] objects : ConfigEntry.globalRules.get(i)) {
// 多线程执行,一定程度上减少阻塞现象 // 多线程执行,一定程度上减少阻塞现象
Thread t = new Thread(() -> {
String matchContent = ""; String matchContent = "";
// 遍历获取规则 // 遍历获取规则
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
@@ -179,14 +177,6 @@ public class DataProcessingUnit {
} }
} }
} }
});
t.start();
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
}
} }
}); });
GlobalCachePool.addToCache(messageIndex, finalMap); GlobalCachePool.addToCache(messageIndex, finalMap);

View File

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

View File

@@ -9,11 +9,8 @@ import burp.config.ConfigLoader;
*/ */
public class MatchTool { public class MatchTool {
// 匹配后缀
ConfigLoader configLoader = new ConfigLoader();
public boolean matchUrlSuffix(String str) { public boolean matchUrlSuffix(String str) {
Pattern pattern = new Pattern(String.format("[\\w]+[\\.](%s)", configLoader.getExcludeSuffix()), REFlags.IGNORE_CASE); Pattern pattern = new Pattern(String.format("[\\w]+[\\.](%s)", ConfigLoader.getExcludeSuffix()), REFlags.IGNORE_CASE);
jregex.Matcher matcher = pattern.matcher(str); jregex.Matcher matcher = pattern.matcher(str);
return matcher.find(); return matcher.find();
} }

View File

@@ -1,9 +1,11 @@
package burp.rule.utils; package burp.rule.utils;
import com.squareup.okhttp.OkHttpClient; import burp.*;
import com.squareup.okhttp.Request; import burp.config.ConfigEntry;
import com.squareup.okhttp.Response; import burp.config.ConfigLoader;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.net.URL;
import java.util.Arrays;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
/** /**
@@ -11,25 +13,42 @@ import javax.swing.JOptionPane;
*/ */
public class RuleTool { public class RuleTool {
private String rulesFilePath; private String rulesFilePath;
private boolean isSuccess;
public RuleTool(String rulesFilePath) { public RuleTool(String rulesFilePath) {
this.rulesFilePath = rulesFilePath; this.rulesFilePath = rulesFilePath;
} }
public void getRulesFromSite() { public void getRulesFromSite() {
String url = "https://cdn.jsdelivr.net/gh/gh0stkey/HaE@gh-pages/Rules.yml"; // 以独立线程使用BurpSuite官方请求接口获取规则
OkHttpClient httpClient = new OkHttpClient(); Thread t = new Thread(()->{
Request httpRequest = new Request.Builder().url(url).get().build();
try { try {
Response httpResponse = httpClient.newCall(httpRequest).execute(); URL url = new URL("https://cdn.jsdelivr.net/gh/gh0stkey/HaE@gh-pages/Rules.yml");
// 获取官方规则文件,在线更新写入 IHttpService iHttpService = BurpExtender.helpers.buildHttpService(url.getHost(), 443, true);
IHttpRequestResponse iHttpRequestResponse = BurpExtender.callbacks.makeHttpRequest(iHttpService, BurpExtender.helpers.buildHttpRequest(url));
byte[] responseByte = iHttpRequestResponse.getResponse();
IResponseInfo iResponseInfo = BurpExtender.helpers.analyzeResponse(responseByte);
int bodyOffset = iResponseInfo.getBodyOffset();
byte[] responseBodyByte = Arrays.copyOfRange(responseByte, bodyOffset, responseByte.length);
FileOutputStream fileOutputStream = new FileOutputStream(this.rulesFilePath); FileOutputStream fileOutputStream = new FileOutputStream(this.rulesFilePath);
fileOutputStream.write(httpResponse.body().bytes()); fileOutputStream.write(responseBodyByte);
JOptionPane.showMessageDialog(null, "Rules updated successfully!", "Info", fileOutputStream.close();
JOptionPane.INFORMATION_MESSAGE); isSuccess = true;
} catch (Exception ignored) { } catch (Exception e) {
JOptionPane.showMessageDialog(null, "Please check your network!", "Error", isSuccess = false;
JOptionPane.ERROR_MESSAGE); }
});
t.start();
try {
t.join(10000);
} catch (Exception e) {
isSuccess = false;
}
if (isSuccess) {
JOptionPane.showMessageDialog(null, "Rules update successfully!", "Info", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "Rule update failed, please check the network!", "Error", JOptionPane.ERROR_MESSAGE);
} }
} }
} }

View File

@@ -1,8 +1,9 @@
package burp.rule.utils; package burp.rule.utils;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map; import java.util.Map;
import burp.BurpExtender;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -25,13 +26,11 @@ public class YamlTool {
public static Map<String, Object> loadYaml(String filePath) { public static Map<String, Object> loadYaml(String filePath) {
try { try {
InputStream inputStream = new FileInputStream(filePath); InputStream inputStream = Files.newInputStream(Paths.get(filePath));
Yaml yaml = newStandardYaml(); return newStandardYaml().load(inputStream);
return yaml.load(inputStream); } catch (Exception e) {
} catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
} }
} }

View File

@@ -3,7 +3,6 @@ package burp.ui;
import burp.config.ConfigEntry; import burp.config.ConfigEntry;
import burp.config.ConfigLoader; import burp.config.ConfigLoader;
import burp.rule.RuleProcessor; import burp.rule.RuleProcessor;
import burp.rule.utils.RuleTool;
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;
@@ -21,12 +20,9 @@ import java.util.Map;
*/ */
public class MainUI extends JPanel { public class MainUI extends JPanel {
private final ConfigLoader loadConn = new ConfigLoader();
private MessagePanel messagePanel;
public MainUI(MessagePanel messagePanel) { public MainUI(MessagePanel messagePanel) {
this.messagePanel = messagePanel; databoardPanel = new Databoard(messagePanel);
databoardPanel = new Databoard(this.messagePanel);
initComponents(); initComponents();
} }
@@ -41,13 +37,9 @@ public class MainUI extends JPanel {
private void onlineUpdateActionPerformed(ActionEvent e) { private void onlineUpdateActionPerformed(ActionEvent e) {
// 添加提示框防止用户误触导致配置更新 // 添加提示框防止用户误触导致配置更新
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to update config?", "Info", int retCode = JOptionPane.showConfirmDialog(null, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION);
JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) { if (retCode == JOptionPane.YES_OPTION) {
String rulesFilePath = rulesPathTextField.getText(); ConfigLoader.initRules();
RuleTool rt = new RuleTool(rulesFilePath);
rt.getRulesFromSite();
new ConfigLoader();
reloadRule(); reloadRule();
} }
} }
@@ -61,7 +53,6 @@ public class MainUI extends JPanel {
i, i,
new RulePane(rules.get(i), ruleTabbedPane) new RulePane(rules.get(i), ruleTabbedPane)
) )
); );
ruleTabbedPane.addTab("...", new JLabel()); ruleTabbedPane.addTab("...", new JLabel());
ruleSwitch.setListen(true); ruleSwitch.setListen(true);
@@ -72,9 +63,9 @@ public class MainUI extends JPanel {
} }
private void excludeSuffixSaveActionPerformed(ActionEvent e) { private void excludeSuffixSaveActionPerformed(ActionEvent e) {
ConfigLoader loadCon = new ConfigLoader(); ConfigLoader.setExcludeSuffix(excludeSuffixTextField.getText());
loadCon.setExcludeSuffix(excludeSuffixTextField.getText());
} }
private void initComponents() { private void initComponents() {
JTabbedPane mainTabbedPane = new JTabbedPane(); JTabbedPane mainTabbedPane = new JTabbedPane();
ruleTabbedPane = new JTabbedPane(); ruleTabbedPane = new JTabbedPane();
@@ -151,10 +142,10 @@ public class MainUI extends JPanel {
ConfigEntry.globalRules.get(i), ConfigEntry.globalRules.get(i),
ruleTabbedPane))); ruleTabbedPane)));
ruleTabbedPane.addTab("...",new JLabel()); ruleTabbedPane.addTab("...", new JLabel());
rulesPathTextField.setText(ConfigLoader.getRulesFilePath()); rulesPathTextField.setText(ConfigLoader.getRulesFilePath());
excludeSuffixTextField.setText(loadConn.getExcludeSuffix()); excludeSuffixTextField.setText(ConfigLoader.getExcludeSuffix());
ruleSwitch = new TabTitleEditListener(ruleTabbedPane); ruleSwitch = new TabTitleEditListener(ruleTabbedPane);
ruleTabbedPane.addChangeListener(ruleSwitch); ruleTabbedPane.addChangeListener(ruleSwitch);
ruleTabbedPane.addMouseListener(ruleSwitch); ruleTabbedPane.addMouseListener(ruleSwitch);

View File

@@ -16,15 +16,16 @@ public class ColorRenderer extends DefaultTableCellRenderer {
public ColorRenderer(List<LogEntry> log, JTable table) { public ColorRenderer(List<LogEntry> log, JTable table) {
this.log = log; this.log = log;
this.colorMap.put("red", Color.RED); // 与BurpSuite的颜色保持一致
this.colorMap.put("orange", Color.ORANGE); this.colorMap.put("red", new Color(0xFF, 0x64, 0x64));
this.colorMap.put("yellow", Color.YELLOW); this.colorMap.put("orange", new Color(0xFF, 0xC8, 0x64));
this.colorMap.put("green", Color.GREEN); this.colorMap.put("yellow", new Color(0xFF, 0xFF, 0x64));
this.colorMap.put("cyan", Color.CYAN); this.colorMap.put("green", new Color(0x64, 0xFF, 0x64));
this.colorMap.put("blue", Color.BLUE); this.colorMap.put("cyan", new Color(0x64, 0xFF, 0xFF));
this.colorMap.put("pink", Color.PINK); this.colorMap.put("blue", new Color(0x64, 0x64, 0xFF));
this.colorMap.put("magenta", Color.MAGENTA); this.colorMap.put("pink", new Color(0xFF, 0xC8, 0xC8));
this.colorMap.put("gray", Color.GRAY); this.colorMap.put("magenta", new Color(0xFF, 0x64, 0xFF));
this.colorMap.put("gray", new Color(0xB4, 0xB4, 0xB4));
this.table = table; this.table = table;
} }
@@ -40,13 +41,15 @@ public class ColorRenderer extends DefaultTableCellRenderer {
Color color = colorMap.get(colorByLog); Color color = colorMap.get(colorByLog);
if (isSelected) { if (isSelected) {
// 如果行被选中,设置阴影颜色 // 通过更改RGB颜色来达成阴影效果
component.setBackground(new Color(173, 216, 230)); // Light Blue component.setBackground(new Color(color.getRed()-0x20, color.getGreen()-0x20, color.getBlue()-0x20));
} else { } else {
// 否则使用原始颜色 // 否则使用原始颜色
component.setBackground(color); component.setBackground(color);
} }
component.setForeground(Color.BLACK);
return component; return component;
} }

View File

@@ -7,7 +7,6 @@ import burp.ui.board.MessagePanel.Table;
import java.util.*; import java.util.*;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
@@ -27,12 +26,28 @@ public class Databoard extends JPanel {
private static Boolean isMatchHost = false; private static Boolean isMatchHost = false;
private JLabel hostLabel; private JLabel hostLabel;
private JTextField hostTextField; private JTextField hostTextField;
private JTabbedPane dataTabbedPaneA; private JTabbedPane dataTabbedPane;
private JTabbedPane dataTabbedPaneB;
private JButton clearButton; private JButton clearButton;
private JSplitPane splitPane; private JSplitPane splitPane;
private MessagePanel messagePanel; private MessagePanel messagePanel;
private Table table; private Table table;
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) { public Databoard(MessagePanel messagePanel) {
this.messagePanel = messagePanel; this.messagePanel = messagePanel;
@@ -40,8 +55,7 @@ public class Databoard extends JPanel {
} }
private void cleanUI() { private void cleanUI() {
dataTabbedPaneA.removeAll(); dataTabbedPane.removeAll();
dataTabbedPaneB.removeAll();
splitPane.setVisible(false); splitPane.setVisible(false);
} }
@@ -68,8 +82,7 @@ public class Databoard extends JPanel {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
hostLabel = new JLabel(); hostLabel = new JLabel();
hostTextField = new JTextField(); hostTextField = new JTextField();
dataTabbedPaneA = new JTabbedPane(JTabbedPane.TOP); dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
dataTabbedPaneB = new JTabbedPane(JTabbedPane.TOP);
clearButton = new JButton(); clearButton = new JButton();
//======== this ======== //======== this ========
@@ -100,6 +113,23 @@ public class Databoard extends JPanel {
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());
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(); setAutoMatch();
} }
@@ -111,38 +141,55 @@ public class Databoard extends JPanel {
* 设置输入自动匹配 * 设置输入自动匹配
*/ */
private void setAutoMatch() { private void setAutoMatch() {
final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); populateComboBoxModel();
final JComboBox hostComboBox = new JComboBox(comboBoxModel) { hostComboBox.setSelectedItem(null);
hostComboBox.addActionListener(this::handleComboBoxAction);
hostTextField.addKeyListener(new KeyAdapter() {
@Override @Override
public Dimension getPreferredSize() { public void keyPressed(KeyEvent e) {
setMaximumRowCount(5); handleKeyEvents(e);
return new Dimension(super.getPreferredSize().width, 0);
} }
}; });
isMatchHost = false; hostTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
update(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
update(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
update(e);
}
public void update(DocumentEvent e) {
filterComboBoxList();
}
});
}
private void populateComboBoxModel() {
for (String host : getHostByList()) { for (String host : getHostByList()) {
comboBoxModel.addElement(host); comboBoxModel.addElement(host);
} }
}
hostComboBox.setSelectedItem(null); private void handleComboBoxAction(ActionEvent e) {
if (!isMatchHost && hostComboBox.getSelectedItem() != null) {
hostComboBox.addActionListener(e -> {
if (!isMatchHost) {
if (hostComboBox.getSelectedItem() != null) {
String selectedHost = hostComboBox.getSelectedItem().toString(); String selectedHost = hostComboBox.getSelectedItem().toString();
hostTextField.setText(selectedHost); hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost); populateTabbedPaneByHost(selectedHost);
} }
} }
});
// 事件监听 private void handleKeyEvents(KeyEvent e) {
hostTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
isMatchHost = true; isMatchHost = true;
int keyCode = e.getKeyCode(); int keyCode = e.getKeyCode();
@@ -150,16 +197,13 @@ public class Databoard extends JPanel {
e.setKeyCode(KeyEvent.VK_ENTER); e.setKeyCode(KeyEvent.VK_ENTER);
} }
if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) { if (Arrays.asList(KeyEvent.VK_ENTER, KeyEvent.VK_UP, KeyEvent.VK_DOWN).contains(keyCode)) {
e.setSource(hostComboBox); e.setSource(hostComboBox);
hostComboBox.dispatchEvent(e); hostComboBox.dispatchEvent(e);
if (keyCode == KeyEvent.VK_ENTER) { if (keyCode == KeyEvent.VK_ENTER) {
String selectedItem = hostComboBox.getSelectedItem().toString(); updateTextFieldFromComboBox();
hostTextField.setText(selectedItem);
populateTabbedPaneByHost(selectedItem);
hostComboBox.setPopupVisible(false); hostComboBox.setPopupVisible(false);
return; e.consume();
} }
} }
@@ -169,52 +213,48 @@ public class Databoard extends JPanel {
isMatchHost = false; isMatchHost = false;
} }
});
hostTextField.getDocument().addDocumentListener(new DocumentListener() { private void updateTextFieldFromComboBox() {
@Override Object selectedItem = hostComboBox.getSelectedItem();
public void insertUpdate(DocumentEvent e) { if (selectedItem != null) {
updateList(); String selectedHost = selectedItem.toString();
hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
}
} }
@Override private void filterComboBoxList() {
public void removeUpdate(DocumentEvent e) {
updateList();
}
@Override
public void changedUpdate(DocumentEvent e) {
updateList();
}
private void updateList() {
isMatchHost = true; isMatchHost = true;
comboBoxModel.removeAllElements(); comboBoxModel.removeAllElements();
String input = hostTextField.getText().toLowerCase(); String input = hostTextField.getText().toLowerCase();
if (!input.isEmpty()){
if (!input.isEmpty()) {
for (String host : getHostByList()) { for (String host : getHostByList()) {
String lowerCaseHost = host.toLowerCase(); String lowerCaseHost = host.toLowerCase();
if (lowerCaseHost.contains(input)) { if (lowerCaseHost.contains(input)) {
if (lowerCaseHost.equals(input)) {
comboBoxModel.insertElementAt(lowerCaseHost, 0);
comboBoxModel.setSelectedItem(lowerCaseHost);
} else {
comboBoxModel.addElement(host); comboBoxModel.addElement(host);
} }
} }
} }
}
hostComboBox.setPopupVisible(comboBoxModel.getSize() > 0); hostComboBox.setPopupVisible(comboBoxModel.getSize() > 0);
isMatchHost = false; isMatchHost = false;
} }
});
hostTextField.setLayout(new BorderLayout());
hostTextField.add(hostComboBox, BorderLayout.SOUTH);
}
private void applyHostFilter(String filterText) { private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter(); TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter();
if (filterText.contains("*.")) { if (filterText.contains("*.")) {
filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", ""); filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
} else if (filterText.contains("*")) { } else if (filterText.contains("*")) {
filterText = ""; filterText = "";
} }
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1); RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1);
sorter.setRowFilter(filter); sorter.setRowFilter(filter);
filterText = filterText.isEmpty() ? "*" : filterText; filterText = filterText.isEmpty() ? "*" : filterText;
@@ -251,51 +291,58 @@ public class Databoard extends JPanel {
selectedDataMap = dataMap.get(selectedHost); selectedDataMap = dataMap.get(selectedHost);
} }
// 由于removeChangeListener不知什么原因不生效因此建立两个tabbedPane dataTabbedPane.removeAll();
dataTabbedPaneA.removeAll();
dataTabbedPaneB.removeAll();
ChangeListener changeListenerInstance = new ChangeListener() { dataTabbedPane.setPreferredSize(new Dimension(500,0));
@Override dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
public void stateChanged(ChangeEvent e) { splitPane.setLeftComponent(dataTabbedPane);
int selectedIndex = dataTabbedPaneA.getSelectedIndex();
String selectedTitle = "";
if (selectedIndex != -1) {
selectedTitle = dataTabbedPaneA.getTitleAt(selectedIndex);
}
applyHostFilter(selectedTitle);
}
};
if (selectedHost.equals("**")) { if (selectedHost.equals("**")) {
dataTabbedPaneA.setPreferredSize(new Dimension(500,0));
dataTabbedPaneA.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPaneA);
for (Map.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) { for (Map.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) {
JTabbedPane newTabbedPane = new JTabbedPane(); JTabbedPane newTabbedPane = new JTabbedPane();
newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) { for (Map.Entry<String, List<String>> entrySet : entry.getValue().entrySet()) {
Thread t = new Thread(() -> { currentWorker = new SwingWorker<Object, Void>() {
String tabTitle = String.format("%s (%s)", entrySet.getKey(), entrySet.getValue().size()); @Override
newTabbedPane.addTab(tabTitle, new JScrollPane(new DataTable(entrySet.getKey(), entrySet.getValue()))); protected Object[] doInBackground() throws Exception {
dataTabbedPaneA.addTab(entry.getKey(), newTabbedPane); String tabTitle = String.format("%s (%s)", entrySet.getKey(),
}); entrySet.getValue().size());
t.start(); DatatablePanel datatablePanel = new DatatablePanel(entrySet.getKey(),
entrySet.getValue());
datatablePanel.setTableListener(messagePanel);
return new Object[] {tabTitle, datatablePanel};
}
@Override
protected void done() {
if (!isCancelled()) {
try { try {
t.join(); Object[] result = (Object[]) get();
newTabbedPane.addTab(result[0].toString(), (DatatablePanel) result[1]);
dataTabbedPane.addTab(entry.getKey(), newTabbedPane);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
dataTabbedPaneA.addChangeListener(changeListenerInstance); };
currentWorker.execute();
}
}
dataTabbedPane.addChangeListener(changeListenerInstance);
} else { } else {
dataTabbedPaneB.setPreferredSize(new Dimension(500,0)); dataTabbedPane.removeChangeListener(changeListenerInstance);
dataTabbedPaneB.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPaneB);
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) { for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size()); String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
dataTabbedPaneB.addTab(tabTitle, new JScrollPane(new DataTable(entry.getKey(), entry.getValue()))); DatatablePanel datatablePanel = new DatatablePanel(entry.getKey(), entry.getValue());
datatablePanel.setTableListener(messagePanel);
dataTabbedPane.addTab(tabTitle, datatablePanel);
} }
} }
@@ -321,34 +368,21 @@ 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();
}
}
}
};
class DataTable extends JTable { dataTabbedPane.addChangeListener(changeListener);
public DataTable(String tableName, List<String> list){
DefaultTableModel model = new DefaultTableModel();
Object[][] data = new Object[list.size()][1];
for (int x = 0; x < list.size(); x++) {
data[x][0] = list.get(x);
}
model.setDataVector(data, new Object[]{"Information"});
setAutoCreateRowSorter(true);
setModel(model);
setDefaultEditor(Object.class, null);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = getSelectedRow();
if (selectedRow != -1) {
String rowData = getValueAt(selectedRow, 0).toString();
messagePanel.applyMessageFilter(tableName, rowData);
}
}
}
});
} }
} }
} }

View File

@@ -0,0 +1,234 @@
package burp.ui.board;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.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 int pageSize; // 动态计算的,每页显示多少条记录
private int currentPage; // 当前页码
private List<String> fullList; // 假设这是一个包含所有数据的列表
private JScrollPane scrollPane;
private String tableName;
private final int SHOW_LENGTH = 3000;
public DatatablePanel(String tableName, List<String> list) {
fullList = list;
currentPage = 0;
pageSize = 10;
this.tableName = tableName;
String[] columnNames = {"#", "Information"};
model = new DefaultTableModel(columnNames, 0);
table = new JTable(model);
sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
TableColumn idColumn = table.getColumnModel().getColumn(0);
idColumn.setMaxWidth(50);
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), 1);
sorter.setRowFilter(rowFilter);
}
}
});
// 设置布局
scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
updatePageSize();
}
});
// 添加滚动监听器,以加载更多数据
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if (fullList.size() > SHOW_LENGTH) {
if (!e.getValueIsAdjusting() && !scrollPane.getVerticalScrollBar().getValueIsAdjusting()) {
if (scrollPane.getVerticalScrollBar().getValue() == scrollPane.getVerticalScrollBar().getMaximum() - scrollPane.getVerticalScrollBar().getVisibleAmount()) {
if ((currentPage + 1) * pageSize < fullList.size()) {
currentPage++;
loadPageData();
}
}
}
}
}
});
setLayout(new BorderLayout(0, 5));
add(scrollPane, BorderLayout.CENTER);
add(searchField, BorderLayout.SOUTH);
loadPageData();
}
// 加载指定页的数据
private void loadPageData() {
if (fullList.size() > SHOW_LENGTH) {
int start = currentPage * pageSize;
int end = Math.min((currentPage + 1) * pageSize, fullList.size());
int lastRow = model.getRowCount();
start = Math.max(start, lastRow);
for (int i = start; i < end; i++) {
addRowToTable(model, new Object[]{fullList.get(i)});
}
} else {
for (String item : fullList) {
addRowToTable(model, new Object[]{item});
}
}
}
public void updatePageSize() {
if (fullList.size() > SHOW_LENGTH && isShowing()) {
int oldPageSize = pageSize;
pageSize = getDynamicSize();
if (oldPageSize != pageSize) {
currentPage = 0;
loadPageData();
}
}
}
private int getDynamicSize() {
int visibleHeight = scrollPane.getViewport().getViewRect().height;
int rowHeight = table.getRowHeight();
return Math.max(1, visibleHeight / rowHeight + 2);
}
public void setTableListener(MessagePanel messagePanel) {
table.setDefaultEditor(Object.class, null);
// 表格内容双击事件
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
String rowData = table.getValueAt(selectedRow, 0).toString();
messagePanel.applyMessageFilter(tableName, rowData);
}
}
}
});
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();
}
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); // 添加行
}
public JTable getTable() {
return this.table;
}
}

View File

@@ -9,16 +9,18 @@ public class LogEntry {
private final IHttpRequestResponsePersisted requestResponse; private final IHttpRequestResponsePersisted requestResponse;
private final URL url; private final URL url;
private final String length; private final String length;
private final String status;
private final String color; private final String color;
private final String method; private final String method;
LogEntry(IHttpRequestResponsePersisted requestResponse, String method, URL url, String comment, String length, String color) { LogEntry(IHttpRequestResponsePersisted requestResponse, String method, URL url, String comment, String length, String color, String status) {
this.requestResponse = requestResponse; this.requestResponse = requestResponse;
this.method = method; this.method = method;
this.url = url; this.url = url;
this.comment = comment; this.comment = comment;
this.length = length; this.length = length;
this.color = color; this.color = color;
this.status = status;
} }
public String getColor() { public String getColor() {
@@ -41,6 +43,10 @@ public class LogEntry {
return this.method; return this.method;
} }
public String getStatus() {
return this.status;
}
public IHttpRequestResponsePersisted getRequestResponse() { public IHttpRequestResponsePersisted getRequestResponse() {
return this.requestResponse; return this.requestResponse;
} }

View File

@@ -7,19 +7,24 @@ import burp.IHttpRequestResponsePersisted;
import burp.IHttpService; import burp.IHttpService;
import burp.IMessageEditor; import burp.IMessageEditor;
import burp.IMessageEditorController; import burp.IMessageEditorController;
import burp.IRequestInfo;
import burp.config.ConfigEntry; import burp.config.ConfigEntry;
import burp.core.GlobalCachePool;
import burp.core.utils.HashCalculator;
import burp.core.utils.StringHelper; import burp.core.utils.StringHelper;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
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 javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
@@ -53,7 +58,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
// Length字段根据大小进行排序 // Length字段根据大小进行排序
TableRowSorter<DefaultTableModel> sorter = (TableRowSorter<DefaultTableModel>) logTable.getRowSorter(); TableRowSorter<DefaultTableModel> sorter = (TableRowSorter<DefaultTableModel>) logTable.getRowSorter();
sorter.setComparator(3, new Comparator<String>() { sorter.setComparator(4, new Comparator<String>() {
@Override @Override
public int compare(String s1, String s2) { public int compare(String s1, String s2) {
Integer age1 = Integer.parseInt(s1); Integer age1 = Integer.parseInt(s1);
@@ -62,7 +67,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
} }
}); });
// Color字段根据颜色顺序进行排序 // Color字段根据颜色顺序进行排序
sorter.setComparator(4, new Comparator<String>() { sorter.setComparator(5, new Comparator<String>() {
@Override @Override
public int compare(String s1, String s2) { public int compare(String s1, String s2) {
int index1 = getIndex(s1); int index1 = getIndex(s1);
@@ -117,7 +122,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override @Override
public int getColumnCount() public int getColumnCount()
{ {
return 5; return 6;
} }
@Override @Override
@@ -132,8 +137,10 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
case 2: case 2:
return "Comment"; return "Comment";
case 3: case 3:
return "Length"; return "Status";
case 4: case 4:
return "Length";
case 5:
return "Color"; return "Color";
default: default:
return ""; return "";
@@ -149,6 +156,9 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override @Override
public Object getValueAt(int rowIndex, int columnIndex) public Object getValueAt(int rowIndex, int columnIndex)
{ {
if (filteredLog.isEmpty()) {
return "";
}
LogEntry logEntry = filteredLog.get(rowIndex); LogEntry logEntry = filteredLog.get(rowIndex);
switch (columnIndex) switch (columnIndex)
{ {
@@ -159,8 +169,10 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
case 2: case 2:
return logEntry.getComment(); return logEntry.getComment();
case 3: case 3:
return logEntry.getLength(); return logEntry.getStatus();
case 4: case 4:
return logEntry.getLength();
case 5:
return logEntry.getColor(); return logEntry.getColor();
default: default:
return ""; return "";
@@ -253,6 +265,7 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
} }
} }
fireTableDataChanged(); fireTableDataChanged();
logTable.lastSelectedIndex = -1;
} }
public void deleteByHost(String filterText) { public void deleteByHost(String filterText) {
@@ -296,17 +309,104 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
} }
public void add(IHttpRequestResponse messageInfo, String comment, String length, String color) { public void add(IHttpRequestResponse messageInfo, String comment, String length, String color) {
synchronized(log) synchronized(log) {
{ IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo);
LogEntry logEntry = new LogEntry(callbacks.saveBuffersToTempFiles(messageInfo), helpers.analyzeRequest(messageInfo).getMethod(), URL url = iRequestInfo.getUrl();
helpers.analyzeRequest(messageInfo).getUrl(), comment, length, color); String method = iRequestInfo.getMethod();
String status = String.valueOf(helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode());
LogEntry logEntry = new LogEntry(callbacks.saveBuffersToTempFiles(messageInfo), method, url, comment, length, color, status);
try {
// 比较Hash如若存在重复的请求或响应则不放入消息内容里
byte[] reqByteA = messageInfo.getRequest();
byte[] resByteA = messageInfo.getResponse();
boolean isDuplicate = false;
if (log.size() > 0) {
for (LogEntry entry : log) {
IHttpRequestResponsePersisted reqResMessage = entry.getRequestResponse();
byte[] reqByteB = reqResMessage.getRequest();
byte[] resByteB = reqResMessage.getResponse();
try {
// 采用匹配数据结果比对
if (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA))) {
isDuplicate = true;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (!isDuplicate) {
log.add(logEntry); log.add(logEntry);
} }
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Map<String, Map<String, Object>> getCacheData(byte[] content)
throws NoSuchAlgorithmException {
String hashIndex = HashCalculator.calculateHash(content);
return GlobalCachePool.getFromCache(hashIndex);
}
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;
}
for (String key : map1.keySet()) {
if (!map2.containsKey(key)) {
return false;
}
if (!areInnerMapsEqual(map1.get(key), map2.get(key))) {
return false;
}
}
return true;
}
private boolean areInnerMapsEqual(Map<String, Object> innerMap1, Map<String, Object> innerMap2) {
if (innerMap1.size() != innerMap2.size()) {
return false;
}
for (String key : innerMap1.keySet()) {
if (!innerMap2.containsKey(key)) {
return false;
}
Object value1 = innerMap1.get(key);
Object value2 = innerMap2.get(key);
// 如果值是Map则递归对比
if (value1 instanceof Map && value2 instanceof Map) {
if (!areInnerMapsEqual((Map<String, Object>) value1, (Map<String, Object>) value2)) {
return false;
}
} else if (!value1.equals(value2)) {
return false;
}
}
return true;
} }
public class Table extends JTable { public class Table extends JTable {
LogEntry logEntry; LogEntry logEntry;
private SwingWorker<Void, Void> currentWorker; private SwingWorker<Object, Void> currentWorker;
// 设置响应报文返回的最大长度为3MB
private final int MAX_LENGTH = 3145728;
private int lastSelectedIndex = -1;
public Table(TableModel tableModel) { public Table(TableModel tableModel) {
super(tableModel); super(tableModel);
@@ -314,37 +414,52 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
@Override @Override
public void changeSelection(int row, int col, boolean toggle, boolean extend) { public void changeSelection(int row, int col, boolean toggle, boolean extend) {
logEntry = filteredLog.get(convertRowIndexToModel(row)); super.changeSelection(row, col, toggle, extend);
int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex;
logEntry = filteredLog.get(selectedIndex);
requestViewer.setMessage("Loading...".getBytes(), true); requestViewer.setMessage("Loading...".getBytes(), true);
responseViewer.setMessage("Loading...".getBytes(), false); responseViewer.setMessage("Loading...".getBytes(), false);
currentlyDisplayedItem = logEntry.getRequestResponse(); currentlyDisplayedItem = logEntry.getRequestResponse();
// 取消之前的后台任务
if (currentWorker != null && !currentWorker.isDone()) { if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true); currentWorker.cancel(true);
} }
// 在后台线程中执行耗时操作
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { currentWorker = new SwingWorker<Object, Void>() {
@Override @Override
protected Void doInBackground() throws Exception { protected byte[][] doInBackground() throws Exception {
refreshMessage(); byte[] requestByte = logEntry.getRequestResponse().getRequest();
return null; byte[] responseByte = logEntry.getRequestResponse().getResponse();
}
}; if (responseByte.length > MAX_LENGTH) {
// 设置当前后台任务 String ellipsis = "\r\n......";
currentWorker = worker; responseByte = Arrays.copyOf(responseByte, MAX_LENGTH + ellipsis.length());
// 启动后台线程 byte[] ellipsisBytes = ellipsis.getBytes();
worker.execute(); System.arraycopy(ellipsisBytes, 0, responseByte, MAX_LENGTH, ellipsisBytes.length);
super.changeSelection(row, col, toggle, extend);
} }
private void refreshMessage() { return new byte[][] {requestByte, responseByte};
SwingUtilities.invokeLater(() -> { }
requestViewer.setMessage(logEntry.getRequestResponse().getRequest(), true);
responseViewer.setMessage(logEntry.getRequestResponse().getResponse(), false); @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();
}
} }
} }
} }

BIN
src/main/resources/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB