├── .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 | 9 | 10 | 14 | 15 | 19 | 20 | 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 | image-20250303233548430 16 | 17 | image-20250303234145025 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 | image-20250304001209634 41 | 42 | image-20250304001418561 43 | 44 | - **编辑域名/IP** 45 | - 选中需要编辑的条目 ,点击左侧编辑,修改后确定。修改后数据不可重复,如果选中多条,则默认修改选中第一条。 46 | 47 | image-20250304002312839 48 | 49 | image-20250304002640059 50 | 51 | - **删除域名/IP** 52 | - 选中需要删除的条目,点击左侧删除,即可删除对应数据,支持多行选中删除。 53 | 54 | image-20250304003218031 55 | 56 | image-20250304003238900 57 | 58 | - **包含子域名** 59 | - 以 `qq.com` 为例,如不勾选包含子域名,则 host 为 `y.qq.com` 的请求无法被捕获。 60 | 61 | #### Payload 设置 62 | 63 | > Tips: 当 **列表中有数据** 时,才会启用该模块功能。 64 | 65 | - **添加payload** 66 | - 点击左侧添加按钮即可添加 payload,每行一个,不可重复。 67 | 68 | image-20250304211651127 69 | 70 | image-20250304211737496 71 | 72 | - **编辑payload** 73 | - 选中需要编辑的条目 ,点击左侧编辑,修改后确定。修改后数据不可重复,如果选中多条,则默认修改选中第一条。 74 | 75 | image-20250304212242627 76 | 77 | image-20250304212334916 78 | 79 | - **删除payload** 80 | - 选中需要删除的条目,点击左侧删除,即可删除对应数据,支持多行选中删除。 81 | 82 | image-20250304214210463 83 | 84 | image-20250304214245111 85 | 86 | - **参数置空** 87 | - 勾选后可增加一项为 `空` 的 payload,在 fuzz 时会将参数值置空。 88 | 89 | image-20250304214330266 90 | 91 | image-20250304215106091 92 | 93 | - **URL编码** 94 | - 勾选后,payload 中若存在特殊字符,非 json 格式的参数在 fuzz 时将对特殊字符进行 URL 编码。 95 | 96 | image-20250304215342739 97 | 98 | #### Auth Header 设置 99 | 100 | > Tips: 当 **列表中有Header数据** 或 **勾选未授权访问** 时,才会启用该模块功能。 101 | 102 | - **添加Header** 103 | - 点击左侧添加按钮即可添加需要进行替换的 Header,每行一个,不可重复。 104 | 105 | image-20250304222451866 106 | 107 | image-20250304222531391 108 | 109 | image-20250304222609482 110 | 111 | - **编辑Header** 112 | - 选中需要编辑的条目 ,点击左侧编辑,修改后确定。修改后数据不可重复,如果选中多条,则默认修改选中第一条。 113 | 114 | image-20250304222912568 115 | 116 | image-20250304222943732 117 | 118 | - **未授权访问** 119 | - 勾选后,在 fuzz 时会去除列表中设置的所有 Header,进行未授权访问测试。 120 | 121 | image-20250304224510635 122 | 123 | #### 查找功能 124 | 125 | 可根据设置的查找作用域在 `request` 或 `response` 中查找是否含有输入的字符串信息,不区分大小写,不支持中文查找。 126 | 127 | image-20250304231940839 128 | 129 | #### 右键菜单 130 | 131 | 可通过右键菜单发送到插件,无视域名/IP范围,无视去重限制。 132 | 133 | image-20250304235348677 134 | 135 | image-20250304235439669 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 每行一个 --------------------------------------------------------------------------------