diff --git a/images/databoard.png b/images/databoard.png index 3357aab..a630c3a 100644 Binary files a/images/databoard.png and b/images/databoard.png differ diff --git a/src/main/java/burp/BurpExtender.java b/src/main/java/burp/BurpExtender.java index a9cdbf9..212dc04 100644 --- a/src/main/java/burp/BurpExtender.java +++ b/src/main/java/burp/BurpExtender.java @@ -5,6 +5,9 @@ import burp.core.processor.ColorProcessor; import burp.core.processor.MessageProcessor; import burp.ui.MainUI; import burp.ui.board.MessagePanel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.net.URL; import java.security.NoSuchAlgorithmException; import java.util.*; import javax.swing.*; @@ -35,7 +38,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito new ConfigLoader(); - String version = "2.5.3"; + String version = "2.5.4"; callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version)); // 定义输出 @@ -57,7 +60,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito } - private void initialize(){ + private void initialize() { messagePanel = new MessagePanel(callbacks, helpers); main = new MainUI(messagePanel); callbacks.customizeUiComponent(main); @@ -65,13 +68,49 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito } @Override - public String getTabCaption(){ + public String getTabCaption() { return "HaE"; } @Override 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; } /** diff --git a/src/main/java/burp/ui/MainUI.java b/src/main/java/burp/ui/MainUI.java index ac72d41..dae1ec1 100644 --- a/src/main/java/burp/ui/MainUI.java +++ b/src/main/java/burp/ui/MainUI.java @@ -20,11 +20,9 @@ import java.util.Map; */ public class MainUI extends JPanel { - private MessagePanel messagePanel; public MainUI(MessagePanel messagePanel) { - this.messagePanel = messagePanel; - databoardPanel = new Databoard(this.messagePanel); + databoardPanel = new Databoard(messagePanel); initComponents(); } @@ -143,7 +141,7 @@ public class MainUI extends JPanel { ConfigEntry.globalRules.get(i), ruleTabbedPane))); - ruleTabbedPane.addTab("...",new JLabel()); + ruleTabbedPane.addTab("...", new JLabel()); rulesPathTextField.setText(ConfigLoader.getRulesFilePath()); excludeSuffixTextField.setText(ConfigLoader.getExcludeSuffix()); diff --git a/src/main/java/burp/ui/board/Databoard.java b/src/main/java/burp/ui/board/Databoard.java index e4c789e..3aa442b 100644 --- a/src/main/java/burp/ui/board/Databoard.java +++ b/src/main/java/burp/ui/board/Databoard.java @@ -33,6 +33,8 @@ public class Databoard extends JPanel { private JSplitPane splitPane; private MessagePanel messagePanel; private Table table; + DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); + JComboBox hostComboBox = new JComboBox(comboBoxModel); public Databoard(MessagePanel messagePanel) { this.messagePanel = messagePanel; @@ -100,6 +102,23 @@ public class Databoard extends JPanel { GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(8, 0, 5, 5), 0, 0)); + hostTextField.setLayout(new BorderLayout()); + hostTextField.add(hostComboBox, BorderLayout.SOUTH); + hostComboBox.setMaximumRowCount(5); + hostComboBox.setPreferredSize(new Dimension(super.getPreferredSize().width, 0)); + + // 由于主题切换造成的UI组件重绘,而自定义组件没有正确地与之同步,因此需要事件监听来进行同步 + UIManager.addPropertyChangeListener(evt -> { + if ("lookAndFeel".equals(evt.getPropertyName())) { + SwingUtilities.invokeLater(() -> { + hostTextField.remove(hostComboBox); + hostTextField.add(hostComboBox, BorderLayout.SOUTH); + hostTextField.revalidate(); + hostTextField.repaint(); + }); + } + }); + setAutoMatch(); } @@ -111,16 +130,6 @@ public class Databoard extends JPanel { * 设置输入自动匹配 */ private void setAutoMatch() { - final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); - - final JComboBox hostComboBox = new JComboBox(comboBoxModel) { - @Override - public Dimension getPreferredSize() { - setMaximumRowCount(5); - return new Dimension(super.getPreferredSize().width, 0); - } - }; - isMatchHost = false; for (String host : getHostByList()) { @@ -208,9 +217,6 @@ public class Databoard extends JPanel { isMatchHost = false; } }); - - hostTextField.setLayout(new BorderLayout()); - hostTextField.add(hostComboBox, BorderLayout.SOUTH); } private void applyHostFilter(String filterText) { diff --git a/src/main/java/burp/ui/board/MessagePanel.java b/src/main/java/burp/ui/board/MessagePanel.java index cc944d4..6765cf7 100644 --- a/src/main/java/burp/ui/board/MessagePanel.java +++ b/src/main/java/burp/ui/board/MessagePanel.java @@ -9,6 +9,7 @@ import burp.IMessageEditor; import burp.IMessageEditorController; import burp.IRequestInfo; import burp.config.ConfigEntry; +import burp.core.GlobalCachePool; import burp.core.utils.HashCalculator; import burp.core.utils.StringHelper; @@ -19,6 +20,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.Objects; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; @@ -314,19 +317,23 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo try { // 比较Hash,如若存在重复的请求或响应,则不放入消息内容里 - String reqHashA = getMessageHash(true, messageInfo.getRequest()); - String resHashA = getMessageHash(false, messageInfo.getResponse()); + byte[] reqByteA = messageInfo.getRequest(); + byte[] resByteA = messageInfo.getResponse(); boolean isDuplicate = false; - for (LogEntry entry : log) { - IHttpRequestResponsePersisted reqResMessage = entry.getRequestResponse(); - String reqHashB = getMessageHash(true, reqResMessage.getRequest()); - String resHashB = getMessageHash(false, reqResMessage.getResponse()); - - if (reqHashB.equals(reqHashA) || resHashB.equals(resHashA)) { - if (entry.getComment().equals(comment)) { - isDuplicate = true; - break; + 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(); } } } @@ -341,19 +348,52 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo } - private String getMessageHash(boolean isRequest, byte[] content) + private Map> getCacheData(byte[] content) throws NoSuchAlgorithmException { - String hash = ""; + String hashIndex = HashCalculator.calculateHash(content); + return GlobalCachePool.getFromCache(hashIndex); + } - if (isRequest) { - hash = HashCalculator.calculateHash(content); - } else { - int responseBodyOffset = helpers.analyzeResponse(content).getBodyOffset(); - byte[] responseBody = Arrays.copyOfRange(content, responseBodyOffset, content.length); - hash = HashCalculator.calculateHash(responseBody); + private boolean areMapsEqual(Map> map1, Map> map2) { + if (map1.size() != map2.size()) { + return false; } - return hash; + 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 innerMap1, Map 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) value1, (Map) value2)) { + return false; + } + } else if (!value1.equals(value2)) { + return false; + } + } + + return true; } public class Table extends JTable { diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png new file mode 100644 index 0000000..3c25210 Binary files /dev/null and b/src/main/resources/logo.png differ diff --git a/src/main/resources/logo_black.png b/src/main/resources/logo_black.png new file mode 100644 index 0000000..682f637 Binary files /dev/null and b/src/main/resources/logo_black.png differ