├── .gitignore
├── .idea
└── .gitignore
├── LICENSE
├── README.md
├── README_CN.md
├── pom.xml
└── src
└── main
├── java
└── burp
│ ├── BurpExtender.java
│ ├── Gui.java
│ ├── bean
│ ├── Config.java
│ ├── Drop.java
│ └── Rule.java
│ ├── constant
│ ├── ConfigKey.java
│ ├── ProjectInfo.java
│ ├── RuleActionOption.java
│ ├── RuleTypeOption.java
│ └── WindowSize.java
│ ├── core
│ ├── AutoDecodeCore.java
│ ├── RuleCore.java
│ └── UserAgentCore.java
│ ├── listener
│ └── IHttpListenerImpl.java
│ ├── ui
│ ├── ContextMenuGui.java
│ ├── component
│ │ ├── BeanTable.java
│ │ ├── BurpPanel.java
│ │ └── PlaceholderTextField.java
│ ├── droppacket
│ │ ├── DropPacketPanel.java
│ │ └── DropPacketTable.java
│ ├── rule
│ │ ├── RulePanel.java
│ │ └── RuleTable.java
│ └── useragent
│ │ └── UserAgentPanel.java
│ └── util
│ ├── FileUtil.java
│ └── URLUtil.java
└── resources
├── info.properties
├── useragent-mobile.txt
└── useragent-pc.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | # MacOS
26 | .DS_Store
27 |
28 | .idea
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BurpHttpHelper
2 |
3 | [](https://GitHub.com/MaskCyberSecurityTeam/BurpHttpHelper/issues?q=is%3Aissue+is%3Aclosed)
4 | [](https://github.com/MaskCyberSecurityTeam/BurpHttpHelper/releases/latest)
5 | [](https://github.com/MaskCyberSecurityTeam/BurpHttpHelper/releases/latest)
6 |
7 | **BurpHttpHelper**是一款Burpsuite插件,主要用于简化和解决Burpsuite对Http的一些操作.
8 |
9 | 目前实现: `HttpHeader增删改` `HttpCookie增删改` `HttpBody替换` `随机UserAgent` `RepeaterResponse自动解码` `丢弃特定数据包`
10 |
11 | [README](README.md) | [文档](README_CN.md)
12 |
13 | # TODO
14 |
15 | * ~~HttpHeader增删改~~
16 | * ~~HttpCookie增删改~~
17 | * ~~HttpBody替换~~
18 | * ~~随机UserAgent~~
19 | * ~~RepeaterResponse自动解码unicode、url编码、html编码~~
20 | * ~~默认配置(常见信息泄露头)~~
21 | * ~~丢弃特定请求~~
22 | * ~~重写README说明~~
23 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | - [Http增删改](#http增删改)
2 | - [Header](#header)
3 | - [Cookie](#cookie)
4 | - [随机UserAgent](#随机useragent)
5 | - [RepeaterResponse自动解码](#repeaterresponse自动解码)
6 | - [丢弃数据包](#丢弃数据包)
7 |
8 | # Http增删改
9 |
10 | BurpHttpHelper中已经默认提供了一些常见的指纹头
11 |
12 | 
13 |
14 | ## Header
15 |
16 | 下图为服务器视角,接收到来自客户端的请求包,下边是一个demo例子,修改host头位demo.com。
17 |
18 |
19 |
20 | **添加完规则后,需要勾选启用。**
21 |
22 | 
23 |
24 | 
25 |
26 |
27 |
28 | ## Cookie
29 |
30 | 打开添加窗口
31 |
32 | 
33 |
34 | Type选择为Cookie即可
35 |
36 | 
37 |
38 | 
39 |
40 | 服务器接收到的数据包如下
41 |
42 |
43 |
44 | # 随机UserAgent
45 |
46 | 在UA面板中勾选: `电脑(PC)` `手机(Mobile)` 有些网站上电脑UA和手机UA呈现页面不同,这里根据实际情况自己选择。
47 |
48 | 
49 |
50 | 在规则面板中勾选随机UA头
51 |
52 | 
53 |
54 | 服务器接收到的数据包将为不同的UserAgent
55 |
56 |
57 |
58 | BurpHttpHelper中已经内置提供了一些UserAgent,可以自行通过添加UserAgent字符串进行扩展。
59 |
60 | 
61 |
62 | # RepeaterResponse自动解码
63 |
64 | 目前支持解码: `unicode` `url编码` `html编码`
65 |
66 | `BurpHttpHeader` -> `勾选RepeaterResponse自动解码(Repeater Response Auto Decode)`
67 |
68 | 
69 |
70 | 解码前
71 |
72 | 
73 |
74 | 解码后
75 |
76 | 
77 |
78 | # 丢弃数据包
79 |
80 | 有时候不想一些请求发送到目标服务器,可以通过丢弃数据包面板进行配置。
81 |
82 | `Extensions` -> `BurpHttpHelper` -> `丢弃该数据包`
83 |
84 | **注意: 丢弃数据包功能,不适用于Repeater模块功能。(因为Repeater本身是用于重放,如果进行过滤,这违反了它的设计。)**
85 |
86 | 
87 |
88 | 在丢弃数据包面板(DropPacketPanel)中可以查看该过滤的数据包信息(该模块还未完善,后续会进行完善。)
89 |
90 | 
91 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.mask-sec.burp
8 | BurpHttpHelper
9 | 1.3.1
10 |
11 |
12 | UTF-8
13 |
14 |
15 |
16 |
17 | net.portswigger.burp.extender
18 | burp-extender-api
19 | 2.3
20 |
21 |
22 | org.projectlombok
23 | lombok
24 | 1.18.24
25 | provided
26 |
27 |
28 | com.miglayout
29 | miglayout-swing
30 | ${miglayout-swing.version}
31 |
32 |
33 | cn.hutool
34 | hutool-all
35 | 5.8.11
36 |
37 |
38 |
39 |
40 |
41 | jitpack.io
42 | https://jitpack.io
43 |
44 |
45 |
46 |
47 | ${artifactId}-${project.version}-${compiler-jdk}
48 |
49 |
50 | org.apache.maven.plugins
51 | maven-clean-plugin
52 | 3.2.0
53 |
54 |
55 | auto-clean
56 | initialize
57 |
58 | clean
59 |
60 |
61 |
62 |
63 |
64 | org.apache.maven.plugins
65 | maven-compiler-plugin
66 | 3.10.1
67 |
68 | ${java.version}
69 | ${java.version}
70 | ${java.version}
71 | ${javac-path}
72 | ${project.build.sourceEncoding}
73 |
74 |
75 |
76 | org.apache.maven.plugins
77 | maven-assembly-plugin
78 | 3.4.2
79 |
80 |
81 | package
82 |
83 | single
84 |
85 |
86 |
87 |
88 |
89 | jar-with-dependencies
90 |
91 | false
92 |
93 |
94 |
95 |
96 |
97 | src/main/resources
98 | true
99 |
100 | info.properties
101 | useragent-*.txt
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | jdk11
110 |
111 | true
112 |
113 |
114 | 11
115 | 11.0
116 | JDK11
117 | /Library/Java/JavaVirtualMachines/jdk-11.0.16.1.jdk/Contents/Home/bin/javac
118 |
119 |
120 |
121 | jdk8
122 |
123 | 1.8
124 | 5.3
125 | JDK8
126 | /Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/bin/javac
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/src/main/java/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import burp.constant.ProjectInfo;
4 | import burp.listener.IHttpListenerImpl;
5 | import burp.ui.ContextMenuGui;
6 |
7 | import javax.swing.*;
8 | import java.awt.*;
9 |
10 | /**
11 | * Burpsuite插件主入口
12 | *
13 | * @author RichardTang
14 | */
15 | public class BurpExtender implements IBurpExtender, ITab {
16 |
17 | // 主要的ui
18 | private Gui gui;
19 |
20 | @Override
21 | public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
22 | gui = new Gui(iBurpExtenderCallbacks);
23 |
24 | // 注册插件的信息和在插件控制台打印插件信息
25 | iBurpExtenderCallbacks.setExtensionName(ProjectInfo.EXT_NAME);
26 | iBurpExtenderCallbacks.printOutput(ProjectInfo.TEAM);
27 | iBurpExtenderCallbacks.printOutput(ProjectInfo.AUTHOR);
28 | iBurpExtenderCallbacks.printOutput(ProjectInfo.VERSION_BANNER);
29 | iBurpExtenderCallbacks.printOutput(ProjectInfo.GITHUB);
30 |
31 | // 注册鼠标右键菜单栏、注册Http监听器
32 | iBurpExtenderCallbacks.registerContextMenuFactory(new ContextMenuGui(iBurpExtenderCallbacks, gui));
33 | iBurpExtenderCallbacks.registerHttpListener(new IHttpListenerImpl(iBurpExtenderCallbacks, gui));
34 |
35 | // 将主页面(tab)添加到Burp面板中
36 | SwingUtilities.invokeLater(() -> iBurpExtenderCallbacks.addSuiteTab(BurpExtender.this));
37 | }
38 |
39 | /**
40 | * 在Burp的tab上显示的标题
41 | *
42 | * @return 在Burp的tab上显示的标题
43 | */
44 | @Override
45 | public String getTabCaption() {
46 | return ProjectInfo.TAB_TITLE;
47 | }
48 |
49 | /**
50 | * 插件主页面
51 | *
52 | * @return 主页面
53 | */
54 | @Override
55 | public Component getUiComponent() {
56 | return gui;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/burp/Gui.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import burp.bean.Config;
4 | import burp.constant.ConfigKey;
5 | import burp.core.UserAgentCore;
6 | import burp.ui.rule.RulePanel;
7 | import burp.ui.droppacket.DropPacketPanel;
8 | import burp.ui.useragent.UserAgentPanel;
9 | import burp.util.FileUtil;
10 | import cn.hutool.json.JSONUtil;
11 | import lombok.Data;
12 |
13 | import javax.swing.*;
14 | import javax.swing.border.CompoundBorder;
15 | import javax.swing.border.EmptyBorder;
16 | import java.awt.*;
17 | import java.awt.event.MouseAdapter;
18 | import java.awt.event.MouseEvent;
19 | import java.io.FileWriter;
20 | import java.util.Arrays;
21 | import java.util.regex.Pattern;
22 |
23 | /**
24 | * 插件主页面
25 | *
26 | * @author RichardTang
27 | */
28 | @Data
29 | public class Gui extends JPanel {
30 |
31 | // 主页面中的三个面板
32 | private RulePanel rulePanel;
33 | private UserAgentPanel userAgentPanel;
34 | private DropPacketPanel dropPacketPanel;
35 | private JTabbedPane tabbedPane = new JTabbedPane();
36 |
37 | // 主页面处右上角的保存
38 | private JLabel saveConfigLabel = new JLabel("保存配置(SaveConfig)");
39 |
40 | private static final float X = 1.0f;
41 | private static final float Y = 0.0f;
42 |
43 | // 配置类
44 | private final Config config = new Config();
45 |
46 | // 配置文件路径
47 | private String configFilePath;
48 |
49 | public Gui(final IBurpExtenderCallbacks iBurpExtenderCallbacks) {
50 | setLayout(new OverlayLayout(this));
51 |
52 | configFilePath = FileUtil.getConfigFilePathByBurpExt(iBurpExtenderCallbacks);
53 |
54 | // 三个面板创建
55 | rulePanel = new RulePanel(iBurpExtenderCallbacks);
56 | userAgentPanel = new UserAgentPanel(iBurpExtenderCallbacks);
57 | dropPacketPanel = new DropPacketPanel(iBurpExtenderCallbacks);
58 |
59 | // 设置保存按钮的位置
60 | saveConfigLabel.setOpaque(false);
61 | saveConfigLabel.setAlignmentX(X);
62 | saveConfigLabel.setAlignmentY(Y);
63 | saveConfigLabel.setBorder(new CompoundBorder(saveConfigLabel.getBorder(), new EmptyBorder(3, 0, 0, 3)));
64 | saveConfigLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
65 | saveConfigLabel.addMouseListener(new MouseAdapter() {
66 | @Override
67 | public void mouseClicked(MouseEvent e) {
68 | saveConfig();
69 | }
70 | });
71 |
72 | // 添加三个面板到主页面
73 | tabbedPane.setAlignmentX(X);
74 | tabbedPane.setAlignmentY(Y);
75 | tabbedPane.addTab("规则面板(RulePanel)", rulePanel);
76 | tabbedPane.addTab("UA面板(UserAgentPanel)", userAgentPanel);
77 | tabbedPane.addTab("丢弃数据包面板(DropPacketPanel)", dropPacketPanel);
78 |
79 | add(saveConfigLabel);
80 | add(tabbedPane);
81 | }
82 |
83 | private void saveConfig() {
84 | // 主面板配置
85 | config.getRulePanelConfig().put(ConfigKey.RULE_TABLE_KEY, RulePanel.table.getTableData());
86 | config.getRulePanelConfig().put(ConfigKey.COMPARER_TOOL_KEY, rulePanel.getComparerToolCheckBox().isSelected());
87 | config.getRulePanelConfig().put(ConfigKey.DECODER_TOOL_KEY, rulePanel.getDecoderToolCheckBox().isSelected());
88 | config.getRulePanelConfig().put(ConfigKey.EXTENDER_TOOL_KEY, rulePanel.getExtenderToolCheckBox().isSelected());
89 | config.getRulePanelConfig().put(ConfigKey.INTRUDER_TOOL_KEY, rulePanel.getIntruderToolCheckBox().isSelected());
90 | config.getRulePanelConfig().put(ConfigKey.PROXY_TOOL_KEY, rulePanel.getProxyToolCheckBox().isSelected());
91 | config.getRulePanelConfig().put(ConfigKey.REPEATER_TOOL_KEY, rulePanel.getRepeaterToolCheckBox().isSelected());
92 | config.getRulePanelConfig().put(ConfigKey.SCANNER_TOOL_KEY, rulePanel.getScannerToolCheckBox().isSelected());
93 | config.getRulePanelConfig().put(ConfigKey.SEQUENCER_TOOL_KEY, rulePanel.getSequencerToolCheckBox().isSelected());
94 | config.getRulePanelConfig().put(ConfigKey.SPIDER_TOOL_KEY, rulePanel.getSpiderToolCheckBox().isSelected());
95 | config.getRulePanelConfig().put(ConfigKey.SUITE_TOOL_KEY, rulePanel.getSuiteToolCheckBox().isSelected());
96 | config.getRulePanelConfig().put(ConfigKey.TARGET_TOOL_KEY, rulePanel.getTargetToolCheckBox().isSelected());
97 | config.getRulePanelConfig().put(ConfigKey.RANDOM_UA_KEY, rulePanel.getRandomUserAgentCheckBox().isSelected());
98 | config.getRulePanelConfig().put(ConfigKey.RP_AD_KEY, rulePanel.getRepeaterResponseAutoDecodeCheckBox().isSelected());
99 |
100 |
101 | // UA面板配置
102 | String pcTextAreaText = userAgentPanel.getPcTextArea().getText();
103 | UserAgentCore.pcUserAgent.clear();
104 | UserAgentCore.pcUserAgent.addAll(Arrays.asList(pcTextAreaText.split("\n")));
105 | String mobileTextAreaText = userAgentPanel.getMobileTextArea().getText();
106 | UserAgentCore.mobileUserAgent.clear();
107 | UserAgentCore.mobileUserAgent.addAll(Arrays.asList(mobileTextAreaText.split("\n")));
108 | config.getUserAgentPanelConfig().put(ConfigKey.PC_UA_KEY, userAgentPanel.getPcCheckBox().isSelected());
109 | config.getUserAgentPanelConfig().put(ConfigKey.MOBILE_UA_KEY, userAgentPanel.getMobileCheckBox().isSelected());
110 | config.getUserAgentPanelConfig().put(ConfigKey.PC_UA_LIST_KEY, UserAgentCore.pcUserAgent);
111 | config.getUserAgentPanelConfig().put(ConfigKey.MOBILE_UA_LIST_KEY, UserAgentCore.mobileUserAgent);
112 |
113 | // 存储配置
114 | String configJson = JSONUtil.toJsonStr(config);
115 |
116 | // 配置写入文件中
117 | try (FileWriter fileWriter = new FileWriter(configFilePath)) {
118 | fileWriter.write(configJson);
119 | fileWriter.flush();
120 | JOptionPane.showMessageDialog(this, "保存成功(Save Success)!", "提示(Tip)", JOptionPane.INFORMATION_MESSAGE);
121 | } catch (Exception exception) {
122 | JOptionPane.showMessageDialog(this, "配置文件保存失败(Config File Save Fail!)", "提示(Tip)", JOptionPane.WARNING_MESSAGE);
123 | }
124 | }
125 |
126 | }
--------------------------------------------------------------------------------
/src/main/java/burp/bean/Config.java:
--------------------------------------------------------------------------------
1 | package burp.bean;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.HashMap;
6 |
7 | /**
8 | * 配置类
9 | *
10 | * @author RichardTang
11 | */
12 | @Data
13 | public class Config {
14 |
15 | // 规则面板配置
16 | private final HashMap rulePanelConfig = new HashMap<>();
17 |
18 | // UA面板配置
19 | private final HashMap userAgentPanelConfig = new HashMap<>();
20 |
21 | }
--------------------------------------------------------------------------------
/src/main/java/burp/bean/Drop.java:
--------------------------------------------------------------------------------
1 | package burp.bean;
2 |
3 | import lombok.Builder;
4 | import lombok.Data;
5 |
6 | /**
7 | * 丢弃数据包
8 | *
9 | * @author RichardTang
10 | */
11 | @Data
12 | @Builder
13 | public class Drop {
14 |
15 | // 编号
16 | private Integer id;
17 |
18 | // 匹配的地址ß
19 | private String url;
20 |
21 | // 备注
22 | private String comment;
23 | }
--------------------------------------------------------------------------------
/src/main/java/burp/bean/Rule.java:
--------------------------------------------------------------------------------
1 | package burp.bean;
2 |
3 | import burp.constant.RuleActionOption;
4 | import burp.constant.RuleTypeOption;
5 | import lombok.Builder;
6 | import lombok.Data;
7 |
8 | /**
9 | * 规则
10 | *
11 | * @author RichardTang
12 | */
13 | @Data
14 | @Builder
15 | public class Rule {
16 |
17 | // 编号
18 | private Integer id;
19 |
20 | // 地址
21 | private String url;
22 |
23 | // 键名
24 | private String keyName;
25 |
26 | // 键值
27 | private String keyValue;
28 |
29 | // 类型
30 | private RuleTypeOption type;
31 |
32 | // 动作
33 | private RuleActionOption action;
34 |
35 | // 状态
36 | private Boolean active;
37 |
38 | public void setKeyName(String keyName) {
39 | // 全部小写方式存储
40 | this.keyName = keyName.toLowerCase();
41 | }
42 | }
--------------------------------------------------------------------------------
/src/main/java/burp/constant/ConfigKey.java:
--------------------------------------------------------------------------------
1 | package burp.constant;
2 |
3 | /**
4 | * 配置项常量
5 | *
6 | * @author RichardTang
7 | */
8 | public class ConfigKey {
9 | public static final String RULE_TABLE_KEY = "RuleTableKey";
10 | public static final String COMPARER_TOOL_KEY = "ComparerToolKey";
11 | public static final String DECODER_TOOL_KEY = "DecoderToolKey";
12 | public static final String EXTENDER_TOOL_KEY = "ExtenderToolKey";
13 | public static final String INTRUDER_TOOL_KEY = "IntruderToolKey";
14 | public static final String PROXY_TOOL_KEY = "ProxyToolKey";
15 | public static final String REPEATER_TOOL_KEY = "RepeaterToolKey";
16 | public static final String SCANNER_TOOL_KEY = "ScannerToolKey";
17 | public static final String SEQUENCER_TOOL_KEY = "SequencerToolKey";
18 | public static final String SPIDER_TOOL_KEY = "SpiderToolKey";
19 | public static final String SUITE_TOOL_KEY = "SuiteToolKey";
20 | public static final String TARGET_TOOL_KEY = "TargetToolKey";
21 | public static final String RANDOM_UA_KEY = "RandomUserAgentKey";
22 | public static final String RP_AD_KEY = "RepeaterResponseAutoDecodeKey";
23 |
24 | public static final String PC_UA_KEY = "PCUserAgentKey";
25 | public static final String MOBILE_UA_KEY = "PCMobileAgentKey";
26 | public static final String PC_UA_LIST_KEY = "PCUserAgentListKey";
27 | public static final String MOBILE_UA_LIST_KEY = "MobileUserAgentListKey";
28 |
29 | public static final String CONFIG_FILE_NAME = "config.json";
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/burp/constant/ProjectInfo.java:
--------------------------------------------------------------------------------
1 | package burp.constant;
2 |
3 | import java.io.IOException;
4 | import java.util.Properties;
5 |
6 | /**
7 | * 项目信息
8 | *
9 | * @author RichardTang
10 | */
11 | public class ProjectInfo {
12 |
13 | // 当前版本号
14 | public static String VERSION;
15 |
16 | static {
17 | try {
18 | // 启动时通过读取配置文件获取版本号
19 | Properties properties = new Properties();
20 | properties.load(ProjectInfo.class.getClassLoader().getResourceAsStream("info.properties"));
21 | VERSION = properties.getProperty("version");
22 | } catch (IOException e) {
23 | VERSION = "None";
24 | }
25 | }
26 |
27 | // Burpsuite上的Tab页显示的标题
28 | public static final String TAB_TITLE = "BurpHttpHelper";
29 |
30 | // 信息
31 | public static final String EXT_NAME = String.format("BurpHttpHelper - %s - Http Protocol Helper", VERSION);
32 | public static final String VERSION_BANNER = String.format("Version: %s", VERSION);
33 | public static final String TEAM = "Team: MaskSec";
34 | public static final String AUTHOR = "Author: RichardTang";
35 | public static final String GITHUB = "GitHub: https://github.com/MaskCyberSecurityTeam/BurpHttpHelper";
36 | }
--------------------------------------------------------------------------------
/src/main/java/burp/constant/RuleActionOption.java:
--------------------------------------------------------------------------------
1 | package burp.constant;
2 |
3 | /**
4 | * 主面板规则 - 动作常量
5 | *
6 | * @author RichardTang
7 | */
8 | public enum RuleActionOption {
9 |
10 | ADD("add"), MODIFY("modify"), REMOVE("remove");
11 |
12 | private String text;
13 |
14 | RuleActionOption(String text) {
15 | this.text = text;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/burp/constant/RuleTypeOption.java:
--------------------------------------------------------------------------------
1 | package burp.constant;
2 |
3 | /**
4 | * 规则类型
5 | * 用于描述规则适用的操作是HttpHeader还是HttpCookie
6 | *
7 | * @author RichardTang
8 | */
9 | public enum RuleTypeOption {
10 |
11 | HEADER("header"), COOKIE("cookie"), BODY("body");
12 |
13 | private String text;
14 |
15 | RuleTypeOption(String text) {
16 | this.text = text;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/main/java/burp/constant/WindowSize.java:
--------------------------------------------------------------------------------
1 | package burp.constant;
2 |
3 | import java.awt.*;
4 |
5 | /**
6 | * 窗口大小常量
7 | *
8 | * @author RichardTang
9 | */
10 | public class WindowSize {
11 |
12 | public static final Dimension RULE_FORM_WINDOW = new Dimension(400, 250);
13 |
14 | public static final Dimension LOCAL_MAIN_WINDOW = new Dimension(1000, 500);
15 | }
--------------------------------------------------------------------------------
/src/main/java/burp/core/AutoDecodeCore.java:
--------------------------------------------------------------------------------
1 | package burp.core;
2 |
3 | import cn.hutool.core.text.UnicodeUtil;
4 | import cn.hutool.core.util.ReUtil;
5 | import cn.hutool.core.util.URLUtil;
6 | import cn.hutool.http.HtmlUtil;
7 |
8 | /**
9 | * 解码核心处理类
10 | *
11 | * @author RichardTang
12 | */
13 | public class AutoDecodeCore {
14 |
15 | public static final String HTML_CHR_REGEX = "(.*);";
16 |
17 | public static final String URL_CHR_REGEX = "%[\\w+]{2}";
18 |
19 | public static final String UNICODE_CHR_REGEX = "\\\\u[\\w+]{4}";
20 |
21 | /**
22 | * 解码html、url、unicode。
23 | *
24 | * @param body 需要解码的body
25 | * @return 解码后的body
26 | */
27 | public static String assembly(final String body) {
28 | String newBody = body;
29 | if (ReUtil.contains(UNICODE_CHR_REGEX, newBody)) {
30 | newBody = UnicodeUtil.toString(newBody);
31 | }
32 | if (ReUtil.contains(HTML_CHR_REGEX, newBody)) {
33 | newBody = HtmlUtil.unescape(newBody);
34 | }
35 | if (ReUtil.contains(URL_CHR_REGEX, newBody)) {
36 | newBody = URLUtil.decode(newBody);
37 | }
38 | return newBody;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/main/java/burp/core/RuleCore.java:
--------------------------------------------------------------------------------
1 | package burp.core;
2 |
3 | import burp.bean.Rule;
4 | import burp.constant.RuleTypeOption;
5 |
6 | import java.net.URL;
7 | import java.util.List;
8 | import java.util.Vector;
9 | import java.util.regex.Pattern;
10 |
11 | /**
12 | * 规则匹配核心处理类
13 | *
14 | * @author RichardTang
15 | */
16 | public class RuleCore {
17 |
18 | public static final String ANY = "*";
19 | public static final String ANY_REGEXP = "(.*)";
20 |
21 | // 标记Http请求头中的cookie
22 | public static final String COOKIE_HEADER_FLAG = "cookie: ";
23 |
24 | // 存储规则的集合
25 | public static final Vector activeRuleData = new Vector<>();
26 |
27 | public static String assembly(final List metaDataHeaders, final URL url, byte[] body) {
28 | // 遍历规则
29 | for (Rule rule : activeRuleData) {
30 |
31 | // 判断当前url是否匹配上现有的规则
32 | boolean flag = Pattern.compile(rule.getUrl().replace(ANY, ANY_REGEXP)).matcher(url.toExternalForm()).find();
33 | if (!flag) {
34 | break;
35 | }
36 |
37 | // 区分规则要操作的类型
38 | if (rule.getType() == RuleTypeOption.HEADER) {
39 | httpHeaderOptionAssembly(rule, metaDataHeaders);
40 | } else if (rule.getType() == RuleTypeOption.BODY) {
41 | // 直接替换字符
42 | return new String(body).replaceAll(rule.getKeyName(), rule.getKeyValue());
43 | } else {
44 | // 遍历所有的头信息
45 | for (int i = 0; i < metaDataHeaders.size(); i++) {
46 | // 找到cookie的头信息
47 | String httpHeader = metaDataHeaders.get(i);
48 | if (!httpHeader.toLowerCase().startsWith(COOKIE_HEADER_FLAG)) {
49 | continue;
50 | }
51 | // 处理cookie头
52 | String newCookie = httpCookieOptionAssembly(rule, httpHeader);
53 | // 设置新值
54 | metaDataHeaders.set(i, newCookie);
55 | }
56 | }
57 | }
58 | return null;
59 | }
60 |
61 | /**
62 | * 根据action分发处理HttpCookie
63 | *
64 | * @param rule 规则
65 | * @param cookies 本次要处理的cookie字符串
66 | * @return 处理后的cookie
67 | */
68 | public static String httpCookieOptionAssembly(Rule rule, String cookies) {
69 | switch (rule.getAction()) {
70 | case ADD:
71 | cookies = addHttpCookieOptionAssembly(rule, cookies);
72 | break;
73 | case MODIFY:
74 | cookies = modifyHttpCookieOptionAssembly(rule, cookies);
75 | break;
76 | case REMOVE:
77 | cookies = removeHttpCookieOptionAssembly(rule, cookies);
78 | break;
79 | }
80 | return cookies;
81 | }
82 |
83 | /**
84 | * 增加cookie键值对
85 | *
86 | * @param rule 规则
87 | * @param cookies 原cookie键值对信息
88 | * @return 处理过后的cookie字符串
89 | */
90 | public static String addHttpCookieOptionAssembly(Rule rule, String cookies) {
91 | return String.format("%s %s=%s;", cookies, rule.getKeyName(), rule.getKeyValue());
92 | }
93 |
94 | /**
95 | * 修改cookie键值对
96 | *
97 | * @param rule 规则
98 | * @param cookies 原cookie键值对信息
99 | * @return 处理过后的cookie字符串
100 | */
101 | public static String modifyHttpCookieOptionAssembly(Rule rule, String cookies) {
102 | return Pattern.compile("(?<=" + rule.getKeyName() + "=).+?(?=;)").matcher(cookies).replaceAll(rule.getKeyValue());
103 | }
104 |
105 | /**
106 | * 删除cookie键值对
107 | *
108 | * @param rule 规则
109 | * @param cookies 原cookie键值对信息
110 | * @return 处理过后的cookie字符串
111 | */
112 | public static String removeHttpCookieOptionAssembly(Rule rule, String cookies) {
113 | // TODO cookie必须是标准的 SESSION=123; 如果缺少;号,则匹配不上。
114 | return Pattern.compile(rule.getKeyName() + "=(.+?;)").matcher(cookies).replaceAll("");
115 | }
116 |
117 | /**
118 | * 根据action分发处理HttpHeader
119 | *
120 | * @param rule 规则
121 | * @param metaDataHeaders HttpHeader头集合
122 | */
123 | public static void httpHeaderOptionAssembly(Rule rule, final List metaDataHeaders) {
124 | // 根据规则的动作,对HttpHeader头中的信息进行增删改操作。
125 | String keyName = rule.getKeyName();
126 | switch (rule.getAction()) {
127 | case ADD:
128 | addHttpHeaderOptionAssembly(metaDataHeaders, keyName, rule.getKeyValue());
129 | break;
130 | case MODIFY:
131 | modifyHttpHeaderOptionAssembly(metaDataHeaders, keyName, rule.getKeyValue());
132 | break;
133 | case REMOVE:
134 | removeHttpHeaderOptionAssembly(metaDataHeaders, keyName);
135 | break;
136 | }
137 | }
138 |
139 | /**
140 | * 新增HttpHeader
141 | *
142 | * @param metaDataHeaders 原HttpHeader数据集合
143 | * @param headerName 新增的HttpHeader
144 | * @param headerValue 新增的HttpHeaderValue
145 | */
146 | public static void addHttpHeaderOptionAssembly(final List metaDataHeaders, final String headerName, final String headerValue) {
147 | metaDataHeaders.add(String.format("%s: %s", headerName, headerValue));
148 | }
149 |
150 | /**
151 | * 修改HttpHeader
152 | *
153 | * @param metaDataHeaders 原HttpHeader数据集合
154 | * @param headerName 修改的HttpHeader
155 | * @param headerValue 修改的HttpHeaderValue
156 | */
157 | public static void modifyHttpHeaderOptionAssembly(final List metaDataHeaders, final String headerName, final String headerValue) {
158 | int index = 0;
159 | for (String header : metaDataHeaders) {
160 | if (header.toLowerCase().contains(headerName)) {
161 | metaDataHeaders.set(index, String.format("%s: %s", headerName, headerValue));
162 | break;
163 | }
164 | index++;
165 | }
166 | }
167 |
168 | /**
169 | * 删除HttpHeader
170 | *
171 | * @param metaDataHeaders 原HttpHeader数据集合
172 | * @param headerName 需要删除的HttpHeaderName
173 | */
174 | public static void removeHttpHeaderOptionAssembly(final List metaDataHeaders, final String headerName) {
175 | metaDataHeaders.removeIf(header -> header.equalsIgnoreCase(headerName));
176 | }
177 | }
--------------------------------------------------------------------------------
/src/main/java/burp/core/UserAgentCore.java:
--------------------------------------------------------------------------------
1 | package burp.core;
2 |
3 | import burp.Gui;
4 |
5 | import java.util.*;
6 |
7 | /**
8 | * UserAgent核心处理类
9 | *
10 | * @author RichardTang
11 | */
12 | public class UserAgentCore {
13 |
14 | // 存储PC端UserAgent
15 | public static final LinkedList pcUserAgent = new LinkedList<>();
16 |
17 | // 存储Mobile端UserAgent
18 | public static final LinkedList mobileUserAgent = new LinkedList<>();
19 |
20 | /**
21 | * 处理类
22 | *
23 | * @param headers 原HttpHeader
24 | * @param gui 主页面
25 | */
26 | public static void assembly(final List headers, Gui gui) {
27 | String userAgent = null;
28 | // PC和Mobile选项都勾选
29 | if (gui.getUserAgentPanel().getPcCheckBox().isSelected() && gui.getUserAgentPanel().getMobileCheckBox().isSelected()) {
30 | userAgent = getRandomUserAgent();
31 | }
32 | // 只勾选PC
33 | else if (gui.getUserAgentPanel().getPcCheckBox().isSelected()) {
34 | userAgent = getRandomPcUserAgent();
35 | }
36 | // 只勾选Mobile
37 | else if (gui.getUserAgentPanel().getMobileCheckBox().isSelected()) {
38 | userAgent = getRandomMobileUserAgent();
39 | }
40 |
41 | // 以上都没有勾选,那么就不进行修改,userAgent有值就代表修改了。
42 | if (userAgent != null) {
43 | RuleCore.modifyHttpHeaderOptionAssembly(headers, "User-Agent", userAgent);
44 | }
45 | }
46 |
47 | /**
48 | * 随机生成PC和Mobile的UserAgent
49 | *
50 | * @return 随机生成的UserAgent
51 | */
52 | public static String getRandomUserAgent() {
53 | if (mobileUserAgent.size() == 0 || pcUserAgent.size() == 0) {
54 | return null;
55 | }
56 | int i = (int) (Math.random() * 2);
57 | if (i == 0) {
58 | return getRandomPcUserAgent();
59 | } else {
60 | return getRandomMobileUserAgent();
61 | }
62 | }
63 |
64 | /**
65 | * 随机生成PC的UserAgent
66 | *
67 | * @return 随机生成的PC UserAgent
68 | */
69 | public static String getRandomPcUserAgent() {
70 | if (pcUserAgent.size() == 0) {
71 | return null;
72 | }
73 | int i = (int) (Math.random() * pcUserAgent.size());
74 | return pcUserAgent.get(i);
75 | }
76 |
77 | /**
78 | * 随机生成Mobile的UserAgent
79 | *
80 | * @return 随机生成的Mobile UserAgent
81 | */
82 | public static String getRandomMobileUserAgent() {
83 | if (mobileUserAgent.size() == 0) {
84 | return null;
85 | }
86 | int i = (int) (Math.random() * mobileUserAgent.size());
87 | return mobileUserAgent.get(i);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/burp/listener/IHttpListenerImpl.java:
--------------------------------------------------------------------------------
1 | package burp.listener;
2 |
3 | import burp.*;
4 | import burp.core.AutoDecodeCore;
5 | import burp.core.RuleCore;
6 | import burp.core.UserAgentCore;
7 | import burp.Gui;
8 | import cn.hutool.core.util.StrUtil;
9 |
10 | import java.util.*;
11 |
12 | /**
13 | * Http协议监听器,所有Http请求都会经过该类进行处理。
14 | *
15 | * @author RichardTang
16 | */
17 | public class IHttpListenerImpl implements IHttpListener {
18 |
19 | private Gui gui;
20 |
21 | private IExtensionHelpers helpers;
22 |
23 | private IBurpExtenderCallbacks iBurpExtenderCallbacks;
24 |
25 | public IHttpListenerImpl(IBurpExtenderCallbacks iBurpExtenderCallbacks, Gui gui) {
26 | this.helpers = iBurpExtenderCallbacks.getHelpers();
27 | this.iBurpExtenderCallbacks = iBurpExtenderCallbacks;
28 | this.gui = gui;
29 | }
30 |
31 | @Override
32 | public void processHttpMessage(int msgType, boolean messageIsRequest, IHttpRequestResponse iHttpRequestResponse) {
33 |
34 | // request
35 | if (messageIsRequest) {
36 | byte[] requestByte = iHttpRequestResponse.getRequest();
37 | IRequestInfo iRequestInfo = helpers.analyzeRequest(iHttpRequestResponse.getHttpService(), requestByte);
38 |
39 | // 判断该数据包是否需要丢弃
40 | boolean flag = gui.getDropPacketPanel().filterUrlOnData(iRequestInfo.getUrl());
41 | if (flag && msgType != IBurpExtenderCallbacks.TOOL_REPEATER) {
42 | // 丢弃该请求
43 | iHttpRequestResponse.setRequest(new byte[0]);
44 | return;
45 | }
46 |
47 | // 匹配规则面板中监听的模块
48 | if (gui.getRulePanel().validListenerEnabled(msgType)) {
49 | byte[] body = Arrays.copyOfRange(requestByte, iRequestInfo.getBodyOffset(), requestByte.length);
50 | List headers = iRequestInfo.getHeaders();
51 |
52 | // 处理UserAgent
53 | if (gui.getRulePanel().getRandomUserAgentCheckBox().isSelected()) {
54 | UserAgentCore.assembly(headers, gui);
55 | }
56 |
57 | // 处理Http协议规则
58 | String newBody = RuleCore.assembly(headers, iRequestInfo.getUrl(), body);
59 | if(newBody != null) {
60 | // 处理HttpBody
61 | body = newBody.getBytes();
62 | }
63 |
64 | // 构造新请求
65 | byte[] newReq = helpers.buildHttpMessage(headers, body);
66 | iHttpRequestResponse.setRequest(newReq);
67 | }
68 | }
69 | // response
70 | else if (gui.getRulePanel().getRepeaterResponseAutoDecodeCheckBox().isSelected() && msgType == IBurpExtenderCallbacks.TOOL_REPEATER) {
71 | byte[] responseByte = iHttpRequestResponse.getResponse();
72 | IResponseInfo iResponseInfo = helpers.analyzeResponse(responseByte);
73 |
74 | // 获取body
75 | byte[] body = Arrays.copyOfRange(responseByte, iResponseInfo.getBodyOffset(), responseByte.length);
76 | // 进行解码,获取新body
77 | byte[] newBody = AutoDecodeCore.assembly(new String(body)).getBytes();
78 |
79 | // 旧body和新body,代表没有进行解码操作。
80 | if (newBody.length != body.length) {
81 | // 进行了解码操作,重新设置响应给客户端的response。
82 | iHttpRequestResponse.setResponse(helpers.buildHttpMessage(iResponseInfo.getHeaders(), newBody));
83 | }
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/ContextMenuGui.java:
--------------------------------------------------------------------------------
1 | package burp.ui;
2 |
3 | import burp.*;
4 | import burp.bean.Drop;
5 | import burp.constant.ProjectInfo;
6 | import burp.util.URLUtil;
7 |
8 | import javax.swing.*;
9 | import java.net.URL;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * Burp中邮件的菜单选项
15 | *
16 | * @author RichardTang
17 | */
18 | public class ContextMenuGui implements IContextMenuFactory {
19 |
20 | private Gui gui;
21 |
22 | private IBurpExtenderCallbacks iBurpExtenderCallbacks;
23 |
24 | public ContextMenuGui(final IBurpExtenderCallbacks iBurpExtenderCallbacks, final Gui gui) {
25 | this.gui = gui;
26 | this.iBurpExtenderCallbacks = iBurpExtenderCallbacks;
27 | }
28 |
29 | @Override
30 | public List createMenuItems(IContextMenuInvocation invocation) {
31 | ArrayList menuItems = new ArrayList();
32 | JMenu burpHttpHelperMenu = new JMenu(ProjectInfo.TAB_TITLE);
33 | JMenuItem dropPacketMenuItem = new JMenuItem("丢弃该数据包(DropThePacket)");
34 | // 当用户点击右键时,将请求的信息发送到丢弃数据包的面板中
35 | dropPacketMenuItem.addActionListener(e -> {
36 | IHttpRequestResponse iReqResp = invocation.getSelectedMessages()[0];
37 | IRequestInfo iRequestInfo = iBurpExtenderCallbacks.getHelpers().analyzeRequest(iReqResp.getHttpService(), iReqResp.getRequest());
38 | String URIPath = URLUtil.getURIPath(iRequestInfo.getUrl().toExternalForm());
39 | Drop drop = Drop.builder().id(gui.getDropPacketPanel().getTable().getDataSize()).url(URIPath).comment("").build();
40 | gui.getDropPacketPanel().getTable().addRow(drop);
41 | });
42 | burpHttpHelperMenu.add(dropPacketMenuItem);
43 | menuItems.add(burpHttpHelperMenu);
44 | return menuItems;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/component/BeanTable.java:
--------------------------------------------------------------------------------
1 | package burp.ui.component;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 | import javax.swing.table.DefaultTableModel;
6 | import java.util.Vector;
7 |
8 | /**
9 | * JavaBean方式的表格
10 | *
11 | * @param JavaBean
12 | * @author RichardTang
13 | */
14 | public abstract class BeanTable extends JTable {
15 |
16 | // 存储表头的集合
17 | protected Vector columnName;
18 |
19 | // 存储表格数据的集合
20 | protected final Vector data = new Vector<>();
21 |
22 | protected DefaultTableModel model = new DefaultTableModel() {
23 |
24 | @Override
25 | public int getRowCount() {
26 | return data.size();
27 | }
28 |
29 | @Override
30 | public String getColumnName(int column) {
31 | return columnName.get(column);
32 | }
33 |
34 | @Override
35 | public int getColumnCount() {
36 | return columnName.size();
37 | }
38 |
39 | @Override
40 | public Object getValueAt(int rowIndex, int columnIndex) {
41 | return BeanTable.this.initializeGetValueAt(rowIndex, columnIndex);
42 | }
43 |
44 | @Override
45 | public void setValueAt(Object aValue, int row, int column) {
46 | BeanTable.this.initializeSetValueAt(aValue, row, column);
47 | }
48 |
49 | @Override
50 | public Class> getColumnClass(int columnIndex) {
51 | // 最后一个column,在当前项目中是固定的组件为CheckBox,所以需要返回为Boolean.class。
52 | if (columnIndex == 6) {
53 | return Boolean.class;
54 | } else {
55 | return super.getColumnClass(columnIndex);
56 | }
57 | }
58 | };
59 |
60 | protected DefaultTableCellRenderer tableCellRenderer = new DefaultTableCellRenderer();
61 |
62 | public BeanTable() {
63 | this.columnName = initializeColumnName();
64 | this.tableCellRenderer.setHorizontalAlignment(JLabel.CENTER);
65 |
66 | setModel(model);
67 | setShowGrid(true);
68 | setRowHeight(30);
69 | setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
70 | setDefaultRenderer(Object.class, tableCellRenderer);
71 | getTableHeader().setDefaultRenderer(tableCellRenderer);
72 | }
73 |
74 | /**
75 | * 自定义根据行号列号获取数据的逻辑
76 | *
77 | * @param rowIndex 行号
78 | * @param columnIndex 列号
79 | * @return 数据
80 | */
81 | public abstract Object initializeGetValueAt(int rowIndex, int columnIndex);
82 |
83 | /**
84 | * 自定义初始化表头
85 | *
86 | * @return 表头集合
87 | */
88 | public abstract Vector initializeColumnName();
89 |
90 | /**
91 | * 自定义根据行号列号存储数据
92 | *
93 | * @param aValue 数据
94 | * @param rowIndex 行号
95 | * @param columnIndex 列号
96 | */
97 | public void initializeSetValueAt(Object aValue, int rowIndex, int columnIndex) {
98 | T valueAt = (T) getValueAt(rowIndex, columnIndex);
99 | data.setElementAt(valueAt, columnIndex);
100 | model.fireTableCellUpdated(rowIndex, columnIndex);
101 | }
102 |
103 | /**
104 | * 添加一行数据
105 | *
106 | * @param item 需要添加的JavaBean实例
107 | */
108 | public void addRow(T item) {
109 | data.add(item);
110 | updateUI();
111 | }
112 |
113 | /**
114 | * 添加多行数据
115 | *
116 | * @param items 需要添加的JavaBean实例集合
117 | */
118 | public void addRows(Vector items) {
119 | data.addAll(items);
120 | updateUI();
121 | }
122 |
123 | /**
124 | * 根据行号删除数据
125 | *
126 | * @param rowIndex 行号
127 | */
128 | public void removeRow(int rowIndex) {
129 | data.remove(rowIndex);
130 | updateUI();
131 | }
132 |
133 | /**
134 | * 删除全部数据
135 | */
136 | public void removeAll() {
137 | int dataSize = data.size();
138 | if (dataSize > 0) {
139 | data.clear();
140 | updateUI();
141 | }
142 | }
143 |
144 | /**
145 | * 获取选中的行数据
146 | *
147 | * @return 选中的行数据
148 | */
149 | public T getSelectedItem() {
150 | int index = getSelectedRow();
151 | if (index != -1) {
152 | return data.get(index);
153 | } else {
154 | return null;
155 | }
156 | }
157 |
158 | /**
159 | * 删除选中的行数据
160 | */
161 | public void removeSelectedItem() {
162 | int index = getSelectedRow();
163 | if (index != -1) {
164 | data.remove(index);
165 | updateUI();
166 | }
167 | }
168 |
169 | /**
170 | * 获取存储表格数据的集合
171 | *
172 | * @return 数据集合
173 | */
174 | public Vector getTableData() {
175 | return data;
176 | }
177 |
178 | /**
179 | * 获取表格行数量
180 | *
181 | * @return 表格行数量
182 | */
183 | public int getDataSize() {
184 | return data.size();
185 | }
186 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/component/BurpPanel.java:
--------------------------------------------------------------------------------
1 | package burp.ui.component;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.constant.ConfigKey;
5 | import burp.util.FileUtil;
6 | import cn.hutool.json.JSONObject;
7 | import cn.hutool.json.JSONUtil;
8 |
9 | import javax.swing.*;
10 | import java.io.File;
11 | import java.nio.charset.StandardCharsets;
12 |
13 | /**
14 | * 通用Panel
15 | *
16 | * @author RichardTang
17 | */
18 | public abstract class BurpPanel extends JPanel {
19 |
20 | // 配置文件绝对路径
21 | private String configFilePath;
22 |
23 | protected IBurpExtenderCallbacks iBurpExtenderCallbacks;
24 |
25 | /**
26 | * 空参构造,用于lombok生成代码,实际的BurpPanel子类,应继承有参数的那个构造函数。
27 | */
28 | protected BurpPanel() {
29 |
30 | }
31 |
32 | public BurpPanel(final IBurpExtenderCallbacks iBurpExtenderCallbacks) {
33 | this.iBurpExtenderCallbacks = iBurpExtenderCallbacks;
34 | this.configFilePath = FileUtil.getConfigFilePathByBurpExt(iBurpExtenderCallbacks);
35 |
36 | // 初始化动作
37 | initComponent();
38 | initEvent();
39 | loadConfigFile();
40 | }
41 |
42 | /**
43 | * 初始化组件
44 | */
45 | public abstract void initComponent();
46 |
47 | /**
48 | * 初始化事件
49 | */
50 | public abstract void initEvent();
51 |
52 | /**
53 | * 初始化配置
54 | *
55 | * @param rootJSONObject 当前这个Panel在配置文件中对应的JSONObject部分配置
56 | */
57 | public abstract void initConfig(JSONObject rootJSONObject);
58 |
59 | /**
60 | * 在配置文件中的根key
61 | *
62 | * @return 根key值
63 | */
64 | public abstract String rootJSONObjectKey();
65 |
66 | /**
67 | * 加载配置文件,会根据rootJSONObjectKey来找到属于当前这个Panel的JSONObject。
68 | */
69 | public void loadConfigFile() {
70 | File configFile = new File(configFilePath);
71 | if (configFile.exists()) {
72 | try {
73 | String rootKey = rootJSONObjectKey();
74 | // 如果rootKey为null,则代表当前这个Panel在配置文件中没有配置。
75 | if (rootKey == null) {
76 | return;
77 | } else {
78 | // 存在配置的情况下,则进行读取配置。
79 | JSONObject rootJSONObject = JSONUtil.readJSONObject(configFile, StandardCharsets.UTF_8).getJSONObject(rootKey);
80 | initConfig(rootJSONObject);
81 | }
82 | } catch (Exception e) {
83 | iBurpExtenderCallbacks.printOutput("配置文件读取失败(Config File Read Fail!)");
84 | iBurpExtenderCallbacks.printOutput(e.getMessage());
85 | }
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/component/PlaceholderTextField.java:
--------------------------------------------------------------------------------
1 | package burp.ui.component;
2 |
3 | import javax.swing.*;
4 | import javax.swing.text.Document;
5 | import java.awt.*;
6 |
7 | /**
8 | * 带有Placeholder效果的TextField - 带有文本提示的输入框
9 | * 参考来源: http://www.uwenku.com/question/p-pyhaxkpo-bez.html
10 | */
11 | @SuppressWarnings("all")
12 | public class PlaceholderTextField extends JTextField {
13 |
14 | private String placeholder;
15 |
16 | public PlaceholderTextField() {
17 | }
18 |
19 | public PlaceholderTextField(final Document pDoc, final String pText, final int pColumns) {
20 | super(pDoc, pText, pColumns);
21 | }
22 |
23 | public PlaceholderTextField(final int pColumns) {
24 | super(pColumns);
25 | }
26 |
27 | public PlaceholderTextField(final String pText) {
28 | super(pText);
29 | }
30 |
31 | public PlaceholderTextField(final String pText, final int pColumns) {
32 | super(pText, pColumns);
33 | }
34 |
35 | public String getPlaceholder() {
36 | return placeholder;
37 | }
38 |
39 | @Override
40 | protected void paintComponent(final Graphics pG) {
41 | super.paintComponent(pG);
42 |
43 | if (placeholder.length() == 0 || getText().length() > 0) {
44 | return;
45 | }
46 |
47 | final Graphics2D g = (Graphics2D) pG;
48 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
49 | g.setColor(getDisabledTextColor());
50 | g.drawString(placeholder, getInsets().left, pG.getFontMetrics().getMaxAscent() + getInsets().top);
51 | }
52 |
53 | public void setPlaceholder(final String s) {
54 | placeholder = s;
55 | }
56 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/droppacket/DropPacketPanel.java:
--------------------------------------------------------------------------------
1 | package burp.ui.droppacket;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.ui.component.BurpPanel;
5 | import cn.hutool.json.JSONObject;
6 |
7 | import javax.swing.*;
8 | import javax.swing.border.TitledBorder;
9 | import java.awt.*;
10 | import java.net.URL;
11 |
12 | /**
13 | * 丢弃数据包面板
14 | *
15 | * @author RichardTang
16 | */
17 | public class DropPacketPanel extends BurpPanel {
18 |
19 | private JPanel configPanel;
20 |
21 | private DropPacketTable table;
22 |
23 | private JButton removeButton;
24 |
25 | public DropPacketPanel(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
26 | super(iBurpExtenderCallbacks);
27 |
28 | setLayout(new BorderLayout());
29 | add(configPanel, BorderLayout.NORTH);
30 | add(new JScrollPane(table), BorderLayout.CENTER);
31 |
32 | setBorder(new TitledBorder("规则管理(RuleManager)"));
33 | }
34 |
35 | public void initComponent() {
36 | this.configPanel = new JPanel();
37 | this.table = new DropPacketTable();
38 |
39 | this.removeButton = new JButton("删除(Remove)");
40 | this.configPanel.add(this.removeButton);
41 | }
42 |
43 | public void initEvent() {
44 | removeButton.addActionListener(e -> {
45 | int index = table.getSelectedRow();
46 | if (index != -1) {
47 | table.removeSelectedItem();
48 | } else {
49 | JOptionPane.showMessageDialog(this, "请选择需要删除的数据(Please select row)!");
50 | }
51 | });
52 | }
53 |
54 | @Override
55 | public void initConfig(JSONObject rootJSONObject) {
56 |
57 | }
58 |
59 | @Override
60 | public String rootJSONObjectKey() {
61 | return null;
62 | }
63 |
64 | /**
65 | * 根据URL在tableData中搜索是否存在这个URL数据
66 | *
67 | * @return true:存在 false:不存在
68 | */
69 | public boolean filterUrlOnData(URL url) {
70 | String targetUrl = url.toExternalForm();
71 | long count = table.getTableData().stream().filter(i -> targetUrl.contains(i.getUrl())).count();
72 | return count > 0;
73 | }
74 |
75 | public DropPacketTable getTable() {
76 | return table;
77 | }
78 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/droppacket/DropPacketTable.java:
--------------------------------------------------------------------------------
1 | package burp.ui.droppacket;
2 |
3 | import burp.bean.Drop;
4 | import burp.ui.component.BeanTable;
5 |
6 | import java.util.Vector;
7 |
8 | /**
9 | * 丢弃数据包表格
10 | *
11 | * @author RichardTang
12 | */
13 | public class DropPacketTable extends BeanTable {
14 |
15 | DropPacketTable() {
16 | super();
17 | }
18 |
19 | @Override
20 | public Object initializeGetValueAt(int rowIndex, int columnIndex) {
21 | Drop item = data.get(rowIndex);
22 | switch (columnIndex) {
23 | case 0:
24 | return item.getId();
25 | case 1:
26 | return item.getUrl();
27 | case 2:
28 | return item.getComment();
29 | }
30 | return null;
31 | }
32 |
33 | @Override
34 | public Vector initializeColumnName() {
35 | final Vector columnName = new Vector<>();
36 | columnName.addElement("编号(ID)");
37 | columnName.addElement("地址(URL)");
38 | columnName.addElement("备注(Comment)");
39 | return columnName;
40 | }
41 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/rule/RulePanel.java:
--------------------------------------------------------------------------------
1 | package burp.ui.rule;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.bean.Rule;
5 | import burp.constant.ConfigKey;
6 | import burp.constant.RuleActionOption;
7 | import burp.constant.RuleTypeOption;
8 | import burp.constant.WindowSize;
9 | import burp.ui.component.BurpPanel;
10 | import burp.ui.component.PlaceholderTextField;
11 | import cn.hutool.json.JSONArray;
12 | import cn.hutool.json.JSONObject;
13 | import cn.hutool.json.JSONUtil;
14 | import lombok.Data;
15 | import net.miginfocom.swing.MigLayout;
16 |
17 | import javax.swing.*;
18 | import javax.swing.border.TitledBorder;
19 | import javax.swing.event.PopupMenuEvent;
20 | import javax.swing.event.PopupMenuListener;
21 | import java.awt.*;
22 | import java.util.Vector;
23 |
24 | /**
25 | * 规则面板
26 | *
27 | * @author RichardTang
28 | */
29 | @Data
30 | public class RulePanel extends BurpPanel {
31 |
32 | private JPanel otherPanel;
33 | private JPanel toolPanel;
34 | private JPanel configPanel;
35 | private JPanel rulePanel;
36 |
37 | private JCheckBox randomUserAgentCheckBox;
38 | private JCheckBox repeaterResponseAutoDecodeCheckBox;
39 |
40 | private JButton addButton;
41 | private JButton removeButton;
42 | private JButton modifyButton;
43 |
44 | private JCheckBox comparerToolCheckBox;
45 | private JCheckBox decoderToolCheckBox;
46 | private JCheckBox extenderToolCheckBox;
47 | private JCheckBox intruderToolCheckBox;
48 | private JCheckBox proxyToolCheckBox;
49 | private JCheckBox repeaterToolCheckBox;
50 | private JCheckBox scannerToolCheckBox;
51 | private JCheckBox sequencerToolCheckBox;
52 | private JCheckBox spiderToolCheckBox;
53 | private JCheckBox suiteToolCheckBox;
54 | private JCheckBox targetToolCheckBox;
55 | private JPanel listenerConfigPanel;
56 |
57 | public static final RuleTable table = new RuleTable();
58 |
59 | // 文本框默认提示信息
60 | public static final String URL_TEXT_DEFAULT_PLACEHOLDER = "https://*mask-sec.com";
61 | public static final String KEY_NAME_DEFAULT_PLACEHOLDER = "User-Agent";
62 | public static final String KEY_VALUE_DEFAULT_PLACEHOLDER = "MaskSecAgent";
63 | public static final String NEW_VALUE_DEFAULT_PLACEHOLDER = "需要删除填空即可";
64 |
65 |
66 | public RulePanel(final IBurpExtenderCallbacks iBurpExtenderCallbacks) {
67 | super(iBurpExtenderCallbacks);
68 |
69 | setLayout(new BorderLayout());
70 | add(configPanel, BorderLayout.NORTH);
71 | add(rulePanel, BorderLayout.CENTER);
72 | }
73 |
74 | public void initComponent() {
75 | randomUserAgentCheckBox = new JCheckBox("随机UA头(Random UA Header)");
76 | repeaterResponseAutoDecodeCheckBox = new JCheckBox("RepeaterResponse自动解码(Repeater Response Auto Decode)");
77 | otherPanel = new JPanel();
78 | otherPanel.add(randomUserAgentCheckBox);
79 | otherPanel.add(repeaterResponseAutoDecodeCheckBox);
80 | otherPanel.setBorder(new TitledBorder("其他配置(OtherConfig)"));
81 |
82 | comparerToolCheckBox = new JCheckBox("对比(Comparer)");
83 | decoderToolCheckBox = new JCheckBox("编码(Decoder)");
84 | extenderToolCheckBox = new JCheckBox("插件(Extender)");
85 | intruderToolCheckBox = new JCheckBox("测试(Intruder)");
86 | proxyToolCheckBox = new JCheckBox("代理(Proxy)");
87 | repeaterToolCheckBox = new JCheckBox("重放(Repeater)");
88 | scannerToolCheckBox = new JCheckBox("扫描(Scanner)");
89 | sequencerToolCheckBox = new JCheckBox("定序(Sequencer)");
90 | spiderToolCheckBox = new JCheckBox("爬虫(Spider)");
91 | suiteToolCheckBox = new JCheckBox("程序(Suite)");
92 | targetToolCheckBox = new JCheckBox("目标(Target)");
93 |
94 | listenerConfigPanel = new JPanel();
95 | listenerConfigPanel.add(comparerToolCheckBox);
96 | listenerConfigPanel.add(decoderToolCheckBox);
97 | listenerConfigPanel.add(extenderToolCheckBox);
98 | listenerConfigPanel.add(intruderToolCheckBox);
99 | listenerConfigPanel.add(proxyToolCheckBox);
100 | listenerConfigPanel.add(repeaterToolCheckBox);
101 | listenerConfigPanel.add(scannerToolCheckBox);
102 | listenerConfigPanel.add(sequencerToolCheckBox);
103 | listenerConfigPanel.add(spiderToolCheckBox);
104 | listenerConfigPanel.add(targetToolCheckBox);
105 | listenerConfigPanel.add(suiteToolCheckBox);
106 | listenerConfigPanel.setBorder(new TitledBorder("监听配置(ListenerConfig)"));
107 |
108 | toolPanel = new JPanel();
109 | addButton = new JButton();
110 | removeButton = new JButton();
111 | modifyButton = new JButton();
112 |
113 | addButton.setText("添加(Add)");
114 | removeButton.setText("删除(Remove)");
115 | modifyButton.setText("修改(Modify)");
116 |
117 | toolPanel.add(addButton);
118 | toolPanel.add(removeButton);
119 | toolPanel.add(modifyButton);
120 |
121 | rulePanel = new JPanel();
122 | rulePanel.setLayout(new BorderLayout());
123 | rulePanel.setBorder(new TitledBorder("规则管理(RuleManager)"));
124 | rulePanel.add(toolPanel, BorderLayout.NORTH);
125 | rulePanel.add(new JScrollPane(table), BorderLayout.CENTER);
126 |
127 | configPanel = new JPanel();
128 | configPanel.setLayout(new BorderLayout());
129 | configPanel.add(otherPanel, BorderLayout.NORTH);
130 | configPanel.add(listenerConfigPanel, BorderLayout.CENTER);
131 | }
132 |
133 | public void initEvent() {
134 | addButton.addActionListener(e -> SwingUtilities.invokeLater(this::ruleFormWindow));
135 |
136 | modifyButton.addActionListener(e -> SwingUtilities.invokeLater(() -> {
137 | Rule rule = table.getSelectedItem();
138 | if (rule == null) {
139 | JOptionPane.showMessageDialog(this, "请选择需要修改的数据(Please Select Row)!");
140 | } else {
141 | ruleFormWindow(rule);
142 | }
143 | }));
144 |
145 | removeButton.addActionListener(e -> {
146 | int index = table.getSelectedRow();
147 | if (index != -1) {
148 | table.removeSelectedItem();
149 | } else {
150 | JOptionPane.showMessageDialog(this, "请选择需要删除的数据(Please Select Row)!");
151 | }
152 | });
153 | }
154 |
155 | @Override
156 | public void initConfig(JSONObject rootJSONObject) {
157 | JSONArray jsonArray = JSONUtil.parseArray(rootJSONObject.get(ConfigKey.RULE_TABLE_KEY));
158 | table.addRows(new Vector<>(jsonArray.toList(Rule.class)));
159 |
160 | randomUserAgentCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.RANDOM_UA_KEY));
161 | repeaterResponseAutoDecodeCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.RP_AD_KEY));
162 | comparerToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.COMPARER_TOOL_KEY));
163 | decoderToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.DECODER_TOOL_KEY));
164 | extenderToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.EXTENDER_TOOL_KEY));
165 | intruderToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.INTRUDER_TOOL_KEY));
166 | proxyToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.PROXY_TOOL_KEY));
167 | repeaterToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.REPEATER_TOOL_KEY));
168 | scannerToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.SCANNER_TOOL_KEY));
169 | sequencerToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.SEQUENCER_TOOL_KEY));
170 | spiderToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.SPIDER_TOOL_KEY));
171 | suiteToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.SUITE_TOOL_KEY));
172 | targetToolCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.TARGET_TOOL_KEY));
173 | }
174 |
175 | @Override
176 | public String rootJSONObjectKey() {
177 | return "rulePanelConfig";
178 | }
179 |
180 | /**
181 | * 弹窗,用于添加新规则。
182 | */
183 | private void ruleFormWindow() {
184 | ruleFormWindow(null);
185 | }
186 |
187 | /**
188 | * 弹窗,用于回显指定的规则。
189 | *
190 | * @param rule 需要回显的规则
191 | */
192 | private void ruleFormWindow(Rule rule) {
193 | JFrame formWindow = new JFrame();
194 | formWindow.setLocationRelativeTo(this);
195 | formWindow.setSize(WindowSize.RULE_FORM_WINDOW);
196 | formWindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
197 |
198 | PlaceholderTextField urlTextField = new PlaceholderTextField();
199 | PlaceholderTextField keyNameTextField = new PlaceholderTextField();
200 | PlaceholderTextField keyValueTextField = new PlaceholderTextField();
201 | JComboBox typeComboBox = new JComboBox<>();
202 | typeComboBox.setModel(new DefaultComboBoxModel<>(RuleTypeOption.values()));
203 | JComboBox actionComboBox = new JComboBox<>();
204 | actionComboBox.setModel(new DefaultComboBoxModel<>(RuleActionOption.values()));
205 | JButton submitButton = new JButton("提交(Submit)");
206 |
207 | // Placeholder文本
208 | urlTextField.setPlaceholder(URL_TEXT_DEFAULT_PLACEHOLDER);
209 | keyNameTextField.setPlaceholder(KEY_NAME_DEFAULT_PLACEHOLDER);
210 | keyValueTextField.setPlaceholder(KEY_VALUE_DEFAULT_PLACEHOLDER);
211 |
212 | // 表单布局
213 | JPanel panel = new JPanel(new MigLayout("", "[][grow]"));
214 | JLabel urlLabel = new JLabel("地址(URL): ");
215 | JLabel keyNameLabel = new JLabel("键名(KeyName): ");
216 | JLabel keyValueLabel = new JLabel("键值(KeyValue): ");
217 | JLabel typeLabel = new JLabel("类型(Type): ");
218 | JLabel actionLabel = new JLabel("动作(Action): ");
219 | panel.add(urlLabel, "cell 0 0");
220 | panel.add(urlTextField, "cell 1 0, grow");
221 | panel.add(typeLabel, "cell 0 1");
222 | panel.add(typeComboBox, "cell 1 1, grow, wrap");
223 | panel.add(keyNameLabel, "cell 0 2");
224 | panel.add(keyNameTextField, "cell 1 2, grow");
225 | panel.add(keyValueLabel, "cell 0 3");
226 | panel.add(keyValueTextField, "cell 1 3, grow");
227 | panel.add(actionLabel, "cell 0 4");
228 | panel.add(actionComboBox, "cell 1 4, grow, wrap");
229 | panel.add(submitButton, "span, grow");
230 |
231 | // 清空窗口上一次的数据
232 | if (rule != null) {
233 | urlTextField.setText(rule.getUrl());
234 | keyNameTextField.setText(rule.getKeyName());
235 | keyValueTextField.setText(rule.getKeyValue());
236 | typeComboBox.setSelectedItem(rule.getType());
237 | actionComboBox.setSelectedItem(rule.getAction());
238 | }
239 |
240 | // 提交
241 | submitButton.addActionListener(e -> {
242 | if (rule != null) {
243 | // 更新规则
244 | rule.setUrl(urlTextField.getText());
245 | rule.setKeyName(keyNameTextField.getText());
246 | rule.setKeyValue(keyValueTextField.getText());
247 | rule.setType((RuleTypeOption) typeComboBox.getSelectedItem());
248 | rule.setAction((RuleActionOption) actionComboBox.getSelectedItem());
249 | table.updateUI();
250 | } else {
251 | // 添加新规则到表格中
252 | Rule newRule = Rule.builder().url(urlTextField.getText()).keyName(keyNameTextField.getText()).keyValue(keyValueTextField.getText()).type((RuleTypeOption) typeComboBox.getSelectedItem()).action((RuleActionOption) actionComboBox.getSelectedItem()).id(table.getRowCount()).build();
253 | table.addRow(newRule);
254 | }
255 | formWindow.dispose();
256 | });
257 |
258 | typeComboBox.addPopupMenuListener(new PopupMenuListener() {
259 | @Override
260 | public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
261 |
262 | }
263 |
264 | @Override
265 | public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
266 | // 切换显示的字符
267 | JComboBox source = (JComboBox) e.getSource();
268 | if (source.getSelectedItem() == RuleTypeOption.BODY) {
269 | actionLabel.setVisible(false);
270 | actionComboBox.setVisible(false);
271 | keyNameLabel.setText("旧值(OldValue)");
272 | keyValueLabel.setText("新值(NewValue)");
273 | keyValueTextField.setPlaceholder(NEW_VALUE_DEFAULT_PLACEHOLDER);
274 | } else {
275 | actionLabel.setVisible(true);
276 | actionComboBox.setVisible(true);
277 | keyNameLabel.setText("键名(KeyName)");
278 | keyValueLabel.setText("键值(KeyValue)");
279 | keyNameTextField.setPlaceholder(KEY_NAME_DEFAULT_PLACEHOLDER);
280 | keyValueTextField.setPlaceholder(KEY_VALUE_DEFAULT_PLACEHOLDER);
281 | }
282 | panel.updateUI();
283 | }
284 |
285 | @Override
286 | public void popupMenuCanceled(PopupMenuEvent e) {
287 |
288 | }
289 | });
290 |
291 | formWindow.setContentPane(panel);
292 | SwingUtilities.invokeLater(() -> formWindow.setVisible(true));
293 | }
294 |
295 | /**
296 | * 校验是否勾选指定的监听模块
297 | *
298 | * @param msgType 模块的类型编号
299 | * @return true:已勾选 false:未勾选
300 | */
301 | public boolean validListenerEnabled(int msgType) {
302 | switch (msgType) {
303 | case IBurpExtenderCallbacks.TOOL_COMPARER:
304 | return comparerToolCheckBox.isSelected();
305 | case IBurpExtenderCallbacks.TOOL_DECODER:
306 | return decoderToolCheckBox.isSelected();
307 | case IBurpExtenderCallbacks.TOOL_EXTENDER:
308 | return extenderToolCheckBox.isSelected();
309 | case IBurpExtenderCallbacks.TOOL_INTRUDER:
310 | return intruderToolCheckBox.isSelected();
311 | case IBurpExtenderCallbacks.TOOL_PROXY:
312 | return proxyToolCheckBox.isSelected();
313 | case IBurpExtenderCallbacks.TOOL_REPEATER:
314 | return repeaterToolCheckBox.isSelected();
315 | case IBurpExtenderCallbacks.TOOL_SCANNER:
316 | return scannerToolCheckBox.isSelected();
317 | case IBurpExtenderCallbacks.TOOL_SEQUENCER:
318 | return sequencerToolCheckBox.isSelected();
319 | case IBurpExtenderCallbacks.TOOL_SPIDER:
320 | return spiderToolCheckBox.isSelected();
321 | case IBurpExtenderCallbacks.TOOL_SUITE:
322 | return suiteToolCheckBox.isSelected();
323 | case IBurpExtenderCallbacks.TOOL_TARGET:
324 | return targetToolCheckBox.isSelected();
325 | }
326 | return false;
327 | }
328 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/rule/RuleTable.java:
--------------------------------------------------------------------------------
1 | package burp.ui.rule;
2 |
3 | import burp.bean.Rule;
4 | import burp.constant.RuleActionOption;
5 | import burp.constant.RuleTypeOption;
6 | import burp.core.RuleCore;
7 | import burp.ui.component.BeanTable;
8 |
9 | import java.util.Vector;
10 |
11 | /**
12 | * 规则表格
13 | *
14 | * @author RichardTang
15 | */
16 | public class RuleTable extends BeanTable {
17 |
18 | RuleTable() {
19 | super();
20 | }
21 |
22 | @Override
23 | public Object initializeGetValueAt(int rowIndex, int columnIndex) {
24 | Rule item = data.get(rowIndex);
25 | switch (columnIndex) {
26 | case 0:
27 | return item.getId();
28 | case 1:
29 | return item.getUrl();
30 | case 2:
31 | return item.getKeyName();
32 | case 3:
33 | return item.getKeyValue();
34 | case 4:
35 | return item.getType();
36 | case 5:
37 | // 如果是处理BODY的数据,则动作栏显示为MODIFY。
38 | if (item.getType() == RuleTypeOption.BODY) {
39 | return RuleActionOption.MODIFY;
40 | }
41 | return item.getAction();
42 | case 6:
43 | return item.getActive();
44 | }
45 | return null;
46 | }
47 |
48 | @Override
49 | public Vector initializeColumnName() {
50 | final Vector columnName = new Vector<>();
51 | columnName.addElement("编号(ID)");
52 | columnName.addElement("地址(URL)");
53 | columnName.addElement("键名/旧值(KeyName/OldValue)");
54 | columnName.addElement("键值/新值(KeyValue/NewValue)");
55 | columnName.addElement("类型(Type)");
56 | columnName.addElement("动作(Action)");
57 | columnName.addElement("状态(State)");
58 | return columnName;
59 | }
60 |
61 | @Override
62 | public void initializeSetValueAt(Object aValue, int row, int column) {
63 | // 最后一个column,在当前项目中是固定的组件为CheckBox,需要特殊处理。
64 | if (column == 6) {
65 | Boolean isActive = (Boolean) aValue;
66 | Rule rule = data.get(row);
67 | rule.setActive(isActive);
68 | if (isActive) {
69 | RuleCore.activeRuleData.addElement(rule);
70 | } else {
71 | RuleCore.activeRuleData.removeElement(rule);
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/useragent/UserAgentPanel.java:
--------------------------------------------------------------------------------
1 | package burp.ui.useragent;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.constant.ConfigKey;
5 | import burp.core.UserAgentCore;
6 | import burp.ui.component.BurpPanel;
7 | import burp.util.FileUtil;
8 | import cn.hutool.json.JSONArray;
9 | import cn.hutool.json.JSONObject;
10 | import cn.hutool.json.JSONUtil;
11 | import lombok.Data;
12 |
13 | import javax.swing.*;
14 | import javax.swing.border.TitledBorder;
15 | import java.awt.*;
16 | import java.io.*;
17 |
18 | /**
19 | * UA面板
20 | *
21 | * @author RichardTang
22 | */
23 | @Data
24 | public class UserAgentPanel extends BurpPanel {
25 |
26 | private JPanel configPanel;
27 | private JCheckBox pcCheckBox;
28 | private JCheckBox mobileCheckBox;
29 | private JTextArea pcTextArea;
30 | private JTextArea mobileTextArea;
31 | private JTabbedPane userAgentTabbedPane;
32 |
33 | // 默认UA值来源的2个文件
34 | public static final String DEFAULT_PC_UA_FILE = "useragent-pc.txt";
35 | public static final String DEFAULT_MOBILE_UA_FILE = "useragent-mobile.txt";
36 |
37 | public UserAgentPanel(final IBurpExtenderCallbacks iBurpExtenderCallbacks) {
38 | super(iBurpExtenderCallbacks);
39 |
40 | // 从配置文件读取配置完成后,需要将UA值填充回TextArea面板中。
41 | // 如果UA的集合中位0,则从默认UA值来源的2个文件中读取数据。
42 | if (UserAgentCore.pcUserAgent.size() == 0 && "".equals(pcTextArea.getText())) {
43 | InputStream inputStream = UserAgentCore.class.getClassLoader().getResourceAsStream(DEFAULT_PC_UA_FILE);
44 | FileUtil.readLines(inputStream, UserAgentCore.pcUserAgent);
45 | }
46 | if (UserAgentCore.mobileUserAgent.size() == 0 && "".equals(mobileTextArea.getText())) {
47 | InputStream inputStream = UserAgentCore.class.getClassLoader().getResourceAsStream(DEFAULT_MOBILE_UA_FILE);
48 | FileUtil.readLines(inputStream, UserAgentCore.mobileUserAgent);
49 | }
50 | // 将内容添加至UserAgent的TextArea中
51 | for (String line : UserAgentCore.pcUserAgent) {
52 | pcTextArea.append(line.trim());
53 | pcTextArea.append("\n");
54 | }
55 | for (String line : UserAgentCore.mobileUserAgent) {
56 | mobileTextArea.append(line.trim());
57 | mobileTextArea.append("\n");
58 | }
59 | }
60 |
61 | public void initComponent() {
62 | configPanel = new JPanel();
63 | pcTextArea = new JTextArea();
64 | mobileTextArea = new JTextArea();
65 | userAgentTabbedPane = new JTabbedPane();
66 |
67 | pcCheckBox = new JCheckBox("电脑(PC)");
68 | mobileCheckBox = new JCheckBox("手机(Mobile)");
69 | configPanel.add(pcCheckBox);
70 | configPanel.add(mobileCheckBox);
71 | configPanel.setBorder(new TitledBorder("配置启用(ConfigSwitch)"));
72 |
73 | userAgentTabbedPane.addTab("电脑(PC)", new JScrollPane(pcTextArea));
74 | userAgentTabbedPane.addTab("手机(Mobile)", new JScrollPane(mobileTextArea));
75 |
76 | setLayout(new BorderLayout());
77 | add(configPanel, BorderLayout.NORTH);
78 | add(userAgentTabbedPane, BorderLayout.CENTER);
79 | }
80 |
81 | @Override
82 | public void initEvent() {
83 |
84 | }
85 |
86 | @Override
87 | public void initConfig(JSONObject rootJSONObject) {
88 | pcCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.PC_UA_KEY));
89 | mobileCheckBox.setSelected(rootJSONObject.getBool(ConfigKey.MOBILE_UA_KEY));
90 |
91 | // 从配置文件中读取UA值
92 | JSONArray pcUAJSONArray = JSONUtil.parseArray(rootJSONObject.get(ConfigKey.PC_UA_LIST_KEY));
93 | JSONArray mobileUAJSONArray = JSONUtil.parseArray(rootJSONObject.get(ConfigKey.MOBILE_UA_LIST_KEY));
94 |
95 | // 将读取的UA值填入集合中
96 | UserAgentCore.pcUserAgent.addAll(pcUAJSONArray.toList(String.class));
97 | UserAgentCore.mobileUserAgent.addAll(mobileUAJSONArray.toList(String.class));
98 | }
99 |
100 | @Override
101 | public String rootJSONObjectKey() {
102 | return "userAgentPanelConfig";
103 | }
104 | }
--------------------------------------------------------------------------------
/src/main/java/burp/util/FileUtil.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.constant.ConfigKey;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.File;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.util.List;
11 |
12 | /**
13 | * File操作工具类
14 | *
15 | * @author RichardTang
16 | */
17 | public class FileUtil {
18 |
19 | /**
20 | * 从数据源中按行读取数据,并存储至lines集合中。
21 | *
22 | * @param inputStream 数据源
23 | * @param lines 存储每一行数据的集合
24 | */
25 | public static void readLines(final InputStream inputStream, final List lines) {
26 | try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
27 | String line;
28 | while ((line = bufferedReader.readLine()) != null) {
29 | lines.add(line);
30 | }
31 | } catch (Exception e) {
32 | e.printStackTrace();
33 | }
34 | }
35 |
36 | /**
37 | * 根据插件的加载路径,获取配置文件需要存放的根目录。
38 | *
39 | * @param iBurpExtenderCallbacks Burp插件扩展对象
40 | * @return 配置文件根目录
41 | */
42 | public static String getConfigFilePathByBurpExt(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
43 | String pluginJarFilePath = iBurpExtenderCallbacks.getExtensionFilename();
44 | return pluginJarFilePath.substring(0, pluginJarFilePath.lastIndexOf(File.separator)) + File.separator + ConfigKey.CONFIG_FILE_NAME;
45 | }
46 | }
--------------------------------------------------------------------------------
/src/main/java/burp/util/URLUtil.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | /**
4 | * URL操作工具类 拓展至Hutool-URLUtil
5 | *
6 | * @author RichardTang
7 | */
8 | public class URLUtil extends cn.hutool.core.util.URLUtil {
9 |
10 | /**
11 | * 获取URIPath部分
12 | * 如: https://www.mask-sec.com/login?username=aaaa 则获取 https://www.mask-sec.com/login
13 | *
14 | * @param URL 需要获取URIPath的URL
15 | * @return URIPath
16 | */
17 | public static String getURIPath(String URL) {
18 | if (URL.contains("?")) {
19 | return URL.split("\\?")[0];
20 | }
21 | return URL;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/main/resources/info.properties:
--------------------------------------------------------------------------------
1 | version=${project.version}
--------------------------------------------------------------------------------
/src/main/resources/useragent-mobile.txt:
--------------------------------------------------------------------------------
1 | MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
2 | Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
3 | Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
4 | Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10
5 | Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13
6 | Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
7 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36
8 | Mozilla/5.0 (Android 7.1.1; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0
9 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36 OPR/53.0.2569.141117
10 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36 EdgA/42.0.2.3819
11 | Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.6 Mobile Safari/537.36
12 | Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 OppoBrowser/10.5.1.2
13 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.97 Mobile Safari/537.36
14 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 360 Alitephone Browser (1.5.0.90/1.0.100.1078) mso_sdk(1.0.0)
15 | Mozilla/5.0 (Linux; U; Android 7.1.1; zh-CN; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.6.0.1040 Mobile Safari/537.36
16 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 LieBaoFast/5.12.3
17 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/48.0.2564.116 Mobile Safari/537.36 T7/9.1 baidubrowser/7.19.13.0 (Baidu; P1 7.1.1)
18 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.106 Mobile Safari/537.36 AWP/2.0 SogouMSE,SogouMobileBrowser/5.22.8
19 | Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 Mb2345Browser/11.0.1
20 | Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/1A542a Safari/419.3
21 | Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7
22 | Mozilla/5.0 (iPhone 6s; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 MQQBrowser/8.3.0 Mobile/15B87 Safari/604.1 MttCustomUA/2 QBWebViewType/1 WKType/1
--------------------------------------------------------------------------------
/src/main/resources/useragent-pc.txt:
--------------------------------------------------------------------------------
1 | Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; Hot Lingo 2.0)
2 | Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36
3 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3451.0 Safari/537.36
4 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:57.0) Gecko/20100101 Firefox/57.0
5 | Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36
6 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.2999.0 Safari/537.36
7 | Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.70 Safari/537.36
8 | Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2
9 | Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 OPR/31.0.1889.174
10 | Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.1.4322; MS-RTC LM 8; InfoPath.2; Tablet PC 2.0)
11 | Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36 TheWorld 7
12 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
13 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 OPR/55.0.2994.61
14 | Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; MATP; InfoPath.2; .NET4.0C; CIBA; Maxthon 2.0)
15 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.814.0 Safari/535.1
16 | Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3
17 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36
18 | Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0; Touch; MASMJS)
19 | Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1041.0 Safari/535.21
20 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
21 | Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11
22 | Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11
23 | Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)
24 | Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
25 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
26 | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
27 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
28 | Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0
29 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36 OPR/63.0.3368.43
30 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362
31 | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; LCTE; rv:11.0) like Gecko
32 | Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/534.54.16 (KHTML, like Gecko) Version/5.1.4 Safari/534.54.16
33 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3722.400 QQBrowser/10.5.3739.400
34 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE
35 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 QIHU 360EE
36 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.3964.2 Safari/537.36
37 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0
38 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER
39 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
40 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36
--------------------------------------------------------------------------------