├── .idea
├── .gitignore
├── compiler.xml
├── encodings.xml
├── jarRepositories.xml
├── misc.xml
├── uiDesigner.xml
└── vcs.xml
├── AutoFuzz.iml
├── README.md
├── assets
├── .DS_Store
├── image-20250303233548430.png
├── image-20250303234145025.png
├── image-20250304001209634.png
├── image-20250304001418561.png
├── image-20250304002312839.png
├── image-20250304002640059.png
├── image-20250304003218031.png
├── image-20250304003238900.png
├── image-20250304211651127.png
├── image-20250304211737496.png
├── image-20250304212242627.png
├── image-20250304212334916.png
├── image-20250304214210463.png
├── image-20250304214245111.png
├── image-20250304214330266.png
├── image-20250304215106091.png
├── image-20250304215342739.png
├── image-20250304222451866.png
├── image-20250304222531391.png
├── image-20250304222609482.png
├── image-20250304222912568.png
├── image-20250304222943732.png
├── image-20250304224510635.png
├── image-20250304231940839.png
├── image-20250304235348677.png
└── image-20250304235439669.png
├── pom.xml
└── src
└── main
├── java
├── burp
│ └── api
│ │ └── montoya
│ │ └── core
│ │ └── ToolType.java
└── com
│ └── chave
│ ├── Main.java
│ ├── bean
│ ├── Data.java
│ ├── FuzzRequestItem.java
│ ├── Language.java
│ ├── Method.java
│ ├── OriginRequestItem.java
│ └── SearchScope.java
│ ├── config
│ ├── Config.java
│ └── UserConfig.java
│ ├── handler
│ └── AutoFuzzHandler.java
│ ├── menu
│ └── AutoFuzzMenu.java
│ ├── service
│ └── AutoFuzzService.java
│ ├── ui
│ └── MainUI.java
│ └── utils
│ ├── Util.java
│ └── YamlUtil.java
└── resources
├── lang_en_US.properties
└── lang_zh_CN.properties
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AutoFuzz.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AutoFuzz
2 |
3 | **AutoFuzz** 是一款安全测试的辅助型项目,主要用于自动识别请求中的参数,根据预设的 payload 逐个发包测试,从而提高测试效率。
4 |
5 | 插件设计源于 **@smxiazi** 师傅的经典项目 **[xia_sql](https://github.com/smxiazi/xia_sql)**,根据自己理解在参数解析上进行优化,同时集成工作中常测试的越权与未授权访问场景。感谢 **@smxiazi** 的经典插件提供思路。
6 |
7 | 插件基于 `Montoya API` 开发,需要满足 BurpSuite 版本(>=2023.12.1)才能使用。
8 |
9 | ## 使用方法
10 |
11 | ### 插件安装
12 |
13 | 插件安装: `Extender - Extensions - Add - Select File - Next`
14 |
15 |
16 |
17 |
18 |
19 | ### 主要功能
20 |
21 | #### 基本功能
22 |
23 | - **启用插件**:顾名思义勾选后该插件启用。
24 | - **监听 Proxy**:自动捕获经过 BurpSuite Proxy 符合条件的请求。
25 | - **监听 Repeter**:自动捕获 BurpSuite Repeter 中符合条件的请求。
26 | - **清空请求记录**:清空右侧表格中的记录。
27 |
28 | > **Tips:**
29 | >
30 | > - 通过监听捕获的流量相同接口只会 fuzz 一次。Method + Host + Path 均相同的请求视为同一请求。
31 | > - 通过右键菜单发送到插件获取的请求,可无视域名/IP限制,无视去重限制。
32 |
33 | #### 域名设置
34 |
35 | 插件仅对用户设置的 **域名/IP** 相关请求发包测试,避免误伤无关站点。
36 |
37 | - **添加域名/IP**
38 | - 点击左侧添加按钮即可添加域名/IP,每行一个,不可重复。
39 |
40 |
41 |
42 |
43 |
44 | - **编辑域名/IP**
45 | - 选中需要编辑的条目 ,点击左侧编辑,修改后确定。修改后数据不可重复,如果选中多条,则默认修改选中第一条。
46 |
47 |
48 |
49 |
50 |
51 | - **删除域名/IP**
52 | - 选中需要删除的条目,点击左侧删除,即可删除对应数据,支持多行选中删除。
53 |
54 |
55 |
56 |
57 |
58 | - **包含子域名**
59 | - 以 `qq.com` 为例,如不勾选包含子域名,则 host 为 `y.qq.com` 的请求无法被捕获。
60 |
61 | #### Payload 设置
62 |
63 | > Tips: 当 **列表中有数据** 时,才会启用该模块功能。
64 |
65 | - **添加payload**
66 | - 点击左侧添加按钮即可添加 payload,每行一个,不可重复。
67 |
68 |
69 |
70 |
71 |
72 | - **编辑payload**
73 | - 选中需要编辑的条目 ,点击左侧编辑,修改后确定。修改后数据不可重复,如果选中多条,则默认修改选中第一条。
74 |
75 |
76 |
77 |
78 |
79 | - **删除payload**
80 | - 选中需要删除的条目,点击左侧删除,即可删除对应数据,支持多行选中删除。
81 |
82 |
83 |
84 |
85 |
86 | - **参数置空**
87 | - 勾选后可增加一项为 `空` 的 payload,在 fuzz 时会将参数值置空。
88 |
89 |
90 |
91 |
92 |
93 | - **URL编码**
94 | - 勾选后,payload 中若存在特殊字符,非 json 格式的参数在 fuzz 时将对特殊字符进行 URL 编码。
95 |
96 |
97 |
98 | #### Auth Header 设置
99 |
100 | > Tips: 当 **列表中有Header数据** 或 **勾选未授权访问** 时,才会启用该模块功能。
101 |
102 | - **添加Header**
103 | - 点击左侧添加按钮即可添加需要进行替换的 Header,每行一个,不可重复。
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | - **编辑Header**
112 | - 选中需要编辑的条目 ,点击左侧编辑,修改后确定。修改后数据不可重复,如果选中多条,则默认修改选中第一条。
113 |
114 |
115 |
116 |
117 |
118 | - **未授权访问**
119 | - 勾选后,在 fuzz 时会去除列表中设置的所有 Header,进行未授权访问测试。
120 |
121 |
122 |
123 | #### 查找功能
124 |
125 | 可根据设置的查找作用域在 `request` 或 `response` 中查找是否含有输入的字符串信息,不区分大小写,不支持中文查找。
126 |
127 |
128 |
129 | #### 右键菜单
130 |
131 | 可通过右键菜单发送到插件,无视域名/IP范围,无视去重限制。
132 |
133 |
134 |
135 |
136 |
137 | ## FAQ
138 |
139 | **Q1**:为什么有了 xia_sql 还重新开发 AutoFuzz,有何不同之处?
140 |
141 | **A1**:来自朋友的需求。添加了越权相关功能测试,提高工作测试效率。xia_sql 对复杂嵌套的 json 解析并不完善,不能满足自己测试需求,进行优化后可精确替换 json 中每个参数的 value。BurpSuite 官方已经推出 Montoya API,紧跟时代,学习一下基于新 API 插件开发
142 |
143 | **Q2**:为什么不添加判断漏洞是否存在功能?
144 |
145 | **A2**:不想插件仅限制于测试某一种漏洞,只希望它作为辅助工具提升测试效率,主要是懒得写、不想写、写不来。
146 |
147 | **Q3**:为什么查找功能不支持中文?
148 |
149 | **A3**:不明原因的编码问题,暂时解决不了,有会的大佬可以帮忙解决下,感谢感谢。
150 |
151 | **Q4**:为什么插件有报错日志,影响使用吗?
152 |
153 | **A4**:异常处理没写好,又不太想找是哪里的异常,应该不影响使用吧,也许吧......
154 |
155 | ## 已知BUG
156 |
157 | - 编辑时的弹窗大小未固定,导致当 payload 或 header 过长,手动编辑时弹窗的宽度不受控制。
158 |
159 | ## 致谢
160 |
161 | 公众号:卫界安全-阿呆攻防、浩凯信安
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/image-20250303233548430.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250303233548430.png
--------------------------------------------------------------------------------
/assets/image-20250303234145025.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250303234145025.png
--------------------------------------------------------------------------------
/assets/image-20250304001209634.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304001209634.png
--------------------------------------------------------------------------------
/assets/image-20250304001418561.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304001418561.png
--------------------------------------------------------------------------------
/assets/image-20250304002312839.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304002312839.png
--------------------------------------------------------------------------------
/assets/image-20250304002640059.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304002640059.png
--------------------------------------------------------------------------------
/assets/image-20250304003218031.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304003218031.png
--------------------------------------------------------------------------------
/assets/image-20250304003238900.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304003238900.png
--------------------------------------------------------------------------------
/assets/image-20250304211651127.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304211651127.png
--------------------------------------------------------------------------------
/assets/image-20250304211737496.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304211737496.png
--------------------------------------------------------------------------------
/assets/image-20250304212242627.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304212242627.png
--------------------------------------------------------------------------------
/assets/image-20250304212334916.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304212334916.png
--------------------------------------------------------------------------------
/assets/image-20250304214210463.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304214210463.png
--------------------------------------------------------------------------------
/assets/image-20250304214245111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304214245111.png
--------------------------------------------------------------------------------
/assets/image-20250304214330266.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304214330266.png
--------------------------------------------------------------------------------
/assets/image-20250304215106091.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304215106091.png
--------------------------------------------------------------------------------
/assets/image-20250304215342739.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304215342739.png
--------------------------------------------------------------------------------
/assets/image-20250304222451866.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304222451866.png
--------------------------------------------------------------------------------
/assets/image-20250304222531391.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304222531391.png
--------------------------------------------------------------------------------
/assets/image-20250304222609482.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304222609482.png
--------------------------------------------------------------------------------
/assets/image-20250304222912568.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304222912568.png
--------------------------------------------------------------------------------
/assets/image-20250304222943732.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304222943732.png
--------------------------------------------------------------------------------
/assets/image-20250304224510635.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304224510635.png
--------------------------------------------------------------------------------
/assets/image-20250304231940839.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304231940839.png
--------------------------------------------------------------------------------
/assets/image-20250304235348677.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304235348677.png
--------------------------------------------------------------------------------
/assets/image-20250304235439669.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chave0v0/AutoFuzz/c7589b25edd953bdca3c5462d2bc287286f54b0e/assets/image-20250304235439669.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.chave
8 | AutoFuzz
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 17
13 | 17
14 |
15 |
16 |
17 |
18 |
19 | org.apache.maven.plugins
20 | maven-assembly-plugin
21 | 3.3.0
22 |
23 |
24 | jar-with-dependencies
25 |
26 |
27 |
28 | com.chave.Main
29 |
30 |
31 |
32 |
33 |
34 | make-assembly
35 | package
36 |
37 | single
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | net.portswigger.burp.extensions
49 | montoya-api
50 | 2024.7
51 |
52 |
53 |
54 | org.projectlombok
55 | lombok
56 | 1.18.30
57 |
58 |
59 |
60 |
61 | com.alibaba
62 | fastjson
63 | 1.2.83
64 |
65 |
66 |
67 | org.yaml
68 | snakeyaml
69 | 2.0
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/main/java/burp/api/montoya/core/ToolType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2022-2023. PortSwigger Ltd. All rights reserved.
3 | *
4 | * This code may be used to extend the functionality of Burp Suite Community Edition
5 | * and Burp Suite Professional, provided that this usage does not violate the
6 | * license terms for those products.
7 | */
8 |
9 | package burp.api.montoya.core;
10 |
11 | /**
12 | * Tools in Burp Suite.
13 | */
14 | public enum ToolType
15 | {
16 | SUITE("Suite"),
17 | TARGET("Target"),
18 | PROXY("Proxy"),
19 | SCANNER("Scanner"),
20 | INTRUDER("Intruder"),
21 | REPEATER("Repeater"),
22 | LOGGER("Logger"),
23 | SEQUENCER("Sequencer"),
24 | DECODER("Decoder"),
25 | COMPARER("Comparer"),
26 | EXTENSIONS("Extensions"),
27 | RECORDED_LOGIN_REPLAYER("Recorded login replayer"),
28 | ORGANIZER("Organizer");
29 |
30 | private final String toolName;
31 |
32 | ToolType(String toolName)
33 | {
34 | this.toolName = toolName;
35 | }
36 |
37 | /**
38 | * @return The tool name.
39 | */
40 | public String toolName()
41 | {
42 | return toolName;
43 | }
44 |
45 | /**
46 | * @return The tool name.
47 | */
48 | @Override
49 | public String toString()
50 | {
51 | return toolName;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/Main.java:
--------------------------------------------------------------------------------
1 | package com.chave;
2 |
3 | import burp.api.montoya.BurpExtension;
4 | import burp.api.montoya.MontoyaApi;
5 | import burp.api.montoya.logging.Logging;
6 | import com.chave.handler.AutoFuzzHandler;
7 | import com.chave.menu.AutoFuzzMenu;
8 | import com.chave.ui.MainUI;
9 | import com.chave.utils.YamlUtil;
10 |
11 | public class Main implements BurpExtension {
12 | public static MontoyaApi API;
13 | public static Logging LOG;
14 | public static MainUI MainUI;
15 | @Override
16 | public void initialize(MontoyaApi api) {
17 | // 初始化api与log
18 | API = api;
19 | LOG = api.logging();
20 |
21 | // banner info
22 | API.extension().setName("AutoFuzz");
23 | LOG.logToOutput("AutoFuzz v1.5");
24 | LOG.logToOutput("Author: Chave");
25 | LOG.logToOutput("Contributors: z-bool, Y5neKO, d1sbb");
26 | LOG.logToOutput("Github: https://github.com/Chave0v0/AutoFuzz");
27 |
28 | // 加载配置文件
29 | if (YamlUtil.checkConfigDir()) {
30 | YamlUtil.loadYamlConfig();
31 | }
32 |
33 |
34 | // 初始化ui
35 | MainUI = new MainUI();
36 |
37 | API.userInterface().registerSuiteTab("AutoFuzz", MainUI.getMainSplitPane());
38 | API.http().registerHttpHandler(new AutoFuzzHandler());
39 | API.userInterface().registerContextMenuItemsProvider(new AutoFuzzMenu());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/bean/Data.java:
--------------------------------------------------------------------------------
1 | package com.chave.bean;
2 |
3 | import burp.api.montoya.http.message.requests.HttpRequest;
4 | import java.util.ArrayList;
5 | import java.util.LinkedHashMap;
6 |
7 | public class Data {
8 | public static LinkedHashMap ORIGIN_REQUEST_TABLE_DATA = new LinkedHashMap<>();
9 | public static LinkedHashMap> NEW_REQUEST_TO_BE_SENT_DATA = new LinkedHashMap<>();
10 | public static ArrayList DOMAIN_LIST = new ArrayList<>();
11 | public static ArrayList PAYLOAD_LIST = new ArrayList<>();
12 | public static LinkedHashMap HEADER_MAP = new LinkedHashMap<>();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/bean/FuzzRequestItem.java:
--------------------------------------------------------------------------------
1 | package com.chave.bean;
2 |
3 | import burp.api.montoya.http.message.HttpRequestResponse;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | @Data
8 | @NoArgsConstructor
9 | public class FuzzRequestItem {
10 | private String param;
11 | private String payload;
12 | private String responseLength;
13 | private String responseLengthChange;
14 | private String responseCode;
15 | private String responseTime;
16 | private OriginRequestItem originRequestItem;
17 | private HttpRequestResponse fuzzRequestResponse;
18 |
19 | public FuzzRequestItem(String param, String payload, String responseLength, String responseLengthChange, String responseCode, String responseTime, OriginRequestItem originRequestItem) {
20 | this.param = param;
21 | this.payload = payload;
22 | this.responseLength = responseLength;
23 | this.responseLengthChange = responseLengthChange;
24 | this.responseCode = responseCode;
25 | this.responseTime = responseTime;
26 | this.originRequestItem = originRequestItem;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/bean/Language.java:
--------------------------------------------------------------------------------
1 | package com.chave.bean;
2 |
3 | public enum Language {
4 | SIMPLIFIED_CHINESE("简体中文"),
5 | ENGLISH("English");
6 |
7 | private final String language;
8 |
9 | Language(String language)
10 | {
11 | this.language = language;
12 | }
13 |
14 |
15 | public String language()
16 | {
17 | return language;
18 | }
19 |
20 |
21 | @Override
22 | public String toString()
23 | {
24 | return language;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/bean/Method.java:
--------------------------------------------------------------------------------
1 | package com.chave.bean;
2 |
3 | public class Method {
4 | public final static String GET = "GET";
5 | public final static String POST = "POST";
6 | public final static String PUT = "PUT";
7 | public final static String DELETE = "DELETE";
8 | public final static String HEAD = "HEAD";
9 | public final static String OPTIONS = "OPTIONS";
10 | public final static String TRACE = "TRACE";
11 | public final static String CONNECT = "CONNECT";
12 | public final static String PATCH = "PATCH";
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/bean/OriginRequestItem.java:
--------------------------------------------------------------------------------
1 | package com.chave.bean;
2 |
3 | import burp.api.montoya.http.message.requests.HttpRequest;
4 | import burp.api.montoya.http.message.responses.HttpResponse;
5 | import lombok.Data;
6 | import lombok.ToString;
7 |
8 | import java.util.ArrayList;
9 |
10 | @Data
11 | @ToString(exclude = {"originRequest", "originResponse", "fuzzRequestArrayList"})//修复栈溢出'java.lang.StackOverflowError' 异常。 无法对 com.chave.bean.OriginRequestItem.toString() 求值
12 | public class OriginRequestItem {
13 | private Integer id;
14 | private String host;
15 | private String method;
16 | private String path;
17 | private String responseLength;
18 | private String responseCode;
19 | private ArrayList fuzzRequestArrayList;
20 | private HttpRequest originRequest;
21 | private HttpResponse originResponse;
22 |
23 | public OriginRequestItem(Integer id, String method, String host, String path, String responseLength, String responseCode) {
24 | this.id = id;
25 | this.host = host;
26 | this.method = method;
27 | this.path = path;
28 | this.responseLength = responseLength;
29 | this.responseCode = responseCode;
30 | this.fuzzRequestArrayList = new ArrayList<>();
31 | }
32 |
33 | @Override
34 | public boolean equals(Object o) {
35 | if (this == o) return true;
36 | if (o == null || getClass() != o.getClass()) return false;
37 |
38 | if (((OriginRequestItem) o).getPath().equals(this.path) && ((OriginRequestItem) o).getHost().equals(this.host) && ((OriginRequestItem) o).getMethod().equals(this.method)) {
39 | return true;
40 | }
41 |
42 | return false;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/bean/SearchScope.java:
--------------------------------------------------------------------------------
1 | package com.chave.bean;
2 |
3 | public enum SearchScope {
4 | REQUEST("request"),
5 | RESPONSE("response");
6 |
7 | private final String scopeName;
8 |
9 | SearchScope(String scopeName)
10 | {
11 | this.scopeName = scopeName;
12 | }
13 |
14 |
15 | public String scopeName()
16 | {
17 | return scopeName;
18 | }
19 |
20 |
21 | @Override
22 | public String toString()
23 | {
24 | return scopeName;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/config/Config.java:
--------------------------------------------------------------------------------
1 | package com.chave.config;
2 |
3 | public class Config {
4 | // 不进行fuzz的后缀
5 | public static final String[] STATIC_RESOURCE = new String[]{
6 | ".css", ".js", ".html", ".htm", ".xml", ".svg", ".woff", ".woff2",
7 | ".ttf", ".eot", ".otf", ".ico", ".jpg", ".jpeg", ".png", ".gif", ".bmp",
8 | ".tiff", ".webp", ".mp4", ".mp3", ".ogg", ".wav", ".flv", ".avi", ".mov",
9 | ".mkv", ".zip", ".tar", ".gzip", ".pdf", ".txt", ".csv", ".xls", ".xlsx",
10 | ".xml", ".yaml", ".md", ".rtf", ".ico", ".woff", ".woff2", ".eot", ".otf",
11 | ".ttf", ".svg", ".csv", "js.map"
12 | };
13 |
14 | // 配置文件路径
15 | public static String CONFIG_DIR = System.getProperty("os.name").toLowerCase().contains("win") ? System.getProperty("user.home") + "\\.config\\AutoFuzz\\" : System.getProperty("user.home") + "/.config/AutoFuzz/";
16 | public static String CONFIG_FILE = System.getProperty("os.name").toLowerCase().contains("win") ? System.getProperty("user.home") + "\\.config\\AutoFuzz\\config.yml" : System.getProperty("user.home") + "/.config/AutoFuzz/config.yml";
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/config/UserConfig.java:
--------------------------------------------------------------------------------
1 | package com.chave.config;
2 |
3 | import com.chave.bean.Language;
4 | import com.chave.bean.SearchScope;
5 |
6 | public class UserConfig {
7 | public static Boolean TURN_ON = Boolean.TRUE;
8 | public static Boolean LISTEN_PROXY = Boolean.FALSE;
9 | public static Boolean LISTEN_REPETER = Boolean.FALSE;
10 | public static Language LANGUAGE = Language.SIMPLIFIED_CHINESE;
11 |
12 | public static Boolean BLACK_OR_WHITE_CHOOSE = Boolean.TRUE; // true为黑名单
13 | public static Boolean INCLUDE_SUBDOMAIN = Boolean.FALSE;
14 |
15 | public static Boolean APPEND_MOD = Boolean.FALSE;
16 | public static Boolean PARAM_URL_ENCODE = Boolean.FALSE;
17 | public static Boolean UNAUTH = Boolean.FALSE;
18 |
19 | public static SearchScope SEARCH_SCOPE;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/handler/AutoFuzzHandler.java:
--------------------------------------------------------------------------------
1 | package com.chave.handler;
2 |
3 | import burp.api.montoya.core.ToolType;
4 | import burp.api.montoya.http.handler.*;
5 | import com.chave.Main;
6 | import com.chave.config.UserConfig;
7 | import com.chave.menu.AutoFuzzMenu;
8 | import com.chave.bean.Data;
9 | import com.chave.bean.OriginRequestItem;
10 | import com.chave.service.AutoFuzzService;
11 | import com.chave.utils.Util;
12 |
13 | import java.net.URL;
14 | import java.util.Map;
15 | import java.util.concurrent.LinkedBlockingQueue;
16 | import java.util.concurrent.ThreadPoolExecutor;
17 | import java.util.concurrent.TimeUnit;
18 |
19 | public class AutoFuzzHandler implements HttpHandler {
20 | private AutoFuzzService autoFuzzService = new AutoFuzzService();
21 | private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200), new ThreadPoolExecutor.AbortPolicy());
22 |
23 | @Override
24 | public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
25 |
26 | try {
27 | // 插件需要启用
28 | if (UserConfig.TURN_ON) {
29 | String host = new URL(requestToBeSent.url()).getHost();
30 | // 先过滤域名
31 | if (Data.DOMAIN_LIST.size() > 0){
32 | for (String domain : Data.DOMAIN_LIST) {
33 | if(matchesDomain(host, domain, UserConfig.BLACK_OR_WHITE_CHOOSE)){
34 | // 预检查
35 | if (fuzzPreCheck(requestToBeSent, host)) {
36 | autoFuzzService.preFuzz(requestToBeSent);
37 | }
38 | break;
39 | }
40 | }
41 | }else if(UserConfig.BLACK_OR_WHITE_CHOOSE){
42 | if (fuzzPreCheck(requestToBeSent, host)) {
43 | autoFuzzService.preFuzz(requestToBeSent);
44 | }
45 | }
46 | }
47 | } catch (Exception e) {
48 | Main.LOG.logToError("request handler 出现异常" + e.getCause());
49 | }
50 |
51 |
52 | return null;
53 | }
54 |
55 | private boolean matchesDomain(String host, String domain, boolean isBlackOrWhite) {
56 | boolean isExactMatch = host.equals(domain);
57 | boolean isSubdomain = UserConfig.INCLUDE_SUBDOMAIN && host.endsWith(domain);
58 |
59 | return isBlackOrWhite
60 | ? (!isExactMatch && !isSubdomain) // 白名单模式:允许精确匹配或子域名
61 | : (isExactMatch || isSubdomain); // 黑名单模式:不允许域名或者子域名匹配到
62 | }
63 |
64 | @Override
65 | public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
66 | int msgId = responseReceived.messageId();
67 | OriginRequestItem originRequestItem = Data.ORIGIN_REQUEST_TABLE_DATA.get(msgId);
68 | if (originRequestItem != null) {
69 | // 完成表格原请求返回包长度/状态码填写
70 | originRequestItem.setResponseLength(responseReceived.toString().length() + "");
71 | originRequestItem.setResponseCode(responseReceived.statusCode() + "");
72 | // 保存原请求响应数据
73 | originRequestItem.setOriginResponse(responseReceived);
74 |
75 | // 加入线程池进行fuzz
76 | executor.submit(new Runnable() {
77 | @Override
78 | public void run() {
79 | // 在子线程中执行 autoFuzzService.startFuzz(msgId);
80 | autoFuzzService.startFuzz(msgId);
81 | }
82 | });
83 |
84 | // fuzz完成后向表格中添加originRequest条目
85 | Util.addOriginRequestItem(originRequestItem);
86 | } else {
87 | originRequestItem = Data.ORIGIN_REQUEST_TABLE_DATA.get(AutoFuzzMenu.ID);
88 | if (originRequestItem != null) {
89 | AutoFuzzMenu.ID--;
90 |
91 | autoFuzzService.startFuzz(AutoFuzzMenu.ID + 1);
92 |
93 | // fuzz完成后向表格中添加originRequest条目
94 | Util.addOriginRequestItem(originRequestItem);
95 | }
96 | }
97 |
98 | return null;
99 | }
100 |
101 |
102 |
103 | public static boolean fuzzPreCheck(HttpRequestToBeSent requestToBeSent, String host) {
104 | if ((UserConfig.LISTEN_PROXY && requestToBeSent.toolSource().isFromTool(ToolType.PROXY)) || (UserConfig.LISTEN_REPETER && requestToBeSent.toolSource().isFromTool(ToolType.REPEATER))) {
105 | // 监听捕获的请求进行去重
106 | if (!checkIsFuzzed(requestToBeSent, host)) {
107 | return true;
108 | }
109 | }
110 |
111 | return false;
112 | }
113 |
114 | private static boolean checkIsFuzzed(HttpRequestToBeSent requestToBeSent, String host) {
115 | for (Map.Entry entry : Data.ORIGIN_REQUEST_TABLE_DATA.entrySet()) {
116 | OriginRequestItem item = entry.getValue();
117 | OriginRequestItem tempItem = new OriginRequestItem(null, requestToBeSent.method(), host, requestToBeSent.pathWithoutQuery(), null, null);
118 | if (item.equals(tempItem)) {
119 | return true;
120 | }
121 | }
122 |
123 | return false;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/menu/AutoFuzzMenu.java:
--------------------------------------------------------------------------------
1 | package com.chave.menu;
2 |
3 | import burp.api.montoya.http.message.requests.HttpRequest;
4 | import burp.api.montoya.ui.contextmenu.ContextMenuEvent;
5 | import burp.api.montoya.ui.contextmenu.ContextMenuItemsProvider;
6 | import com.chave.Main;
7 | import com.chave.service.AutoFuzzService;
8 | import javax.swing.*;
9 | import java.awt.*;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 |
15 | public class AutoFuzzMenu implements ContextMenuItemsProvider {
16 | private AutoFuzzService autoFuzzService = new AutoFuzzService();
17 | public static Integer ID = -1;
18 |
19 | @Override
20 | public List provideMenuItems(ContextMenuEvent event) {
21 |
22 | // 定义菜单
23 | ArrayList menus = new ArrayList<>();
24 | //@d1sbb修改,修复get()可能为空时异常
25 | event.messageEditorRequestResponse().ifPresent(editorReqResp -> {
26 | HttpRequest request = editorReqResp.requestResponse().request();
27 |
28 | JMenuItem sentToAutoFuzzMenuItem = new JMenuItem("Send to AutoFuzz");
29 | sentToAutoFuzzMenuItem.addActionListener(e -> {
30 | try {
31 | autoFuzzService.preFuzz(request);
32 |
33 | ExecutorService executorService = Executors.newSingleThreadExecutor();
34 | executorService.submit(() -> {
35 | Main.API.http().sendRequest(request);
36 | });
37 | //修复第二次发送同一个请求路径会失败,提示interrupted 的情况
38 | executorService.shutdown();
39 |
40 | } catch (Exception ex) {
41 | Main.LOG.logToError("[ERROR] 右键主动fuzz出现异常: " + ex.getMessage());
42 | }
43 | });
44 |
45 | menus.add(sentToAutoFuzzMenuItem);
46 | });
47 | return menus;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/service/AutoFuzzService.java:
--------------------------------------------------------------------------------
1 | package com.chave.service;
2 |
3 | import burp.api.montoya.core.ByteArray;
4 | import burp.api.montoya.http.handler.HttpRequestToBeSent;
5 | import burp.api.montoya.http.handler.TimingData;
6 | import burp.api.montoya.http.message.HttpRequestResponse;
7 | import burp.api.montoya.http.message.params.HttpParameter;
8 | import burp.api.montoya.http.message.params.HttpParameterType;
9 | import burp.api.montoya.http.message.params.ParsedHttpParameter;
10 | import burp.api.montoya.http.message.requests.HttpRequest;
11 | import com.alibaba.fastjson.JSON;
12 | import com.alibaba.fastjson.JSONArray;
13 | import com.alibaba.fastjson.JSONException;
14 | import com.alibaba.fastjson.JSONObject;
15 | import com.chave.Main;
16 | import com.chave.config.Config;
17 | import com.chave.config.UserConfig;
18 | import com.chave.menu.AutoFuzzMenu;
19 | import com.chave.bean.Data;
20 | import com.chave.bean.FuzzRequestItem;
21 | import com.chave.bean.OriginRequestItem;
22 | import com.chave.utils.Util;
23 | import java.io.UnsupportedEncodingException;
24 | import java.math.BigDecimal;
25 | import java.net.MalformedURLException;
26 | import java.net.URL;
27 | import java.nio.charset.StandardCharsets;
28 | import java.util.*;
29 |
30 | public class AutoFuzzService {
31 |
32 | // 做一些准备工作 解析参数 准备待发送请求列表 初始化表格数据
33 | public synchronized void preFuzz(HttpRequest request) throws UnsupportedEncodingException, MalformedURLException {
34 | // 如果没有参数 直接返回 //@d1sbb修改,如果Auth Header没有设置,则不进行fuzz
35 | if (!request.hasParameters() && Data.HEADER_MAP.isEmpty()) {
36 | return;
37 | }
38 |
39 | // 如果是静态资源 直接返回
40 | for (String suffix : Config.STATIC_RESOURCE) {
41 | if (request.pathWithoutQuery().endsWith(suffix)) {
42 | return;
43 | }
44 | }
45 |
46 |
47 | // 创建表格数据
48 | OriginRequestItem originRequestItem = new OriginRequestItem(Data.ORIGIN_REQUEST_TABLE_DATA.size() + 1, request.method(), new URL(request.url()).getHost(), request.pathWithoutQuery(), null, null);
49 | // 创建newRequestToBeSent列表
50 | ArrayList newRequestToBeSentList = new ArrayList<>();
51 | // 保存原请求数据
52 | originRequestItem.setOriginRequest(request);
53 | if (request instanceof HttpRequestToBeSent) { // 如果来源是监听,则存入msgID
54 | HttpRequestToBeSent requestToBeSent = (HttpRequestToBeSent) request;
55 | Data.ORIGIN_REQUEST_TABLE_DATA.put(requestToBeSent.messageId(), originRequestItem);
56 | Data.NEW_REQUEST_TO_BE_SENT_DATA.put(requestToBeSent.messageId(), newRequestToBeSentList);
57 | } else { // 如果来源是插件主动扫描 则存入静态变量递减做id
58 | newRequestToBeSentList.add(request); // 这里先存入原请求等待发送
59 | Data.ORIGIN_REQUEST_TABLE_DATA.put(AutoFuzzMenu.ID, originRequestItem);
60 | Data.NEW_REQUEST_TO_BE_SENT_DATA.put(AutoFuzzMenu.ID, newRequestToBeSentList);
61 | }
62 |
63 |
64 |
65 | // 处理没有json参数的情况
66 | if (!request.hasParameters(HttpParameterType.JSON)) {
67 | // 如果设置了header 自动开始检查
68 | if (Data.HEADER_MAP.size() != 0) {
69 | if (UserConfig.UNAUTH) { // 如果开启未授权检查 则去除对应header
70 | HttpRequest newRequest = request;
71 | for (Map.Entry entry : Data.HEADER_MAP.entrySet()) { // 去除所有设置的header
72 | newRequest = newRequest.withRemovedHeader(entry.getKey());
73 | }
74 | newRequestToBeSentList.add(newRequest);
75 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem("*HEADER*", "*unauth*", null, null, null, null, originRequestItem));
76 | }
77 |
78 | HttpRequest newRequest = request;
79 | for (Map.Entry entry : Data.HEADER_MAP.entrySet()) { // 替换掉所有header
80 | newRequest = newRequest.withHeader(entry.getKey(), entry.getValue());
81 | }
82 | newRequestToBeSentList.add(newRequest);
83 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem("*HEADER*", "*auth*", null, null, null, null, originRequestItem));
84 | }
85 |
86 | // 获取所有请求参数
87 | List parameters = request.parameters();
88 | for (ParsedHttpParameter parameter : parameters) {
89 | // 这里不考虑cookie,multipart,xml
90 | if (parameter.type().equals(HttpParameterType.COOKIE) || parameter.type().equals(HttpParameterType.MULTIPART_ATTRIBUTE) || parameter.type().equals(HttpParameterType.XML) || parameter.type().equals(HttpParameterType.XML_ATTRIBUTE)) {
91 | continue;
92 | }
93 | for (String payload : Data.PAYLOAD_LIST) {
94 | // 创建新请求
95 | HttpRequest newRequest;
96 | if (UserConfig.PARAM_URL_ENCODE) { // 检查urlencode是否开启
97 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
98 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), parameter.value() + Util.urlEncode(payload), parameter.type()));
99 | } else {
100 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), Util.urlEncode(payload), parameter.type()));
101 | }
102 | } else {
103 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
104 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), parameter.value() + payload, parameter.type()));
105 | } else {
106 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), payload, parameter.type()));
107 | }
108 | }
109 |
110 | newRequestToBeSentList.add(newRequest); // 添加待发送请求
111 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem(parameter.name(), payload, null, null, null, null, originRequestItem)); // 添加表格数据
112 | }
113 | }
114 |
115 | } else { // 处理请求中有json参数的情况
116 | // 如果设置了header 自动开始检查
117 | if (Data.HEADER_MAP.size() != 0) {
118 | if (UserConfig.UNAUTH) { // 如果开启未授权检查 则去除对应header
119 | HttpRequest newRequest = request;
120 | for (Map.Entry entry : Data.HEADER_MAP.entrySet()) { // 去除所有设置的header
121 | newRequest = newRequest.withRemovedHeader(entry.getKey());
122 | }
123 | newRequestToBeSentList.add(newRequest);
124 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem("*HEADER*", "*unauth*", null, null, null, null, originRequestItem));
125 | }
126 |
127 | HttpRequest newRequest = request;
128 | for (Map.Entry entry : Data.HEADER_MAP.entrySet()) { // 替换掉所有header
129 | newRequest = newRequest.withHeader(entry.getKey(), entry.getValue());
130 | }
131 | newRequestToBeSentList.add(newRequest);
132 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem("*HEADER*", "*auth*", null, null, null, null, originRequestItem));
133 | }
134 |
135 | // 先获取普通参数
136 | List parameters = new ArrayList<>();
137 | for (ParsedHttpParameter parameter : request.parameters()) {
138 | if (parameter.type().equals(HttpParameterType.URL) || parameter.type().equals(HttpParameterType.BODY)) {
139 | parameters.add(parameter);
140 | }
141 | }
142 | // 先将普通参数的新请求添加完成
143 | for (ParsedHttpParameter parameter : parameters) {
144 | for (String payload : Data.PAYLOAD_LIST) {
145 | HttpRequest newRequest;
146 | if (UserConfig.PARAM_URL_ENCODE) { // 检查urlencode是否开启
147 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
148 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), parameter.value() + Util.urlEncode(payload), parameter.type()));
149 | } else {
150 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), Util.urlEncode(payload), parameter.type()));
151 | }
152 | } else {
153 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
154 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), parameter.value() + payload, parameter.type()));
155 | } else {
156 | newRequest = request.withParameter(HttpParameter.parameter(parameter.name(), payload, parameter.type()));
157 | }
158 |
159 | }
160 |
161 | newRequestToBeSentList.add(newRequest); // 添加待发送请求
162 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem(parameter.name(), payload, null, null, null, null, originRequestItem)); // 添加表格数据
163 | }
164 | }
165 |
166 | // @d1sbb修改,修复中文乱码,获取json字符串
167 | byte[] bodyBytes = request.body().getBytes();
168 | String json = new String(bodyBytes, StandardCharsets.UTF_8);
169 |
170 | // 这里开始处理json
171 | Object jsonObject = null;
172 | try {
173 | jsonObject = JSON.parse(json);
174 | } catch (JSONException jsonException) {
175 | jsonObject = JSON.parse("{}"); // 如果解析异常就替换为一个空的json串
176 | Main.LOG.logToError("json解析异常" + jsonException.getCause());
177 | }
178 |
179 | // jsonObject 解析成功
180 | if (!jsonObject.equals(JSON.parse("{}"))) {
181 | LinkedHashMap, HashMap> result = new LinkedHashMap<>(); // 用于存放要替换的value
182 | parseJsonParam(null, jsonObject, result); // 解析json获取所有要替换的value
183 |
184 |
185 | for (Map.Entry, HashMap> resultEntry : result.entrySet()) {
186 | HashMap resultKey = resultEntry.getKey();
187 |
188 | for (Integer integer : resultEntry.getKey().keySet()) {
189 | for (String payload : Data.PAYLOAD_LIST) {
190 | jsonObject = JSON.parse(json); // 重新赋值一个未修改过的json
191 | String newJsonBody = updateJsonValue(integer, payload, jsonObject, result).get("json").toString(); // 生成新的payload
192 | //@d1sbb修改,修复json请求包中如果有中文乱码的情况
193 | ByteArray utf8Body = ByteArray.byteArray(newJsonBody.getBytes(StandardCharsets.UTF_8));
194 | HttpRequest newRequest = request.withBody(utf8Body);
195 | newRequestToBeSentList.add(newRequest); // 添加到待发送请求
196 | originRequestItem.getFuzzRequestArrayList().add(new FuzzRequestItem((String) resultKey.get(integer), payload, null, null, null, null, originRequestItem)); // 添加表格数据
197 | }
198 | }
199 |
200 | }
201 | }
202 | }
203 | }
204 |
205 |
206 | // 发送请求
207 | public void startFuzz(int msgId) {
208 | // 如果记录中没有的 直接return掉
209 | if (!Data.NEW_REQUEST_TO_BE_SENT_DATA.containsKey(msgId)) {
210 | return;
211 | }
212 | ArrayList requestToBeSentList = Data.NEW_REQUEST_TO_BE_SENT_DATA.get(msgId);
213 | ArrayList fuzzRequestItemArrayList = Data.ORIGIN_REQUEST_TABLE_DATA.get(msgId).getFuzzRequestArrayList();
214 |
215 | int i = 0;
216 |
217 | if (msgId < 0) {
218 | HttpRequest originRequest = requestToBeSentList.get(0);
219 | HttpRequestResponse httpRequestResponse = Main.API.http().sendRequest(originRequest);
220 |
221 | OriginRequestItem originRequestItem = Data.ORIGIN_REQUEST_TABLE_DATA.get(msgId);
222 | originRequestItem.setOriginResponse(httpRequestResponse.response());
223 | originRequestItem.setResponseLength(httpRequestResponse.response().toString().length() + "");
224 | originRequestItem.setResponseCode(httpRequestResponse.response().statusCode() + "");
225 |
226 | requestToBeSentList.remove(0);
227 | }
228 |
229 | for (HttpRequest request : requestToBeSentList) {
230 | HttpRequestResponse httpRequestResponse = Main.API.http().sendRequest(request);
231 | FuzzRequestItem fuzzRequestItem = fuzzRequestItemArrayList.get(i);
232 | fuzzRequestItem.setFuzzRequestResponse(httpRequestResponse); // 与table数据关联
233 |
234 | // 获取返回包长度信息
235 | String responseLength = httpRequestResponse.response().toString().length() + "";
236 | int lengthChange = httpRequestResponse.response().toString().length() - Integer.parseInt(fuzzRequestItem.getOriginRequestItem().getResponseLength());
237 | fuzzRequestItem.setResponseLength(responseLength);
238 | fuzzRequestItem.setResponseLengthChange((lengthChange > 0 ? "+" + lengthChange : String.valueOf(lengthChange)));
239 | fuzzRequestItem.setResponseCode(httpRequestResponse.response().statusCode() + "");
240 |
241 | // 获取返回包时间信息
242 | Optional timingOpt = httpRequestResponse.timingData();
243 | if (timingOpt.isPresent()) {
244 | long timeInSeconds = timingOpt.get().timeBetweenRequestSentAndEndOfResponse().toMillis();
245 | fuzzRequestItem.setResponseTime(timeInSeconds + "");
246 | } else {
247 | Main.LOG.logToError("[ERROR] 未获取到 timingData");
248 | }
249 | i++;
250 | }
251 |
252 | Data.NEW_REQUEST_TO_BE_SENT_DATA.remove(msgId);
253 |
254 | }
255 |
256 | // 用于解析json中所有需要fuzz的value
257 | public static void parseJsonParam(Object jsonKey, Object jsonObj, LinkedHashMap result) {
258 | // 递归处理不同的 JSON 类型
259 | if (jsonObj instanceof JSONObject) {
260 | JSONObject jsonObject = (JSONObject) jsonObj;
261 | for (Map.Entry entry : jsonObject.entrySet()) {
262 | Object value = entry.getValue();
263 | Object key = entry.getKey();
264 |
265 | parseJsonParam(key, value, result);
266 | }
267 | } else if (jsonObj instanceof JSONArray) {
268 | JSONArray jsonArray = (JSONArray) jsonObj;
269 | for (Object value : jsonArray) {
270 | parseJsonParam(jsonKey, value, result);
271 | }
272 | } else {
273 | // 每次遇到一个基本类型的值,保存它的类型和具体值
274 | HashMap valueMap = new HashMap<>();
275 | HashMap keyMap = new HashMap<>();
276 | if (jsonObj instanceof String) {
277 | valueMap.put("String", jsonObj);
278 | } else if (jsonObj instanceof BigDecimal) {
279 | valueMap.put("BigDecimal", jsonObj);
280 | } else if (jsonObj instanceof Integer) {
281 | valueMap.put("Integer", jsonObj);
282 | } else if (jsonObj instanceof Boolean) {
283 | valueMap.put("Boolean", jsonObj);
284 | } else if (jsonObj instanceof Long) {
285 | valueMap.put("Long", jsonObj);
286 | }
287 | keyMap.put(result.size() + 1, jsonKey);
288 |
289 | result.put(keyMap, valueMap);
290 | }
291 | }
292 |
293 | public static HashMap updateJsonValue(int i, String payload, Object jsonObj, LinkedHashMap, HashMap> result) {
294 | HashMap newJsonStringMap = new HashMap();
295 | newJsonStringMap.put("isModified", false);
296 | newJsonStringMap.put("json", jsonObj);
297 | // 递归处理不同的 JSON 类型
298 | if (jsonObj instanceof JSONObject) {
299 | JSONObject jsonObject = (JSONObject) jsonObj;
300 | // 传进来的是json 遍历每个entry
301 | for (Map.Entry entry : ((JSONObject) jsonObj).entrySet()) {
302 | Object value = entry.getValue();
303 | Object key = entry.getKey();
304 | // 如果entry是基础类型 则开始替换payload
305 | if (!(value instanceof JSONObject) && !(value instanceof JSONArray)) {
306 | Object resultValue = null;
307 | Object resultValueType = null;
308 | Object resultKey = null;
309 |
310 | for (Map.Entry, HashMap> resultEntry : result.entrySet()) {
311 | for (Map.Entry keyEntry : resultEntry.getKey().entrySet()) {
312 | if (keyEntry.getKey().equals(i)) {
313 | for (Map.Entry valueEntry : resultEntry.getValue().entrySet()) {
314 | resultKey = keyEntry.getValue();
315 | resultValue = valueEntry.getValue();
316 | resultValueType = valueEntry.getKey();
317 | }
318 | }
319 | }
320 | }
321 |
322 | // 如果与resultValue相同 key相同 那么就是需要修改的值
323 | if (resultValue.equals(value) && resultKey.equals(key)) {
324 | if (resultValueType.equals("BigDecimal") || resultValueType.equals("Integer") || resultValueType.equals("Long")) {
325 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
326 | jsonObject.put(entry.getKey(), Util.isNumber(entry.getValue() + payload));
327 | } else {
328 | jsonObject.put(entry.getKey(), Util.isNumber(payload));
329 | }
330 |
331 | } else if (resultValueType.equals("Boolean")) {
332 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
333 | jsonObject.put(entry.getKey(), Util.isBoolean(entry.getValue() + payload));
334 | } else {
335 | jsonObject.put(entry.getKey(), Util.isBoolean(payload));
336 | }
337 | } else {
338 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
339 | jsonObject.put(entry.getKey(), entry.getValue() + payload);
340 | } else {
341 | jsonObject.put(entry.getKey(), payload);
342 | }
343 | }
344 |
345 | newJsonStringMap.put("isModified", true);
346 | newJsonStringMap.put("json", jsonObject);
347 | return newJsonStringMap;
348 | }
349 | } else {
350 | // 如果依然是json嵌套 那么递归调用
351 | HashMap tmpResult = updateJsonValue(i, payload, value, result);
352 | jsonObject.put(entry.getKey(), tmpResult.get("json"));
353 | if ((boolean) tmpResult.get("isModified")) {
354 | newJsonStringMap.put("json", jsonObject);
355 | return newJsonStringMap;
356 | }
357 | }
358 | }
359 | } else if (jsonObj instanceof JSONArray) { // 用于处理jsonArray类型
360 | JSONArray jsonArray = (JSONArray) jsonObj;
361 |
362 | for (int index = 0; index < jsonArray.size(); index++) {
363 | Object value = jsonArray.get(index);
364 | // 如果数组中这个元素是基本类型 开始准备替换
365 | if (!(value instanceof JSONObject) && !(value instanceof JSONArray)) {
366 | Object resultValue = null;
367 | Object resultValueType = null;
368 |
369 | for (Map.Entry, HashMap> resultEntry : result.entrySet()) {
370 | for (Map.Entry keyEntry : resultEntry.getKey().entrySet()) {
371 | if (keyEntry.getKey().equals(i)) {
372 | for (Map.Entry valueEntry : resultEntry.getValue().entrySet()) {
373 | resultValue = valueEntry.getValue();
374 | resultValueType = valueEntry.getKey();
375 | }
376 | }
377 | }
378 | }
379 |
380 | // 如果与resultValue相同 那么就是需要修改的值 同时保证数组挨个替换
381 | if (resultValue.equals(value) && i == jsonArray.size() + index + 1) {
382 | if (resultValueType.equals("BigDecimal") || resultValueType.equals("Integer") || resultValueType.equals("Long")) {
383 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
384 | jsonArray.set(index, Util.isNumber(value + payload));
385 | } else {
386 | jsonArray.set(index, Util.isNumber(payload));
387 | }
388 | } else if (resultValueType.equals("Boolean")) {
389 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
390 | jsonArray.set(index, Util.isBoolean(value + payload));
391 | } else {
392 | jsonArray.set(index, Util.isBoolean(payload));
393 | }
394 | } else {
395 | if (UserConfig.APPEND_MOD && payload.length() != 0) {
396 | jsonArray.set(index, value + payload);
397 | } else {
398 | jsonArray.set(index, payload);
399 | }
400 | }
401 |
402 | newJsonStringMap.put("isModified", true);
403 | newJsonStringMap.put("json", jsonArray);
404 | return newJsonStringMap;
405 | }
406 | } else {
407 | // 如果依然是json嵌套 那么递归调用
408 | HashMap tmpResult = updateJsonValue(i, payload, value, result);
409 | jsonArray.set(index, tmpResult.get("json"));
410 | if ((boolean) tmpResult.get("isModified")) {
411 | newJsonStringMap.put("json", jsonArray);
412 | return newJsonStringMap;
413 | }
414 | }
415 | }
416 |
417 | }
418 |
419 | // 用于嵌套的jsonObject类型没有做任何修改时 返回原jsonObject
420 | return newJsonStringMap;
421 | }
422 | }
423 |
--------------------------------------------------------------------------------
/src/main/java/com/chave/ui/MainUI.java:
--------------------------------------------------------------------------------
1 | package com.chave.ui;
2 |
3 | import burp.api.montoya.ui.editor.EditorOptions;
4 | import burp.api.montoya.ui.editor.HttpRequestEditor;
5 | import burp.api.montoya.ui.editor.HttpResponseEditor;
6 | import com.chave.Main;
7 | import com.chave.config.UserConfig;
8 | import com.chave.handler.AutoFuzzHandler;
9 | import com.chave.bean.*;
10 | import com.chave.utils.Util;
11 | import com.chave.utils.YamlUtil;
12 | import lombok.Data;
13 |
14 | import javax.swing.*;
15 | import javax.swing.border.TitledBorder;
16 | import javax.swing.table.*;
17 | import java.awt.*;
18 | import java.awt.event.*;
19 | import java.lang.reflect.Field;
20 | import java.lang.reflect.Method;
21 | import java.nio.charset.StandardCharsets;
22 | import java.util.*;
23 | import java.util.concurrent.LinkedBlockingQueue;
24 | import java.util.concurrent.ThreadPoolExecutor;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | import static com.chave.bean.Data.*;
28 |
29 | @Data
30 | public class MainUI {
31 | // 创建组件
32 |
33 | private JRadioButton blackListRadioButton;
34 | private JRadioButton whiteListRadioButton;
35 | private ButtonGroup blackOrWhiteGroup;
36 |
37 | private JPanel leftPanel;
38 | private JPanel rightPanel;
39 | private JPanel rightTopPanel;
40 | private JPanel rightBottomPanel;
41 | private JPanel turnOnPanel;
42 | private JPanel domainOperatePanel;
43 | private JPanel domainMainPanel;
44 | private JPanel listenProxyPanel;
45 | private JPanel listenRepeterPanel;
46 | private JPanel basicTitlePanel;
47 | private JPanel domainTitlePanel;
48 | private JPanel payloadTitlePanel;
49 | private JPanel languageSupportPanel;
50 | private JPanel cleanRequestListPanel;
51 | private JPanel payloadMainPanel;
52 | private JPanel payloadOperatePanel;
53 | private JPanel authHeaderTitlePanel;
54 | private JPanel authHeaderMainPanel;
55 | private JPanel authHeaderOperatePanel;
56 | private JPanel searchPanel;
57 | private JPanel tablePanel;
58 | private JSplitPane mainSplitPane;
59 | private JSplitPane rightSplitPane;
60 | private JTable fuzzRequestItemTable;
61 | private JTable originRequestItemTable;
62 | private JTable domainTable;
63 | private JTable payloadTable;
64 | private JTable authHeaderTable;
65 | private JButton addDomainButton;
66 | private JButton removeDomainButton;
67 | private JButton cleanRequestItemButton;
68 | private JButton editDomainButton;
69 | private JButton addPayloadButton;
70 | private JButton editPayloadButton;
71 | private JButton removePayloadButton;
72 | private JButton searchButton;
73 | private JButton cleanSearchResultButton;
74 | private JButton addAuthHeaderButton;
75 | private JButton editAuthHeaderButton;
76 | private JButton removeAuthHeaderButton;
77 | private JCheckBox turnOnCheckBox;
78 | private JCheckBox listenProxyCheckBox;
79 | private JCheckBox listenRepeterCheckBox;
80 | private JCheckBox includeSubDomainCheckBox;
81 | private JCheckBox emptyParamCheckBox;
82 | private JCheckBox paramURLEncodeCheckBox;
83 | private JCheckBox unauthCheckBox;
84 | private JCheckBox appendModCheckBox;
85 | private JLabel basicTitleLabel;
86 | private JLabel domainTitleLabel;
87 | private JLabel payloadTitleLabel;
88 | private JLabel authHeaderTitleLabel;
89 | private JTextField searchTextField;
90 | private JComboBox searchScopeComboBox;
91 | private JComboBox languageSupportComboBox;
92 | private HttpRequestEditor requestEditor;
93 | private HttpResponseEditor responseEditor;
94 | private HashMap> highlightMap;
95 | private String[] originRequestItemTableColumnName;
96 | private String[] fuzzRequestItemTableColumnName;
97 |
98 | private LinkedHashMap languageSupportMap = new LinkedHashMap();
99 |
100 | // 多语言支持的组件
101 | private ArrayList simplifiedChineseLib = new ArrayList<>();
102 | private ArrayList englishLib = new ArrayList<>();
103 | private ArrayList langSupportComponent = new ArrayList<>();
104 | private ResourceBundle bundle;
105 |
106 |
107 |
108 | {
109 | if (UserConfig.LANGUAGE.equals(Language.SIMPLIFIED_CHINESE)) {
110 | bundle = ResourceBundle.getBundle("lang", new Locale("zh", "CN"));
111 | } else if (UserConfig.LANGUAGE.equals(Language.ENGLISH)) {
112 | bundle = ResourceBundle.getBundle("lang", new Locale("en", "US"));
113 | }
114 |
115 | langSupportComponent.add("basicTitleLabel");
116 | langSupportComponent.add("turnOnCheckBox");
117 | langSupportComponent.add("listenProxyCheckBox");
118 | langSupportComponent.add("listenRepeterCheckBox");
119 | langSupportComponent.add("cleanRequestItemButton");
120 | langSupportComponent.add("domainTitleLabel");
121 | langSupportComponent.add("blackListRadioButton");
122 | langSupportComponent.add("whiteListRadioButton");
123 | langSupportComponent.add("addDomainButton");
124 | langSupportComponent.add("editDomainButton");
125 | langSupportComponent.add("removeDomainButton");
126 | langSupportComponent.add("includeSubDomainCheckBox");
127 | langSupportComponent.add("payloadTitleLabel");
128 | langSupportComponent.add("addPayloadButton");
129 | langSupportComponent.add("editPayloadButton");
130 | langSupportComponent.add("removePayloadButton");
131 | langSupportComponent.add("appendModCheckBox");
132 | langSupportComponent.add("emptyParamCheckBox");
133 | langSupportComponent.add("paramURLEncodeCheckBox");
134 | langSupportComponent.add("authHeaderTitleLabel");
135 | langSupportComponent.add("addAuthHeaderButton");
136 | langSupportComponent.add("editAuthHeaderButton");
137 | langSupportComponent.add("removeAuthHeaderButton");
138 | langSupportComponent.add("unauthCheckBox");
139 | langSupportComponent.add("searchButton");
140 | langSupportComponent.add("cleanSearchResultButton");
141 | }
142 |
143 | public MainUI() {
144 | init();
145 | }
146 |
147 | private void init() {
148 | // 初始化各个组件
149 | blackListRadioButton = new JRadioButton();
150 | whiteListRadioButton = new JRadioButton();
151 | blackOrWhiteGroup = new ButtonGroup();
152 | leftPanel = new JPanel();
153 | rightPanel = new JPanel();
154 | rightTopPanel = new JPanel();
155 | rightBottomPanel = new JPanel();
156 | turnOnPanel = new JPanel();
157 | domainOperatePanel = new JPanel();
158 | domainMainPanel = new JPanel();
159 | listenProxyPanel = new JPanel();
160 | listenRepeterPanel = new JPanel();
161 | languageSupportPanel = new JPanel();
162 | cleanRequestListPanel = new JPanel();
163 | basicTitlePanel = new JPanel();
164 | domainTitlePanel = new JPanel();
165 | payloadTitlePanel = new JPanel();
166 | payloadMainPanel = new JPanel();
167 | payloadOperatePanel = new JPanel();
168 | authHeaderTitlePanel = new JPanel();
169 | authHeaderMainPanel = new JPanel();
170 | authHeaderOperatePanel = new JPanel();
171 | searchPanel = new JPanel();
172 | tablePanel = new JPanel();
173 | turnOnCheckBox = new JCheckBox();
174 | listenProxyCheckBox = new JCheckBox();
175 | listenRepeterCheckBox = new JCheckBox();
176 | emptyParamCheckBox = new JCheckBox();
177 | paramURLEncodeCheckBox = new JCheckBox();
178 | includeSubDomainCheckBox = new JCheckBox();
179 | unauthCheckBox = new JCheckBox();
180 | appendModCheckBox = new JCheckBox();
181 | addDomainButton = new JButton();
182 | editDomainButton = new JButton();
183 | removeDomainButton = new JButton();
184 | cleanRequestItemButton = new JButton();
185 | addPayloadButton = new JButton();
186 | editPayloadButton = new JButton();
187 | removePayloadButton = new JButton();
188 | searchButton = new JButton();
189 | cleanSearchResultButton = new JButton();
190 | addAuthHeaderButton = new JButton();
191 | editAuthHeaderButton = new JButton();
192 | removeAuthHeaderButton = new JButton();
193 | searchTextField = new JTextField();
194 |
195 | // 初始化searchscope下拉框
196 | String[] searchScopeOptions = {"request", "response"};
197 | searchScopeComboBox = new JComboBox<>(searchScopeOptions);
198 | searchScopeComboBox.setSelectedItem("request");
199 | UserConfig.SEARCH_SCOPE = SearchScope.REQUEST;
200 |
201 | // 初始化多语言下拉框
202 | String[] languageSupportOptions = {"简体中文", "English"};
203 | languageSupportComboBox = new JComboBox<>(languageSupportOptions);
204 | languageSupportComboBox.setSelectedItem(bundle.getString("language"));
205 |
206 | // 初始化标题栏
207 | basicTitleLabel = new JLabel();
208 | domainTitleLabel = new JLabel();
209 | payloadTitleLabel = new JLabel();
210 | authHeaderTitleLabel = new JLabel();
211 |
212 | // 初始化request/response展示框
213 | requestEditor = Main.API.userInterface().createHttpRequestEditor(EditorOptions.READ_ONLY);
214 | responseEditor = Main.API.userInterface().createHttpResponseEditor(EditorOptions.READ_ONLY);
215 |
216 | // 用于存放需要高亮的条目
217 | highlightMap = new HashMap<>(); // 这里防止空指针 后面还会重新初始化
218 |
219 | loadLanguageSupportComponent();
220 |
221 | /**
222 | * **自定义 TableModel,确保 ID 列按数值大小排序**
223 | */
224 | class SortedTableModel extends DefaultTableModel {
225 | public SortedTableModel(Object[] columnNames, int rowCount) {
226 | super(columnNames, rowCount);
227 | }
228 |
229 | @Override
230 | public void addRow(Object[] rowData) {
231 | super.addRow(rowData);
232 | sortData(); // 添加数据后自动排序
233 | fireTableDataChanged(); // 通知 UI 更新
234 | }
235 |
236 | @Override
237 | public void removeRow(int row) {
238 | super.removeRow(row);
239 | sortData(); // 删除数据后自动排序
240 | fireTableDataChanged(); // 通知 UI 更新
241 | }
242 |
243 | @Override
244 | public void fireTableDataChanged() {
245 | sortData(); // 确保每次数据更新后排序
246 | super.fireTableDataChanged();
247 | }
248 |
249 | /**
250 | * **对数据按 ID(第 0 列)排序**
251 | */
252 | private void sortData() {
253 | // 获取数据
254 | Vector dataVector = getDataVector();
255 | // 按照 ID 列(第 0 列)升序排序
256 | dataVector.sort((v1, v2) -> {
257 | Integer id1 = (Integer) v1.get(0); // 获取第 0 列 ID,强制转换为 Integer
258 | Integer id2 = (Integer) v2.get(0); // 获取第 0 列 ID,强制转换为 Integer
259 | return id1.compareTo(id2); // 按升序排序
260 | });
261 | }
262 | }
263 |
264 | // 创建左边表格
265 | String[] originRequestItemTableColumnName = {"#", "Method", "Host", "Path", "Length", "Status"};
266 | SortedTableModel originRequestItemTableModel = new SortedTableModel(originRequestItemTableColumnName, 0);
267 | originRequestItemTable = new JTable(originRequestItemTableModel);
268 | // 设置列默认宽度
269 | originRequestItemTable.getColumnModel().getColumn(0).setPreferredWidth(20);
270 | originRequestItemTable.getColumnModel().getColumn(1).setPreferredWidth(30);
271 | originRequestItemTable.getColumnModel().getColumn(4).setPreferredWidth(50);
272 | originRequestItemTable.getColumnModel().getColumn(5).setPreferredWidth(20);
273 | // 禁止整个表格编辑
274 | originRequestItemTable.setDefaultEditor(Object.class, null);
275 | // 创建表格滚动面板
276 | JScrollPane originRequestItemTableScrollPane = new JScrollPane(originRequestItemTable);
277 |
278 |
279 | // 创建右边表格
280 | String[] fuzzRequestItemTableColumnName = {"Param", "Payload", "Length", "Change", "Status", "Time(ms)"};
281 | DefaultTableModel fuzzRequestItemTableModel = new DefaultTableModel(fuzzRequestItemTableColumnName, 0) {
282 | @Override
283 | public boolean isCellEditable(int row, int column) {
284 | return true;
285 | }
286 | };
287 | fuzzRequestItemTable = new JTable(fuzzRequestItemTableModel);
288 | // 禁止整个表格编辑
289 | fuzzRequestItemTable.setDefaultEditor(Object.class, null);
290 | // 创建表格滚动面板
291 | JScrollPane fuzzRequestItemTableScrollPane = new JScrollPane(fuzzRequestItemTable);
292 |
293 |
294 | // 绘制左侧面板(用户配置面板)
295 | // 设置左边主面板布局
296 | BoxLayout leftBoxLayout = new BoxLayout(leftPanel, BoxLayout.Y_AXIS);
297 | leftPanel.setLayout(leftBoxLayout);
298 | // 设置四个标题label
299 | BoxLayout basicTitleLayout = new BoxLayout(basicTitlePanel, BoxLayout.X_AXIS);
300 | BoxLayout domainTitleLayout = new BoxLayout(domainTitlePanel, BoxLayout.X_AXIS);
301 | BoxLayout payloadTitleLayout = new BoxLayout(payloadTitlePanel, BoxLayout.X_AXIS);
302 | BoxLayout authHeaderTitleLayout = new BoxLayout(authHeaderTitlePanel, BoxLayout.X_AXIS);
303 | basicTitlePanel.setLayout(basicTitleLayout);
304 | domainTitlePanel.setLayout(domainTitleLayout);
305 | payloadTitlePanel.setLayout(payloadTitleLayout);
306 | authHeaderTitlePanel.setLayout(authHeaderTitleLayout);
307 | basicTitlePanel.add(basicTitleLabel, Component.CENTER_ALIGNMENT);
308 | domainTitlePanel.add(domainTitleLabel, Component.CENTER_ALIGNMENT);
309 | payloadTitlePanel.add(payloadTitleLabel, Component.CENTER_ALIGNMENT);
310 | authHeaderTitlePanel.add(authHeaderTitleLabel, Component.CENTER_ALIGNMENT);
311 | // 设置复选框默认勾选状态 居中放置
312 | BoxLayout turnOnLayout = new BoxLayout(turnOnPanel, BoxLayout.X_AXIS);
313 | turnOnPanel.setLayout(turnOnLayout);
314 | turnOnCheckBox.setSelected(UserConfig.TURN_ON);
315 | turnOnPanel.add(Box.createHorizontalStrut(100));
316 | turnOnPanel.add(turnOnCheckBox);
317 | turnOnPanel.add(Box.createHorizontalGlue());
318 | turnOnPanel.setMaximumSize(new Dimension(20000, 22));
319 | // 选择监听proxy 监听repeter 居中放置
320 | BoxLayout listenProxyLayout = new BoxLayout(listenProxyPanel, BoxLayout.X_AXIS);
321 | listenProxyPanel.setLayout(listenProxyLayout);
322 | listenProxyPanel.add(Box.createHorizontalStrut(100));
323 | listenProxyCheckBox.setSelected(UserConfig.LISTEN_PROXY);
324 | listenProxyPanel.add(listenProxyCheckBox);
325 | listenProxyPanel.add(Box.createHorizontalGlue());
326 | listenProxyPanel.setMaximumSize(new Dimension(20000, 22));
327 | BoxLayout listenRepeterLayout = new BoxLayout(listenRepeterPanel, BoxLayout.X_AXIS);
328 | listenRepeterPanel.setLayout(listenRepeterLayout);
329 | listenRepeterPanel.add(Box.createHorizontalStrut(100));
330 | listenRepeterCheckBox.setSelected(UserConfig.LISTEN_REPETER);
331 | listenRepeterPanel.add(listenRepeterCheckBox);
332 | listenRepeterPanel.add(Box.createHorizontalGlue());
333 | listenRepeterPanel.setMaximumSize(new Dimension(20000, 22));
334 | // 切换语言
335 | BoxLayout languageSupportPanelLayout = new BoxLayout(languageSupportPanel, BoxLayout.X_AXIS);
336 | languageSupportPanel.setLayout(languageSupportPanelLayout);
337 | languageSupportPanel.add(Box.createHorizontalStrut(100));
338 | languageSupportPanel.add(languageSupportComboBox);
339 | languageSupportPanel.add(Box.createHorizontalStrut(105));
340 | languageSupportPanel.setMaximumSize(new Dimension(20000, 22));
341 | // 清空请求列表
342 | BoxLayout cleanRequestListLayout = new BoxLayout(cleanRequestListPanel, BoxLayout.X_AXIS);
343 | cleanRequestListPanel.setLayout(cleanRequestListLayout);
344 | cleanRequestListPanel.add(Box.createHorizontalStrut(100));
345 | cleanRequestListPanel.add(cleanRequestItemButton);
346 | cleanRequestListPanel.add(Box.createHorizontalGlue());
347 | cleanRequestListPanel.setMaximumSize(new Dimension(20000, 22));
348 | // 域名配置部分绘制
349 | // 用户操作部分
350 | BoxLayout domainMainLayout = new BoxLayout(domainMainPanel, BoxLayout.X_AXIS);
351 | BoxLayout domainOperateLayout = new BoxLayout(domainOperatePanel, BoxLayout.Y_AXIS);
352 | domainMainPanel.setLayout(domainMainLayout);
353 | domainOperatePanel.setLayout(domainOperateLayout);
354 | // 初始化黑白名单勾选情况
355 | blackOrWhiteGroup.add(blackListRadioButton);
356 | blackOrWhiteGroup.add(whiteListRadioButton);
357 | blackListRadioButton.setSelected(UserConfig.BLACK_OR_WHITE_CHOOSE);
358 | whiteListRadioButton.setSelected(!UserConfig.BLACK_OR_WHITE_CHOOSE);
359 | // 初始化包含子域名勾选情况
360 | includeSubDomainCheckBox.setSelected(UserConfig.INCLUDE_SUBDOMAIN);
361 | // 向面板中添加各个组件
362 | domainOperatePanel.add(blackListRadioButton, Component.CENTER_ALIGNMENT);
363 | domainOperatePanel.add(Box.createVerticalStrut(5));
364 | domainOperatePanel.add(whiteListRadioButton, Component.CENTER_ALIGNMENT);
365 | domainOperatePanel.add(Box.createVerticalStrut(5));
366 | domainOperatePanel.add(addDomainButton, Component.CENTER_ALIGNMENT);
367 | domainOperatePanel.add(Box.createVerticalStrut(5));
368 | domainOperatePanel.add(editDomainButton, Component.CENTER_ALIGNMENT);
369 | domainOperatePanel.add(Box.createVerticalStrut(5));
370 | domainOperatePanel.add(removeDomainButton, Component.CENTER_ALIGNMENT);
371 | domainOperatePanel.add(Box.createVerticalStrut(5));
372 | domainOperatePanel.add(includeSubDomainCheckBox, Component.CENTER_ALIGNMENT);
373 | domainMainPanel.add(Box.createHorizontalStrut(5));
374 | domainMainPanel.add(domainOperatePanel);
375 | // 初始化域名表格
376 | String[] domainTableColumnName = {"Domain"};
377 | DefaultTableModel domainModel = new DefaultTableModel(domainTableColumnName, 0) {
378 | @Override
379 | public boolean isCellEditable(int row, int column) {
380 | return true;
381 | }
382 | };
383 | domainTable = new JTable(domainModel);
384 | // 支持多行选中
385 | domainTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
386 | // 禁止表格编辑
387 | domainTable.setDefaultEditor(Object.class, null);
388 | // 创建表格滚动面板
389 | JScrollPane domainTableScrollPane = new JScrollPane(domainTable);
390 | domainMainPanel.add(Box.createHorizontalStrut(5));
391 | domainMainPanel.add(domainTableScrollPane);
392 | domainMainPanel.add(Box.createHorizontalStrut(5));
393 | // 初始化配置文件中的domain
394 | Util.flushConfigTable("domain", domainTable);
395 |
396 |
397 | // payload配置部分绘制
398 | // 用户操作部分
399 | BoxLayout payloadMainLayout = new BoxLayout(payloadMainPanel, BoxLayout.X_AXIS);
400 | BoxLayout payloadOperateLayout = new BoxLayout(payloadOperatePanel, BoxLayout.Y_AXIS);
401 | payloadMainPanel.setLayout(payloadMainLayout);
402 | payloadOperatePanel.setLayout(payloadOperateLayout);
403 | // 根据此时payloadlist中是否存在置空参数判断是否勾选
404 | if (PAYLOAD_LIST.size() != 0 && PAYLOAD_LIST.get(0).equals("")) {
405 | emptyParamCheckBox.setSelected(true);
406 | }
407 | // 设置追加模式勾选情况
408 | appendModCheckBox.setSelected(UserConfig.APPEND_MOD);
409 | // 设置urlencode勾选情况
410 | paramURLEncodeCheckBox.setSelected(UserConfig.PARAM_URL_ENCODE);
411 | // payload用户操作部分添加组件
412 | payloadOperatePanel.add(addPayloadButton, Component.CENTER_ALIGNMENT);
413 | payloadOperatePanel.add(Box.createVerticalStrut(10));
414 | payloadOperatePanel.add(editPayloadButton, Component.CENTER_ALIGNMENT);
415 | payloadOperatePanel.add(Box.createVerticalStrut(10));
416 | payloadOperatePanel.add(removePayloadButton, Component.CENTER_ALIGNMENT);
417 | payloadOperatePanel.add(Box.createVerticalStrut(10));
418 | payloadOperatePanel.add(appendModCheckBox, Component.CENTER_ALIGNMENT);
419 | payloadOperatePanel.add(Box.createVerticalStrut(10));
420 | payloadOperatePanel.add(emptyParamCheckBox, Component.CENTER_ALIGNMENT);
421 | payloadOperatePanel.add(Box.createVerticalStrut(10));
422 | payloadOperatePanel.add(paramURLEncodeCheckBox, Component.CENTER_ALIGNMENT);
423 | payloadMainPanel.add(Box.createHorizontalStrut(5));
424 | payloadMainPanel.add(payloadOperatePanel);
425 | // 初始化payload表格
426 | String[] payloadTableColumnName = {"Payload"};
427 | DefaultTableModel payloadModel = new DefaultTableModel(payloadTableColumnName, 0) {
428 | @Override
429 | public boolean isCellEditable(int row, int column) {
430 | return true;
431 | }
432 | };
433 | payloadTable = new JTable(payloadModel);
434 | // 支持多行选中
435 | payloadTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
436 | // 禁止表格编辑
437 | payloadTable.setDefaultEditor(Object.class, null);
438 | // 创建表格滚动面板
439 | JScrollPane payloadTableScrollPane = new JScrollPane(payloadTable);
440 | // 添加滚动面板
441 | payloadMainPanel.add(Box.createHorizontalStrut(11));
442 | payloadMainPanel.add(payloadTableScrollPane, Component.CENTER_ALIGNMENT);
443 | payloadMainPanel.add(Box.createHorizontalStrut(5));
444 | // 初始化配置文件中的payload
445 | Util.flushConfigTable("payload", payloadTable);
446 |
447 |
448 | // Auth Header配置部分绘制
449 | // 用户操作部分
450 | BoxLayout authHeaderMainLayout = new BoxLayout(authHeaderMainPanel, BoxLayout.X_AXIS);
451 | BoxLayout authHeaderOperateLayout = new BoxLayout(authHeaderOperatePanel, BoxLayout.Y_AXIS);
452 | authHeaderMainPanel.setLayout(authHeaderMainLayout);
453 | authHeaderOperatePanel.setLayout(authHeaderOperateLayout);
454 | // 设置未授权访问勾选情况
455 | unauthCheckBox.setSelected(UserConfig.UNAUTH);
456 | authHeaderOperatePanel.add(addAuthHeaderButton, Component.CENTER_ALIGNMENT);
457 | authHeaderOperatePanel.add(Box.createVerticalStrut(10));
458 | authHeaderOperatePanel.add(editAuthHeaderButton, Component.CENTER_ALIGNMENT);
459 | authHeaderOperatePanel.add(Box.createVerticalStrut(10));
460 | authHeaderOperatePanel.add(removeAuthHeaderButton, Component.CENTER_ALIGNMENT);
461 | authHeaderOperatePanel.add(Box.createVerticalStrut(10));
462 | authHeaderOperatePanel.add(unauthCheckBox, Component.CENTER_ALIGNMENT);
463 | authHeaderMainPanel.add(Box.createHorizontalStrut(5));
464 | authHeaderMainPanel.add(authHeaderOperatePanel);
465 | // 初始化header表格
466 | String[] authHeaderTableColumnName = {"Header", "Value"};
467 | DefaultTableModel authHeaderModel = new DefaultTableModel(authHeaderTableColumnName, 0) {
468 | @Override
469 | public boolean isCellEditable(int row, int column) {
470 | return true;
471 | }
472 | };
473 | authHeaderTable = new JTable(authHeaderModel);
474 | // 支持多行选中
475 | authHeaderTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
476 | // 禁止表格编辑
477 | authHeaderTable.setDefaultEditor(Object.class, null);
478 | // 设置首选宽度
479 | authHeaderTable.getColumnModel().getColumn(0).setPreferredWidth(10);
480 | // 创建表格滚动面板
481 | JScrollPane authHeaderTableScrollPane = new JScrollPane(authHeaderTable);
482 | authHeaderMainPanel.add(Box.createHorizontalStrut(5));
483 | authHeaderMainPanel.add(authHeaderTableScrollPane);
484 | authHeaderMainPanel.add(Box.createHorizontalStrut(5));
485 | // 初始化配置文件中的header
486 | Util.flushConfigTable("header", authHeaderTable);
487 |
488 |
489 | // 左侧面板添加各个组件
490 | leftPanel.add(Box.createVerticalStrut(5));
491 | leftPanel.add(basicTitlePanel);
492 | leftPanel.add(Box.createVerticalStrut(5));
493 | leftPanel.add(turnOnPanel);
494 | leftPanel.add(Box.createVerticalStrut(5));
495 | leftPanel.add(listenProxyPanel);
496 | leftPanel.add(Box.createVerticalStrut(5));
497 | leftPanel.add(listenRepeterPanel);
498 | leftPanel.add(Box.createVerticalStrut(5));
499 | leftPanel.add(languageSupportPanel);
500 | leftPanel.add(Box.createVerticalStrut(5));
501 | leftPanel.add(cleanRequestListPanel);
502 | leftPanel.add(Box.createVerticalStrut(5));
503 | leftPanel.add(domainTitlePanel);
504 | leftPanel.add(Box.createVerticalStrut(5));
505 | leftPanel.add(domainMainPanel);
506 | leftPanel.add(Box.createVerticalStrut(5));
507 | leftPanel.add(payloadTitlePanel);
508 | leftPanel.add(Box.createVerticalStrut(5));
509 | leftPanel.add(payloadMainPanel);
510 | leftPanel.add(Box.createVerticalStrut(5));
511 | leftPanel.add(authHeaderTitlePanel);
512 | leftPanel.add(Box.createVerticalStrut(5));
513 | leftPanel.add(authHeaderMainPanel);
514 | leftPanel.add(Box.createVerticalStrut(5));
515 |
516 | BoxLayout tableLayout = new BoxLayout(tablePanel, BoxLayout.X_AXIS);
517 | tablePanel.setLayout(tableLayout);
518 | tablePanel.add(originRequestItemTableScrollPane);
519 | tablePanel.add(fuzzRequestItemTableScrollPane);
520 |
521 | // 右上方面板绘制
522 | BoxLayout rightTopLayout = new BoxLayout(rightTopPanel, BoxLayout.Y_AXIS);
523 | rightTopPanel.setLayout(rightTopLayout);
524 | // 搜索框绘制
525 | BoxLayout searchPanelLayout = new BoxLayout(searchPanel, BoxLayout.X_AXIS);
526 | searchPanel.setLayout(searchPanelLayout);
527 | searchPanel.add(Box.createHorizontalStrut(2));
528 | searchPanel.add(searchScopeComboBox);
529 | searchPanel.add(Box.createHorizontalStrut(2));
530 | searchPanel.add(searchTextField);
531 | searchPanel.add(Box.createHorizontalStrut(2));
532 | searchPanel.add(searchButton);
533 | searchPanel.add(Box.createHorizontalStrut(2));
534 | searchPanel.add(cleanSearchResultButton);
535 | searchPanel.add(Box.createHorizontalStrut(5));
536 | searchPanel.setMaximumSize(new Dimension(20000, 30));
537 | rightTopPanel.add(tablePanel);
538 | rightTopPanel.add(Box.createVerticalStrut(3));
539 | rightTopPanel.add(searchPanel);
540 | rightTopPanel.add(Box.createVerticalStrut(2));
541 |
542 | // 创建request/response展示面板
543 | BoxLayout rightBottomLayout = new BoxLayout(rightBottomPanel, BoxLayout.X_AXIS);
544 | rightBottomPanel.setLayout(rightBottomLayout);
545 | rightBottomPanel.add(requestEditor.uiComponent());
546 | rightBottomPanel.add(responseEditor.uiComponent());
547 |
548 | // 创建右侧分隔面板 上下分隔
549 | rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, rightTopPanel, rightBottomPanel);
550 | rightSplitPane.setDividerLocation(400);
551 |
552 | // 创建主分隔面板 左右分隔
553 | mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightSplitPane);
554 | mainSplitPane.setDividerLocation(300);
555 | mainSplitPane.setEnabled(false);
556 |
557 | for (int i = 0; i < originRequestItemTable.getColumnCount(); i++) {
558 | originRequestItemTable.getColumnModel().getColumn(i).setCellRenderer(new DefaultTableCellRenderer() {
559 | @Override
560 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
561 | // 调用默认的渲染器来获取单元格组件
562 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
563 |
564 | if (column == 0 || column == 1 || column == 4 || column == 5) {
565 | ((DefaultTableCellRenderer) c).setHorizontalAlignment(SwingConstants.CENTER);
566 | }
567 |
568 | if (highlightMap.containsKey(row)) {
569 | c.setForeground(Color.RED);
570 | } else {
571 | c.setForeground(originRequestItemTable.getForeground());
572 | }
573 |
574 | return c;
575 | }
576 | });
577 | }
578 |
579 | for (int i = 0; i < fuzzRequestItemTable.getColumnCount(); i++) {
580 | fuzzRequestItemTable.getColumnModel().getColumn(i).setCellRenderer(new DefaultTableCellRenderer() {
581 | @Override
582 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
583 | // 调用默认的渲染器来获取单元格组件
584 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
585 |
586 | try {
587 | int originRequestSelectedRow = originRequestItemTable.getSelectedRows()[0];
588 |
589 | if (column == 0 || column == 1 || column == 2 || column == 3 || column == 4 || column == 5) {
590 | ((DefaultTableCellRenderer) c).setHorizontalAlignment(SwingConstants.CENTER);
591 | }
592 |
593 | if (highlightMap.get(originRequestSelectedRow).contains(row)) {
594 | c.setForeground(Color.RED);
595 | } else {
596 | c.setForeground(originRequestItemTable.getForeground());
597 | }
598 | } catch (Exception e) {
599 | c.setForeground(originRequestItemTable.getForeground());
600 | }
601 |
602 | return c;
603 | }
604 | });
605 | }
606 |
607 | ActionListener listener = e -> {
608 | JRadioButton selected = (JRadioButton)e.getSource();
609 | if(selected == blackListRadioButton) {
610 | UserConfig.BLACK_OR_WHITE_CHOOSE = Boolean.TRUE;
611 | } else if(selected == whiteListRadioButton){
612 | UserConfig.BLACK_OR_WHITE_CHOOSE = Boolean.FALSE;
613 | }
614 | YamlUtil.exportToYaml();
615 | };
616 |
617 | blackListRadioButton.addActionListener(listener);
618 | whiteListRadioButton.addActionListener(listener);
619 |
620 |
621 | // 设置启用插件复选框监听器
622 | turnOnCheckBox.addItemListener(new ItemListener() {
623 | @Override
624 | public void itemStateChanged(ItemEvent e) {
625 | if (e.getStateChange() == ItemEvent.SELECTED) {
626 | UserConfig.TURN_ON = Boolean.TRUE;
627 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
628 | UserConfig.TURN_ON = Boolean.FALSE;
629 | }
630 | YamlUtil.exportToYaml();
631 | }
632 | });
633 |
634 |
635 | // 监听proxy复选框监听器
636 | listenProxyCheckBox.addItemListener(new ItemListener() {
637 | @Override
638 | public void itemStateChanged(ItemEvent e) {
639 | if (e.getStateChange() == ItemEvent.SELECTED) {
640 | UserConfig.LISTEN_PROXY = Boolean.TRUE;
641 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
642 | UserConfig.LISTEN_PROXY = Boolean.FALSE;
643 | }
644 | YamlUtil.exportToYaml();
645 | }
646 | });
647 |
648 |
649 | // 监听repeter复选框监听器
650 | listenRepeterCheckBox.addItemListener(new ItemListener() {
651 | @Override
652 | public void itemStateChanged(ItemEvent e) {
653 | if (e.getStateChange() == ItemEvent.SELECTED) {
654 | UserConfig.LISTEN_REPETER = Boolean.TRUE;
655 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
656 | UserConfig.LISTEN_REPETER = Boolean.FALSE;
657 | }
658 | YamlUtil.exportToYaml();
659 | }
660 | });
661 |
662 |
663 | // 清空请求记录按钮监听器
664 | cleanRequestItemButton.addActionListener(new ActionListener() {
665 | @Override
666 | public void actionPerformed(ActionEvent e) {
667 | ORIGIN_REQUEST_TABLE_DATA.clear();
668 | NEW_REQUEST_TO_BE_SENT_DATA.clear();
669 | originRequestItemTableModel.setRowCount(0);
670 | fuzzRequestItemTableModel.setRowCount(0);
671 |
672 | // 清空editor
673 | requestEditor.setRequest(null);
674 | responseEditor.setResponse(null);
675 |
676 | try {
677 | Field executorField = AutoFuzzHandler.class.getDeclaredField("executor");
678 | executorField.setAccessible(true);
679 | executorField.set(null, new ThreadPoolExecutor(10, 100, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200), new ThreadPoolExecutor.AbortPolicy()));
680 | } catch (NoSuchFieldException ex) {
681 | Main.LOG.logToError(ex.getMessage() + "重置线程池异常");
682 | } catch (IllegalAccessException ex) {
683 | Main.LOG.logToError(ex.getMessage() + "重置线程池异常");
684 | }
685 | }
686 | });
687 |
688 |
689 | // 添加域名按钮监听器
690 | addDomainButton.addActionListener(new ActionListener() {
691 | @Override
692 | public void actionPerformed(ActionEvent e) {
693 | showAddDataDialog("domain");
694 | }
695 | });
696 |
697 | // 添加payload按钮监听器
698 | addPayloadButton.addActionListener(new ActionListener() {
699 | @Override
700 | public void actionPerformed(ActionEvent e) {
701 | showAddDataDialog("payload");
702 | }
703 | });
704 |
705 | // 添加域名按钮监听器
706 | addAuthHeaderButton.addActionListener(new ActionListener() {
707 | @Override
708 | public void actionPerformed(ActionEvent e) {
709 | showAddDataDialog("header");
710 | }
711 | });
712 |
713 | // 编辑域名按钮监听器
714 | editDomainButton.addActionListener(new ActionListener() {
715 | @Override
716 | public void actionPerformed(ActionEvent e) {
717 | // 如果选择多行 默认编辑选中的第一行
718 | showEditDataDialog("domain", domainTable.getSelectedRows()[0]);
719 | }
720 | });
721 |
722 | // 编辑payload按钮监听器
723 | editPayloadButton.addActionListener(new ActionListener() {
724 | @Override
725 | public void actionPerformed(ActionEvent e) {
726 | // 如果选中多行 默认编辑选中的第一行
727 | showEditDataDialog("payload", payloadTable.getSelectedRows()[0]);
728 | }
729 | });
730 |
731 | // 编辑AuthHeader按钮监听器
732 | editAuthHeaderButton.addActionListener(new ActionListener() {
733 | @Override
734 | public void actionPerformed(ActionEvent e) {
735 | // 如果选中多行 默认编辑选中的第一行
736 | showEditDataDialog("header", authHeaderTable.getSelectedRows()[0]);
737 | }
738 | });
739 |
740 | // 删除domain按钮监听器
741 | removeDomainButton.addActionListener(new ActionListener() {
742 | @Override
743 | public void actionPerformed(ActionEvent e) {
744 | Util.removeConfigData("domain", domainTable.getSelectedRows());
745 | Util.flushConfigTable("domain", domainTable);
746 | YamlUtil.exportToYaml();
747 | }
748 | });
749 |
750 | // 删除payload按钮监听器
751 | removePayloadButton.addActionListener(new ActionListener() {
752 | @Override
753 | public void actionPerformed(ActionEvent e) {
754 | int[] rows = payloadTable.getSelectedRows();
755 | // 如果移除的payload包含参数置空 则同时取消勾选
756 | if (rows[0] == 0 && PAYLOAD_LIST.get(0).equals("")) {
757 | emptyParamCheckBox.setSelected(false);
758 |
759 | // 空值会在触发checkbox事件时移除 去掉第一个空值元素
760 | int[] tmp = new int[rows.length - 1];
761 | for (int i = 1; i < rows.length; i++) {
762 | tmp[i - 1] = rows[i] - 1;
763 | }
764 | rows = tmp;
765 | }
766 | Util.removeConfigData("payload", rows);
767 | Util.flushConfigTable("payload", payloadTable);
768 | YamlUtil.exportToYaml();
769 |
770 | }
771 | });
772 |
773 | // 删除header按钮监听器
774 | removeAuthHeaderButton.addActionListener(new ActionListener() {
775 | @Override
776 | public void actionPerformed(ActionEvent e) {
777 | Util.removeConfigData("header", authHeaderTable.getSelectedRows());
778 | Util.flushConfigTable("header", authHeaderTable);
779 | YamlUtil.exportToYaml();
780 | }
781 | });
782 |
783 | // appendMod复选框监听器
784 | appendModCheckBox.addItemListener(new ItemListener() {
785 | @Override
786 | public void itemStateChanged(ItemEvent e) {
787 | if (e.getStateChange() == ItemEvent.SELECTED) {
788 | UserConfig.APPEND_MOD = Boolean.TRUE;
789 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
790 | UserConfig.APPEND_MOD = Boolean.FALSE;
791 | }
792 | YamlUtil.exportToYaml();
793 | }
794 | });
795 |
796 | // 参数置空监听器
797 | emptyParamCheckBox.addItemListener(new ItemListener() {
798 | @Override
799 | public void itemStateChanged(ItemEvent e) {
800 | if (e.getStateChange() == ItemEvent.SELECTED) {
801 | PAYLOAD_LIST.add(0, "");
802 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
803 | PAYLOAD_LIST.remove("");
804 | }
805 |
806 | Util.flushConfigTable("payload", payloadTable);
807 | YamlUtil.exportToYaml();
808 | }
809 | });
810 |
811 | paramURLEncodeCheckBox.addItemListener((new ItemListener() {
812 | @Override
813 | public void itemStateChanged(ItemEvent e) {
814 | if (e.getStateChange() == ItemEvent.SELECTED) {
815 | UserConfig.PARAM_URL_ENCODE = Boolean.TRUE;
816 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
817 | UserConfig.PARAM_URL_ENCODE = Boolean.FALSE;
818 | }
819 | YamlUtil.exportToYaml();
820 | }
821 | }));
822 |
823 | // 包含子域名复选框监听器
824 | includeSubDomainCheckBox.addItemListener(new ItemListener() {
825 | @Override
826 | public void itemStateChanged(ItemEvent e) {
827 | if (e.getStateChange() == ItemEvent.SELECTED) {
828 | UserConfig.INCLUDE_SUBDOMAIN = Boolean.TRUE;
829 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
830 | UserConfig.INCLUDE_SUBDOMAIN = Boolean.FALSE;
831 | }
832 | YamlUtil.exportToYaml();
833 | }
834 | });
835 |
836 | // 包含子域名复选框监听器
837 | unauthCheckBox.addItemListener(new ItemListener() {
838 | @Override
839 | public void itemStateChanged(ItemEvent e) {
840 | if (e.getStateChange() == ItemEvent.SELECTED) {
841 | UserConfig.UNAUTH = Boolean.TRUE;
842 | } else if (e.getStateChange() == ItemEvent.DESELECTED) {
843 | UserConfig.UNAUTH = Boolean.FALSE;
844 | }
845 | YamlUtil.exportToYaml();
846 | }
847 | });
848 |
849 | // 查找作用域下拉框监听器
850 | searchScopeComboBox.addActionListener(new ActionListener() {
851 | @Override
852 | public void actionPerformed(ActionEvent e) {
853 | // 获取用户选择
854 | String selectedOption = (String) searchScopeComboBox.getSelectedItem();
855 |
856 | if (SearchScope.REQUEST.scopeName().equals(selectedOption)) {
857 | UserConfig.SEARCH_SCOPE = SearchScope.REQUEST;
858 | } else if (SearchScope.RESPONSE.scopeName().equals(selectedOption)) {
859 | UserConfig.SEARCH_SCOPE = SearchScope.RESPONSE;
860 | }
861 | }
862 | });
863 |
864 | // 查找作用域下拉框监听器
865 | languageSupportComboBox.addActionListener(new ActionListener() {
866 | @Override
867 | public void actionPerformed(ActionEvent e) {
868 | // 获取用户选择
869 | String selectedOption = (String) languageSupportComboBox.getSelectedItem();
870 | if (Language.SIMPLIFIED_CHINESE.language().equals(selectedOption)) {
871 | UserConfig.LANGUAGE = Language.SIMPLIFIED_CHINESE;
872 | bundle = ResourceBundle.getBundle("lang", new Locale("zh", "CN"));
873 | } else if (Language.ENGLISH.language().equals(selectedOption)) {
874 | UserConfig.LANGUAGE = Language.ENGLISH;
875 | bundle = ResourceBundle.getBundle("lang", new Locale("en", "US"));
876 | }
877 |
878 | // 加载多语言支持组件
879 | loadLanguageSupportComponent();
880 | // 更新本地配置文件
881 | YamlUtil.exportToYaml();
882 | }
883 | });
884 |
885 |
886 | // 查找按钮监听器
887 | searchButton.addActionListener(new ActionListener() {
888 | @Override
889 | public void actionPerformed(ActionEvent e) {
890 | String keyword = searchTextField.getText().trim();
891 |
892 | if (UserConfig.SEARCH_SCOPE == SearchScope.REQUEST) {
893 | searchAllRequestResponse(keyword, SearchScope.REQUEST);
894 | } else if (UserConfig.SEARCH_SCOPE == SearchScope.RESPONSE) {
895 | searchAllRequestResponse(keyword, SearchScope.RESPONSE);
896 | }
897 | }
898 | });
899 |
900 | // 清空查找结果按钮监听器
901 | cleanSearchResultButton.addActionListener(new ActionListener() {
902 | @Override
903 | public void actionPerformed(ActionEvent e) {
904 | highlightMap.clear();
905 |
906 | originRequestItemTable.repaint();
907 | fuzzRequestItemTable.repaint();
908 | }
909 | });
910 |
911 | // 创建fuzzRequestItem被点击时的监听事件 用于展示request response
912 | fuzzRequestItemTable.getSelectionModel().addListSelectionListener(e -> {
913 | if (!e.getValueIsAdjusting()) {
914 | int fuzzRow = fuzzRequestItemTable.getSelectedRow();
915 | int originRow = originRequestItemTable.getSelectedRow();
916 | if (fuzzRow < 0 || originRow < 0) return;
917 |
918 | Integer id = (Integer) originRequestItemTableModel.getValueAt(originRow, 0);
919 | String method = (String) originRequestItemTableModel.getValueAt(originRow, 1);
920 | String host = (String) originRequestItemTableModel.getValueAt(originRow, 2);
921 | String path = (String) originRequestItemTableModel.getValueAt(originRow, 3);
922 | OriginRequestItem tempItem = new OriginRequestItem(id, method, host, path, null, null);
923 |
924 | for (OriginRequestItem item : ORIGIN_REQUEST_TABLE_DATA.values()) {
925 | if (item.equals(tempItem) && item.getId().equals(id)) {
926 | if (fuzzRow < item.getFuzzRequestArrayList().size()) {
927 | FuzzRequestItem fuzzItem = item.getFuzzRequestArrayList().get(fuzzRow);
928 | requestEditor.setRequest(fuzzItem.getFuzzRequestResponse().request());
929 | responseEditor.setResponse(fuzzItem.getFuzzRequestResponse().response());
930 | }
931 | break;
932 | }
933 | }
934 | }
935 | });
936 |
937 |
938 | // 创建oritinRequestItem被点击时的监听事件 用于展示request response
939 | originRequestItemTable.getSelectionModel().addListSelectionListener(e -> {
940 | if (!e.getValueIsAdjusting()) {
941 | int row = originRequestItemTable.getSelectedRow();
942 | if (row < 0) return;
943 |
944 | Integer id = (Integer) originRequestItemTableModel.getValueAt(row, 0);
945 | String methodText = (String) originRequestItemTableModel.getValueAt(row, 1);
946 | String hostText = (String) originRequestItemTableModel.getValueAt(row, 2);
947 | String pathText = (String) originRequestItemTableModel.getValueAt(row, 3);
948 | OriginRequestItem tempItem = new OriginRequestItem(id, methodText, hostText, pathText, null, null);
949 |
950 | for (OriginRequestItem item : ORIGIN_REQUEST_TABLE_DATA.values()) {
951 | if (item.equals(tempItem) && item.getId().equals(id)) {
952 | requestEditor.setRequest(item.getOriginRequest());
953 | responseEditor.setResponse(item.getOriginResponse());
954 |
955 | // 刷新fuzz子项
956 | fuzzRequestItemTableModel.setRowCount(0);
957 | for (FuzzRequestItem fuzzItem : item.getFuzzRequestArrayList()) {
958 | fuzzRequestItemTableModel.addRow(new Object[]{
959 | fuzzItem.getParam(),
960 | fuzzItem.getPayload(),
961 | fuzzItem.getResponseLength(),
962 | fuzzItem.getResponseLengthChange(),
963 | fuzzItem.getResponseCode(),
964 | fuzzItem.getResponseTime()
965 | });
966 | }
967 | break;
968 | }
969 | }
970 | }
971 | });
972 |
973 | // 添加MouseListener(用于重复点击当前选中行也刷新)
974 | originRequestItemTable.addMouseListener(new MouseAdapter() {
975 | @Override
976 | public void mouseClicked(MouseEvent e) {
977 | if (e.getClickCount() >= 1) {
978 | int row = originRequestItemTable.rowAtPoint(e.getPoint());
979 | if (row < 0) return;
980 |
981 | Integer id = (Integer) originRequestItemTableModel.getValueAt(row, 0);
982 | String methodText = (String) originRequestItemTableModel.getValueAt(row, 1);
983 | String hostText = (String) originRequestItemTableModel.getValueAt(row, 2);
984 | String pathText = (String) originRequestItemTableModel.getValueAt(row, 3);
985 | OriginRequestItem tempItem = new OriginRequestItem(id, methodText, hostText, pathText, null, null);
986 |
987 | for (Map.Entry entry : ORIGIN_REQUEST_TABLE_DATA.entrySet()) {
988 | OriginRequestItem item = entry.getValue();
989 | if (item.equals(tempItem) && item.getId().equals(id)) {
990 | requestEditor.setRequest(item.getOriginRequest());
991 | responseEditor.setResponse(item.getOriginResponse());
992 |
993 | // 刷新 fuzzRequestItemTable
994 | fuzzRequestItemTableModel.setRowCount(0);
995 | for (FuzzRequestItem fuzzRequestItem : item.getFuzzRequestArrayList()) {
996 | fuzzRequestItemTableModel.addRow(new Object[]{
997 | fuzzRequestItem.getParam(),
998 | fuzzRequestItem.getPayload(),
999 | fuzzRequestItem.getResponseLength(),
1000 | fuzzRequestItem.getResponseLengthChange(),
1001 | fuzzRequestItem.getResponseCode(),
1002 | fuzzRequestItem.getResponseTime()
1003 | });
1004 | }
1005 | fuzzRequestItemTable.updateUI();
1006 | break;
1007 | }
1008 | }
1009 | }
1010 | }
1011 | });
1012 | }
1013 |
1014 | private void showAddDataDialog(String type) {
1015 | TitledBorder titledBorder = null;
1016 | if (type.equals("domain")) {
1017 | titledBorder = BorderFactory.createTitledBorder(bundle.getString("addDomainTitledBorder.text"));
1018 | } else if (type.equals("payload")) {
1019 | titledBorder = BorderFactory.createTitledBorder(bundle.getString("addPayloadTitledBorder.text"));
1020 | } else if (type.equals("header")) {
1021 | titledBorder = BorderFactory.createTitledBorder(bundle.getString("addHeaderTitledBorder.text"));
1022 | }
1023 |
1024 | JTextArea userInputTextArea = new JTextArea();
1025 | JScrollPane scrollPane = new JScrollPane(userInputTextArea);
1026 | userInputTextArea.setBorder(BorderFactory.createCompoundBorder(userInputTextArea.getBorder(), titledBorder));
1027 | scrollPane.setPreferredSize(new Dimension(350, 250));
1028 |
1029 | int option = 0;
1030 | if (type.equals("domain")) {
1031 | option = JOptionPane.showConfirmDialog(null, scrollPane, bundle.getString("addDomainTitle.text"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
1032 | } else if (type.equals("payload")) {
1033 | option = JOptionPane.showConfirmDialog(null, scrollPane, bundle.getString("addPayloadTitle.text"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
1034 | } else if (type.equals("header")) {
1035 | option = JOptionPane.showConfirmDialog(null, scrollPane, bundle.getString("addAuthHeaderTitle.text"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
1036 | }
1037 |
1038 |
1039 | // 检测用户选择
1040 | if (option == JOptionPane.YES_OPTION) {
1041 | // 用户选择了 "是" 则添加数据,其他情况不做操作
1042 | Util.addConfigData(type, userInputTextArea);
1043 |
1044 | if (type.equals("domain")) {
1045 | Util.flushConfigTable(type, domainTable);
1046 | } else if (type.equals("payload")) {
1047 | Util.flushConfigTable(type, payloadTable);
1048 | } else if (type.equals("header")) {
1049 | Util.flushConfigTable(type, authHeaderTable);
1050 | }
1051 |
1052 | YamlUtil.exportToYaml();
1053 | }
1054 | }
1055 |
1056 | private void showEditDataDialog(String type, int row) {
1057 | JTextField dataTextField = new JTextField();
1058 | dataTextField.setPreferredSize(new Dimension(350, 25));
1059 |
1060 | if (type.equals("domain")) {
1061 | dataTextField.setText((String) domainTable.getModel().getValueAt(row, 0));
1062 | } else if (type.equals("payload")) {
1063 | dataTextField.setText((String) payloadTable.getModel().getValueAt(row, 0));
1064 | } else if (type.equals("header")) {
1065 | dataTextField.setText(authHeaderTable.getModel().getValueAt(row, 0) + ": " + authHeaderTable.getModel().getValueAt(row, 1));
1066 | }
1067 |
1068 | int option = 0;
1069 | if (type.equals("domain")) {
1070 | option = JOptionPane.showConfirmDialog(null, dataTextField, bundle.getString("editDomainTitle.text"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
1071 | } else if (type.equals("payload")) {
1072 | option = JOptionPane.showConfirmDialog(null, dataTextField, bundle.getString("editPayloadTitle.text"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
1073 | } else if (type.equals("header")) {
1074 | option = JOptionPane.showConfirmDialog(null, dataTextField, bundle.getString("editAuthHeaderTitle.text"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
1075 | }
1076 |
1077 |
1078 | // 检测用户选择
1079 | if (option == JOptionPane.YES_OPTION) {
1080 | // 用户选择了 "是" 则添加数据,其他情况不做操作
1081 | Util.editConfigData(type, dataTextField, row);
1082 |
1083 | if (type.equals("domain")) {
1084 | Util.flushConfigTable(type, domainTable);
1085 | } else if (type.equals("payload")) {
1086 | Util.flushConfigTable(type, payloadTable);
1087 | } else if (type.equals("header")) {
1088 | Util.flushConfigTable(type, authHeaderTable);
1089 | }
1090 | YamlUtil.exportToYaml();
1091 | }
1092 | }
1093 |
1094 | private void searchAllRequestResponse(String keyword, SearchScope searchScope) {
1095 | // 先初始化map
1096 | highlightMap = new HashMap<>();
1097 |
1098 | // 获取列表中所有origin的行
1099 | TableModel model = originRequestItemTable.getModel();
1100 | int rowCount = model.getRowCount();
1101 |
1102 | for (int i = 0; i < rowCount; i++) {
1103 | // 由于顺序不一样 根据表中的值先创建一个originRequest
1104 | Integer id = (Integer) model.getValueAt(i, 0);
1105 | String method = (String) model.getValueAt(i, 1);
1106 | String host = (String) model.getValueAt(i, 2);
1107 | String path = (String) model.getValueAt(i, 3);
1108 | OriginRequestItem selectOriginRequestItem = new OriginRequestItem(id, method, host, path, null, null);
1109 |
1110 | // 遍历data中的数据找到对应的originRequest 检查内容
1111 | for (Map.Entry originRequestItemEntry : ORIGIN_REQUEST_TABLE_DATA.entrySet()) {
1112 | OriginRequestItem originRequestItem = originRequestItemEntry.getValue();
1113 | if (originRequestItem.equals(selectOriginRequestItem) && originRequestItem.getId().equals(id)) {
1114 | String originRequestString = new String(originRequestItem.getOriginRequest().toByteArray().getBytes(), StandardCharsets.UTF_8);
1115 | String originResponseString = new String(originRequestItem.getOriginResponse().toByteArray().getBytes(), StandardCharsets.UTF_8);
1116 | // 进行不区分大小写的比对originRequest
1117 | if (originRequestString.toLowerCase().contains(keyword.toLowerCase()) && searchScope.equals(SearchScope.REQUEST)) {
1118 | highlightMap.put(i, new ArrayList<>());
1119 | } else if (originResponseString.toLowerCase().contains(keyword.toLowerCase()) && searchScope.equals(SearchScope.RESPONSE)) {
1120 | highlightMap.put(i, new ArrayList<>());
1121 | }
1122 |
1123 | // 比对fuzzRequest
1124 | ArrayList fuzzRequestArrayList = originRequestItem.getFuzzRequestArrayList();
1125 | int index = 0;
1126 | for (FuzzRequestItem fuzzRequestItem : fuzzRequestArrayList) {
1127 | String fuzzRequestString = new String(fuzzRequestItem.getFuzzRequestResponse().request().toByteArray().getBytes(), StandardCharsets.UTF_8);
1128 | String fuzzResponseString = new String(fuzzRequestItem.getFuzzRequestResponse().response().toByteArray().getBytes(), StandardCharsets.UTF_8);
1129 | if (fuzzRequestString.toLowerCase().contains(keyword.toLowerCase()) && searchScope.equals(SearchScope.REQUEST)) {
1130 | if (highlightMap.containsKey(i)) {
1131 | highlightMap.get(i).add(index);
1132 | } else { // 如果originRequest没匹配到 fuzzRequest匹配到了 也加入map
1133 | ArrayList fuzzRequestHighlightList = new ArrayList<>();
1134 | fuzzRequestHighlightList.add(index);
1135 | highlightMap.put(i, fuzzRequestHighlightList);
1136 | }
1137 | } else if (fuzzResponseString.toLowerCase().contains(keyword.toLowerCase()) && searchScope.equals(SearchScope.RESPONSE)) {
1138 | if (highlightMap.containsKey(i)) {
1139 | highlightMap.get(i).add(index);
1140 | } else { // 如果originResponse没匹配到 fuzzResponse匹配到了 也加入map
1141 | ArrayList fuzzRequestHighlightList = new ArrayList<>();
1142 | fuzzRequestHighlightList.add(index);
1143 | highlightMap.put(i, fuzzRequestHighlightList);
1144 | }
1145 | }
1146 |
1147 | index++;
1148 | }
1149 | }
1150 | }
1151 |
1152 | // 遍历完之后 重新渲染表格
1153 | originRequestItemTable.repaint();
1154 | fuzzRequestItemTable.repaint();
1155 | }
1156 | }
1157 |
1158 |
1159 | // 初始化多语言支持组件
1160 | private void loadLanguageSupportComponent() {
1161 | for (String componentName : langSupportComponent) {
1162 | try {
1163 | Field componentField = MainUI.class.getDeclaredField(componentName);
1164 | componentField.setAccessible(true);
1165 | Object component = componentField.get(this);
1166 | Method setTextMethod = component.getClass().getMethod("setText", String.class);
1167 | setTextMethod.invoke(component, bundle.getString(componentName + ".text"));
1168 | } catch (Exception e) {
1169 | Main.LOG.logToError("[ERROR] 加载多语言支持组件出现异常.");
1170 | }
1171 | }
1172 | }
1173 | }
--------------------------------------------------------------------------------
/src/main/java/com/chave/utils/Util.java:
--------------------------------------------------------------------------------
1 | package com.chave.utils;
2 |
3 | import com.chave.Main;
4 | import com.chave.bean.Data;
5 | import com.chave.bean.OriginRequestItem;
6 |
7 | import javax.swing.*;
8 | import javax.swing.table.DefaultTableModel;
9 | import javax.swing.table.TableRowSorter;
10 | import java.io.UnsupportedEncodingException;
11 | import java.math.BigDecimal;
12 | import java.util.*;
13 |
14 | public class Util {
15 |
16 | // 刷新fuzz请求列表
17 | public static synchronized void addOriginRequestItem(OriginRequestItem item) {
18 | try {
19 | JTable originRequestItemTable = Main.MainUI.getOriginRequestItemTable();
20 | DefaultTableModel model = (DefaultTableModel) originRequestItemTable.getModel();
21 |
22 | model.addRow(new Object[]{item.getId(), item.getMethod(), item.getHost(), item.getPath(), item.getResponseLength(), item.getResponseCode()});
23 |
24 | // 始终以id排序
25 | TableRowSorter sorter = (TableRowSorter) originRequestItemTable.getRowSorter();
26 | sorter.sort();
27 | } catch (NullPointerException nullPointerException) {
28 | // 不明原因出现空指针
29 | }
30 |
31 | }
32 |
33 | public static String fullyURLEncode(String input) throws UnsupportedEncodingException {
34 | StringBuilder encodedString = new StringBuilder();
35 |
36 | // Iterate over each character in the input string
37 | for (char ch : input.toCharArray()) {
38 | // Encode each character to its %XX format
39 | encodedString.append(String.format("%%%02X", (int) ch));
40 | }
41 |
42 | return encodedString.toString();
43 | }
44 |
45 | public synchronized static void setOriginRequestId(OriginRequestItem item) {
46 | item.setId(Data.ORIGIN_REQUEST_TABLE_DATA.size());
47 | }
48 |
49 | // 刷新domain表格
50 | public static synchronized void flushConfigTable(String type, JTable table) {
51 | DefaultTableModel model = (DefaultTableModel) table.getModel();
52 | model.setRowCount(0); // 清空表格
53 |
54 | switch (type) {
55 | case "domain":
56 | fillTableWithList(model, Data.DOMAIN_LIST);
57 | break;
58 | case "payload":
59 | fillTableWithList(model, Data.PAYLOAD_LIST);
60 | break;
61 | case "header":
62 | fillTableWithMap(model, Data.HEADER_MAP);
63 | break;
64 | }
65 | }
66 |
67 | private static void fillTableWithList(DefaultTableModel model, ArrayList list) {
68 | ArrayList tempList = new ArrayList<>();
69 | if (list.size() > 0) {
70 | for (String item : list) {
71 | if (!item.equals("")) {
72 | tempList.add(item);
73 | }
74 | model.addRow(new Object[]{item});
75 | }
76 | }
77 | }
78 |
79 | private static void fillTableWithMap(DefaultTableModel model, LinkedHashMap map) {
80 | for (Map.Entry entry : map.entrySet()) {
81 | model.addRow(new Object[]{entry.getKey(), entry.getValue()});
82 | }
83 | }
84 |
85 |
86 | // 用户添加配置
87 | public static void addConfigData(String type, JTextArea userInputTextArea) {
88 | String userInput = userInputTextArea.getText();
89 | if (isBlankInput(userInput)) {
90 | return;
91 | }
92 |
93 | switch (type) {
94 | case "domain":
95 | addToDomainList(userInput);
96 | break;
97 | case "payload":
98 | addToPayloadList(userInput);
99 | break;
100 | case "header":
101 | addToHeaderMap(userInput);
102 | break;
103 | }
104 | }
105 |
106 | private static boolean isBlankInput(String input) {
107 | return input == null || input.trim().length() == 0;
108 | }
109 |
110 | private static void addToDomainList(String input) {
111 | processListInput(Data.DOMAIN_LIST, input);
112 | }
113 |
114 | private static void addToPayloadList(String input) {
115 | processListInput(Data.PAYLOAD_LIST, input);
116 | }
117 |
118 | private static void processListInput(ArrayList targetList, String input) {
119 | String[] lines = input.split("\n");
120 | for (String line : lines) {
121 | line = line.trim();
122 | if (!line.isEmpty() && !targetList.contains(line)) {
123 | targetList.add(line);
124 | }
125 | }
126 | }
127 |
128 | private static void addToHeaderMap(String input) {
129 | LinkedHashMap headerMap = Data.HEADER_MAP;
130 | String[] lines = input.split("\n");
131 |
132 | for (String line : lines) {
133 | String[] parts = line.split(":", 2); // 最多分割成两部分
134 | if (parts.length == 2) {
135 | String header = parts[0].trim();
136 | String value = parts[1].trim();
137 | if (!header.isEmpty() && !value.isEmpty() && !headerMap.containsKey(header)) {
138 | headerMap.put(header, value);
139 | }
140 | }
141 | }
142 | }
143 | // 用户编辑配置
144 | public static void editConfigData(String type, JTextField userInputTextField, int row) {
145 | String userInput = userInputTextField.getText();
146 | // 防止空指针
147 | if (userInput == null) {
148 | return;
149 | }
150 |
151 | // 去除前后空格换行符
152 | userInput = userInput.trim();
153 |
154 | // 防止只有空白字符
155 | if (userInput.length() == 0) {
156 | return;
157 | }
158 |
159 |
160 | if (type.equals("domain")) {
161 | ArrayList targetList = Data.DOMAIN_LIST;
162 | if (!targetList.contains(userInput)) {
163 | targetList.set(row, userInput);
164 | }
165 | } else if (type.equals("payload")) {
166 | ArrayList targetList = Data.PAYLOAD_LIST;
167 | if (!targetList.contains(userInput)) {
168 | targetList.set(row, userInput);
169 | }
170 | } else if (type.equals("header")) {
171 | LinkedHashMap headerMap = new LinkedHashMap<>();
172 | int count = 0;
173 |
174 | int index = userInput.indexOf(":");
175 | if (index != -1) { // 确保是合法的header的key-value
176 | String header = userInput.substring(0, index); // 左边部分
177 | String value = userInput.substring(index + 1).trim(); // 右边部分(去掉冒号)
178 |
179 | // 创建一个新的linkedhashmap用于修改
180 | for (Map.Entry headerEntry : Data.HEADER_MAP.entrySet()) {
181 | if (count == row) {
182 | headerMap.put(header, value);
183 | } else {
184 | headerMap.put(headerEntry.getKey(), headerEntry.getValue());
185 | }
186 | count++;
187 | }
188 |
189 | Data.HEADER_MAP = headerMap;
190 | }
191 | }
192 | }
193 |
194 | // 用户删除配置
195 | public static void removeConfigData(String type, int[] rows) {
196 | if (rows.length == 0) {
197 | return;
198 | }
199 |
200 | switch (type) {
201 | case "domain":
202 | removeFromList(Data.DOMAIN_LIST, rows);
203 | break;
204 | case "payload":
205 | removeFromList(Data.PAYLOAD_LIST, rows);
206 | break;
207 | case "header":
208 | removeFromMap(Data.HEADER_MAP, rows);
209 | break;
210 | }
211 | }
212 |
213 | private static void removeFromList(ArrayList list, int[] rows) {
214 | for (int i = rows.length - 1; i >= 0; i--) {
215 | list.remove(rows[i]);
216 | }
217 | }
218 |
219 | private static void removeFromMap(LinkedHashMap map, int[] rows) {
220 | for (int i = rows.length - 1; i >= 0; i--) {
221 | int index = 0;
222 | for (Map.Entry entry : map.entrySet()) {
223 | if (index == rows[i]) {
224 | map.remove(entry.getKey());
225 | break;
226 | }
227 | index++;
228 | }
229 | }
230 | }
231 |
232 | // 对有歧义的字符进行url编码
233 | public static String urlEncode(String input) throws UnsupportedEncodingException {
234 | StringBuilder encodedString = new StringBuilder();
235 |
236 | // 遍历字符串中的每个字符
237 | for (int i = 0; i < input.length(); i++) {
238 | char c = input.charAt(i);
239 |
240 | // 如果是字母或数字,则不进行编码
241 | if (Character.isLetterOrDigit(c)) {
242 | encodedString.append(c);
243 | } else {
244 | // 对所有其他字符进行URL编码
245 | encodedString.append("%").append(String.format("%02X", (int) c)); // 其他字符进行URL编码
246 | }
247 | }
248 |
249 | return encodedString.toString();
250 | }
251 |
252 |
253 | public static Object isNumber(String value) {
254 | // 尝试将字符串转化为整数
255 | try {
256 | return Integer.parseInt(value); // 如果能转化为整数,返回整数
257 | } catch (NumberFormatException e1) {
258 | // 如果转换为整数失败,尝试将其转化为 BigDecimal
259 | try {
260 | return new BigDecimal(value); // 如果能转化为 BigDecimal,返回 BigDecimal
261 | } catch (NumberFormatException e2) {
262 | // 如果无法转换为整数或 BigDecimal,返回原始的字符串
263 | return value;
264 | }
265 | }
266 | }
267 |
268 | public static Object isBoolean(String value) {
269 | if (value.equals("true")) {
270 | return true;
271 | } else if (value.equals("false")) {
272 | return false;
273 | } else {
274 | return value;
275 | }
276 | }
277 | }
--------------------------------------------------------------------------------
/src/main/java/com/chave/utils/YamlUtil.java:
--------------------------------------------------------------------------------
1 | package com.chave.utils;
2 |
3 | import com.chave.Main;
4 | import com.chave.bean.Data;
5 | import com.chave.bean.Language;
6 | import com.chave.config.Config;
7 | import com.chave.config.UserConfig;
8 | import org.yaml.snakeyaml.DumperOptions;
9 | import org.yaml.snakeyaml.Yaml;
10 |
11 | import java.io.*;
12 | import java.util.*;
13 |
14 | public class YamlUtil {
15 |
16 | public static void loadYamlConfig() {
17 | try {
18 | File ruleYamlFile = new File(Config.CONFIG_FILE);
19 | if (!ruleYamlFile.exists()) {
20 | // 如果文件不存在 使用默认配置导出一份到文件
21 | exportToYaml();
22 | Main.LOG.logToOutput("[INFO] 成功创建默认配置文件: " + Config.CONFIG_FILE);
23 | } else {
24 | // 如果文件存在 加载配置文件
25 | FileInputStream fis = new FileInputStream(ruleYamlFile);
26 | LinkedHashMap map = new Yaml().load(fis);
27 |
28 | // 加载用户配置
29 | LinkedHashMap config = (LinkedHashMap) map.get("config");
30 | LinkedHashMap basicSetting = (LinkedHashMap) config.get("basic_setting");
31 | LinkedHashMap domainSetting = (LinkedHashMap) config.get("domain_setting");
32 | LinkedHashMap payloadSetting = (LinkedHashMap) config.get("payload_setting");
33 | LinkedHashMap authHeaderSetting = (LinkedHashMap) config.get("auth_header_setting");
34 |
35 | UserConfig.TURN_ON = (Boolean) basicSetting.get("turn_on");
36 | UserConfig.LISTEN_PROXY = (Boolean) basicSetting.get("listen_proxy");
37 | UserConfig.LISTEN_REPETER = (Boolean) basicSetting.get("listen_repeter");
38 | for (Language lang : Language.values()) {
39 | if (basicSetting.get("language").equals(lang.name())) {
40 | UserConfig.LANGUAGE = lang;
41 | break;
42 | }
43 | }
44 | UserConfig.BLACK_OR_WHITE_CHOOSE = (Boolean) domainSetting.get("black_or_white_choose");
45 | UserConfig.INCLUDE_SUBDOMAIN = (Boolean) domainSetting.get("include_subdomain");
46 | UserConfig.APPEND_MOD = (Boolean) payloadSetting.get("append_mod");
47 | UserConfig.PARAM_URL_ENCODE = (Boolean) payloadSetting.get("param_url_encode");
48 | UserConfig.UNAUTH = (Boolean) authHeaderSetting.get("unauth");
49 |
50 |
51 | // 加载payload
52 | ArrayList payload = (ArrayList) map.get("payload");
53 | if (payload != null) {
54 | for (LinkedHashMap payloadmap : payload) {
55 | Data.PAYLOAD_LIST.add((String) payloadmap.get("value"));
56 | }
57 | }
58 |
59 | // @d1sbb修改,加载 domain
60 | ArrayList domain = (ArrayList) map.get("domain");
61 | if (domain != null) {
62 | for (LinkedHashMap domainMap : domain) {
63 | Data.DOMAIN_LIST.add((String) domainMap.get("value"));
64 | }
65 | }
66 |
67 | // @d1sbb修改,header
68 | ArrayList header = (ArrayList) map.get("header");
69 | if (header != null) {
70 | for (LinkedHashMap headerMap : header) {
71 | String key = (String) headerMap.get("key");
72 | String value = (String) headerMap.get("value");
73 | Data.HEADER_MAP.put(key, value);
74 | }
75 | }
76 |
77 |
78 | Main.LOG.logToOutput("[INFO] 已加载配置文件.");
79 | }
80 |
81 | } catch (Exception e) {
82 | Main.LOG.logToError("[ERROR] Yaml配置文件加载异常.");
83 | }
84 | }
85 |
86 | public static boolean checkConfigDir() {
87 | // 检查存放配置文件的目录是否存在,若不存在则创建目录
88 | File rule_dir = new File(Config.CONFIG_DIR);
89 | if (!rule_dir.exists()) {
90 | Main.LOG.logToOutput("[INFO] 配置文件目录不存在, 即将自动创建.");
91 | boolean created = rule_dir.mkdirs(); // 创建目录及其父目录
92 | if (created) {
93 | Main.LOG.logToOutput("[INFO] 配置文件目录创建成功: " + rule_dir.getAbsolutePath());
94 | return true;
95 | } else {
96 | Main.LOG.logToOutput("[ERROR] 配置文件目录创建失败.");
97 | return false;
98 | }
99 | } else {
100 | Main.LOG.logToOutput("[INFO] 配置文件目录已存在.");
101 | return true;
102 | }
103 |
104 | }
105 |
106 |
107 | public static void exportToYaml() {
108 | Map yamlData = new LinkedHashMap<>();
109 |
110 | LinkedHashMap configMap = new LinkedHashMap<>();
111 | LinkedHashMap basicSetting = new LinkedHashMap<>();
112 | LinkedHashMap domainSetting = new LinkedHashMap<>();
113 | LinkedHashMap payloadSetting = new LinkedHashMap<>();
114 | LinkedHashMap authHeaderSetting = new LinkedHashMap<>();
115 | basicSetting.put("turn_on", UserConfig.TURN_ON);
116 | basicSetting.put("listen_proxy", UserConfig.LISTEN_PROXY);
117 | basicSetting.put("listen_repeter", UserConfig.LISTEN_REPETER);
118 | basicSetting.put("language", UserConfig.LANGUAGE.name());
119 | domainSetting.put("black_or_white_choose", UserConfig.BLACK_OR_WHITE_CHOOSE);
120 | domainSetting.put("include_subdomain", UserConfig.INCLUDE_SUBDOMAIN);
121 | payloadSetting.put("append_mod", UserConfig.APPEND_MOD);
122 | payloadSetting.put("param_url_encode", UserConfig.PARAM_URL_ENCODE);
123 | authHeaderSetting.put("unauth", UserConfig.UNAUTH);
124 | configMap.put("basic_setting", basicSetting);
125 | configMap.put("domain_setting", domainSetting);
126 | configMap.put("payload_setting", payloadSetting);
127 | configMap.put("auth_header_setting", authHeaderSetting);
128 |
129 | ArrayList> payloadList = null;
130 | if (Data.PAYLOAD_LIST.size() > 0) {
131 | payloadList = new ArrayList<>();
132 | for (String payload : Data.PAYLOAD_LIST) {
133 | LinkedHashMap payloadMap = new LinkedHashMap<>();
134 | payloadMap.put("value", payload);
135 | payloadList.add(payloadMap);
136 | }
137 | }
138 | ArrayList> domainList = null;
139 | if (Data.DOMAIN_LIST.size() > 0) {
140 | domainList = new ArrayList<>();
141 | for (String domain : Data.DOMAIN_LIST) {
142 | LinkedHashMap domainMap = new LinkedHashMap<>();
143 | domainMap.put("value", domain);
144 | domainList.add(domainMap);
145 | }
146 | }
147 | ArrayList> headerList = null;
148 | if (Data.HEADER_MAP.size() > 0) {
149 | headerList = new ArrayList<>();
150 | for (Map.Entry entry : Data.HEADER_MAP.entrySet()) {
151 | LinkedHashMap headerMap = new LinkedHashMap<>();
152 | headerMap.put("key", entry.getKey());
153 | headerMap.put("value", entry.getValue());
154 | headerList.add(headerMap);
155 | }
156 | }
157 | //@d1sbb修改,新增导出时domain、header配置
158 | yamlData.put("config", configMap);
159 | yamlData.put("payload", payloadList);
160 | yamlData.put("domain", domainList);
161 | yamlData.put("header", headerList);
162 |
163 | // 配置YAML选项(保持块格式)
164 | DumperOptions options = new DumperOptions();
165 | options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
166 | options.setIndent(2);
167 | options.setPrettyFlow(true);
168 |
169 | // 写入文件
170 | try {
171 | Yaml yaml = new Yaml(options);
172 | try (FileWriter writer = new FileWriter(Config.CONFIG_FILE)) {
173 | yaml.dump(yamlData, writer);
174 | }
175 | } catch (Exception e) {
176 | Main.LOG.logToError("[ERROR] 导出Yaml配置文件出现异常.");
177 | }
178 |
179 |
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/src/main/resources/lang_en_US.properties:
--------------------------------------------------------------------------------
1 | language=English
2 | basicTitleLabel.text=--------------------------Basic Setting--------------------------
3 | turnOnCheckBox.text=TurnOnPlugin
4 | listenProxyCheckBox.text=ListenOnProxy
5 | listenRepeterCheckBox.text=ListenOnRepeter
6 | cleanRequestItemButton.text=CleanRequest
7 | domainTitleLabel.text=--------------------------Domain Setting-------------------------
8 | blackListRadioButton.text=BlackList
9 | whiteListRadioButton.text=WhiteList
10 | addDomainButton.text=Add
11 | editDomainButton.text=Edit
12 | removeDomainButton.text=Remove
13 | includeSubDomainCheckBox.text=SubDomain
14 | payloadTitleLabel.text=-------------------------Payload Setting-------------------------
15 | addPayloadButton.text=Add
16 | editPayloadButton.text=Edit
17 | removePayloadButton.text=Remove
18 | appendModCheckBox.text=Append
19 | emptyParamCheckBox.text=Empty
20 | paramURLEncodeCheckBox.text=URLEncode
21 | authHeaderTitleLabel.text=-----------------------Auth Header Setting-----------------------
22 | addAuthHeaderButton.text=Add
23 | editAuthHeaderButton.text=Edit
24 | removeAuthHeaderButton.text=Remove
25 | unauthCheckBox.text=UnAuth
26 | searchButton.text=Search
27 | cleanSearchResultButton.text=CleanSearchResult
28 | addDomainTitle.text=Add Domain
29 | addPayloadTitle.text=Add Payload
30 | addAuthHeaderTitle.text=Add Auth Header
31 | editDomainTitle.text=Edit Domain
32 | editPayloadTitle.text=Edit Payload
33 | editAuthHeaderTitle.text=Edit Auth Header
34 | addDomainTitledBorder.text=Add one domain per line
35 | addPayloadTitledBorder.text=Add one payload per line
36 | addHeaderTitledBorder.text=Add one header per line
--------------------------------------------------------------------------------
/src/main/resources/lang_zh_CN.properties:
--------------------------------------------------------------------------------
1 | language=简体中文
2 | basicTitleLabel.text=-----------------------------基本设置-----------------------------
3 | turnOnCheckBox.text=启用插件
4 | listenProxyCheckBox.text=监听Proxy
5 | listenRepeterCheckBox.text=监听Repeter
6 | cleanRequestItemButton.text=清空请求记录
7 | domainTitleLabel.text=-----------------------------域名设置-----------------------------
8 | blackListRadioButton.text=黑名单
9 | whiteListRadioButton.text=白名单
10 | addDomainButton.text=添加
11 | editDomainButton.text=编辑
12 | removeDomainButton.text=删除
13 | includeSubDomainCheckBox.text=包含子域名
14 | payloadTitleLabel.text=---------------------------Payload设置---------------------------
15 | addPayloadButton.text=添加
16 | editPayloadButton.text=编辑
17 | removePayloadButton.text=删除
18 | appendModCheckBox.text=追加模式
19 | emptyParamCheckBox.text=参数置空
20 | paramURLEncodeCheckBox.text=URL编码
21 | authHeaderTitleLabel.text=-------------------------Auth Header设置-------------------------
22 | addAuthHeaderButton.text=添加
23 | editAuthHeaderButton.text=编辑
24 | removeAuthHeaderButton.text=删除
25 | unauthCheckBox.text=未授权访问
26 | searchButton.text=查找
27 | cleanSearchResultButton.text=清空查找记录
28 | addDomainTitle.text=添加域名
29 | addPayloadTitle.text=添加Payload
30 | addAuthHeaderTitle.text=添加AuthHeader
31 | editDomainTitle.text=编辑域名
32 | editPayloadTitle.text=编辑Payload
33 | editAuthHeaderTitle.text=编辑AuthHeader
34 | addDomainTitledBorder.text=添加域名 每行一个
35 | addPayloadTitledBorder.text=添加Payload 每行一个
36 | addHeaderTitledBorder.text=添加Header 每行一个
--------------------------------------------------------------------------------