├── .gitattributes
├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── README-EN.MD
├── README.md
├── doc
├── APIFinder运行流程.png
├── webpack简单格式.字符型.png
└── webpack简单格式.数字型.png
├── pom.xml
├── rule
├── finger-important-cn.json
└── finger-important-en.json
└── src
└── main
├── java
├── EnumType
│ ├── LocationType.java
│ ├── MatchType.java
│ └── RiskLevel.java
├── burp
│ ├── AnalyseInfo.java
│ ├── BurpExtender.java
│ └── IProxyScanner.java
├── database
│ ├── AnalyseHostResultTable.java
│ ├── AnalyseHostUnVisitedUrls.java
│ ├── AnalyseUrlResultTable.java
│ ├── CommonDeleteLine.java
│ ├── CommonFetchData.java
│ ├── CommonUpdateStatus.java
│ ├── Constants.java
│ ├── DBService.java
│ ├── PathTreeTable.java
│ ├── RecordPathTable.java
│ ├── RecordUrlTable.java
│ ├── ReqDataTable.java
│ ├── ReqMsgDataTable.java
│ ├── TableLineDataModelBasicHostSQL.java
│ ├── TableLineDataModelBasicUrlSQL.java
│ └── UnionTableSql.java
├── model
│ ├── AccessedUrlInfo.java
│ ├── AnalyseHostResultModel.java
│ ├── AnalyseUrlResultModel.java
│ ├── BasicHostTableLineDataModel.java
│ ├── BasicHostTableTabDataModel.java
│ ├── BasicUrlTableLineDataModel.java
│ ├── BasicUrlTableTabDataModel.java
│ ├── FindPathModel.java
│ ├── FingerPrintRule.java
│ ├── FingerPrintRulesWrapper.java
│ ├── HttpMsgInfo.java
│ ├── HttpRespInfo.java
│ ├── HttpUrlInfo.java
│ ├── PathToUrlsModel.java
│ ├── PathTreeModel.java
│ ├── RecordHashMap.java
│ ├── RecordPathDirsModel.java
│ ├── RecordPathModel.java
│ ├── ReqMsgDataModel.java
│ ├── ReqUrlRespStatusModel.java
│ ├── RespFieldsModel.java
│ └── UnVisitedUrlsModel.java
├── ui
│ ├── BasicHostConfigPanel.java
│ ├── BasicHostInfoPanel.java
│ ├── BasicUrlConfigPanel.java
│ ├── BasicUrlInfoPanel.java
│ ├── FingerTabRender
│ │ ├── ButtonEditor.java
│ │ ├── ButtonRenderer.java
│ │ ├── CenterRenderer.java
│ │ ├── HeaderIconTypeRenderer.java
│ │ └── LeftRenderer.java
│ ├── MainTabRender
│ │ ├── ColorInfoCellRenderer.java
│ │ ├── HasImportantCellRenderer.java
│ │ ├── RunStatusCellRenderer.java
│ │ └── TableHeaderWithTips.java
│ ├── RuleConfigPanel.java
│ └── Tabs.java
├── utilbox
│ ├── ByteArrayUtils.java
│ ├── CharsetUtils.java
│ ├── DomainUtils.java
│ ├── HelperPlus.java
│ ├── IPAddressUtils.java
│ └── TextUtils.java
└── utils
│ ├── AnalyseInfoUtils.java
│ ├── AnalyseUriFilter.java
│ ├── BurpFileUtils.java
│ ├── BurpHttpUtils.java
│ ├── BurpPrintUtils.java
│ ├── BurpSitemapUtils.java
│ ├── CastUtils.java
│ ├── ConfigUtils.java
│ ├── ElementUtils.java
│ ├── PathTreeUtils.java
│ ├── RegularUtils.java
│ ├── RespFieldCompareutils.java
│ ├── RespHashUtils.java
│ ├── RespTitleUtils.java
│ ├── RespWebpackJsParser.java
│ └── UiUtils.java
└── resources
├── conf
└── finger-important.json
└── icon
├── addButtonIcon.png
├── convenientOperationIcon.png
├── copyIcon.png
├── customizeIcon.png
├── deleteButton.png
├── editButton.png
├── exportItem.png
├── filterIcon.png
├── findUrlFromJS.png
├── importItem.png
├── importantButtonIcon.png
├── insertNewPathIcon.png
├── moreButton.png
├── noFindUrlFromJS.png
├── openButtonIcon.png
├── refreshButton.png
├── refreshButton2.png
├── resetItem.png
├── runningButton.png
├── saveItem.png
├── searchButton.png
├── shutdownButtonIcon.png
└── urlIcon.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
3 |
4 | # This workflow uses actions that are not certified by GitHub.
5 | # They are provided by a third-party and are governed by
6 | # separate terms of service, privacy policy, and support
7 | # documentation.
8 |
9 | name: Java CI with Maven
10 |
11 | on:
12 | # # 在创建新标签时触发
13 | # push:
14 | # tags:
15 | # - '*' # 匹配任何标签
16 | # 在发布新版本时触发
17 | release:
18 | types: [created]
19 |
20 |
21 | jobs:
22 | build:
23 |
24 | runs-on: ubuntu-latest
25 |
26 | steps:
27 | - uses: actions/checkout@v3
28 | - name: Set up JDK 8
29 | uses: actions/setup-java@v3
30 | with:
31 | java-version: '8'
32 | distribution: 'temurin'
33 | cache: maven
34 | - name: Build with Maven
35 | run: mvn -B package --file pom.xml
36 |
37 | - name: Archive Jar file
38 | uses: actions/upload-artifact@v4
39 | with:
40 | name: jar-with-dependencies
41 | path: ./target/*-jar-with-dependencies.jar # 指定二进制文件的路径
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 | replay_pid*
25 | .idea/
26 | target/
27 | *.db
28 |
--------------------------------------------------------------------------------
/README-EN.MD:
--------------------------------------------------------------------------------
1 | # BurpAPIFinder-Refactor
2 |
3 | This plugin is a refactored development based on APIFinder UI [https://github.com/shuanx/BurpAPIFinder]
4 |
5 | ### Tips:
6 |
7 | Hover over the buttons to see their descriptions; currently, there is no documentation available.
8 |
9 | To ensure data integrity, all response body data is saved. If no whitelist is specified, it is recommended to clear the database for each new project to avoid occupying too much space.
10 |
11 | ### Download Link
12 |
13 | Visit https://github.com/winezer0/APIFinderPlus/actions
14 | ```
15 | -> Latest workflow [Green indicates valid]
16 | -> Summary
17 | -> Download jar-with-dependencies at the bottom
18 | ```
19 |
20 | ### Plugin Goals
21 |
22 | Create the most comprehensive API mining tool,
23 | Reduce manual path extraction and testing,
24 | Supplement operations that cannot be handled automatically,
25 |
26 | 1. Support extraction of sensitive information, URLs, and URI information from response data.
27 | 2. Support automatic calculation of actual URLs based on known path information.
28 | 3. Support automatic access to mined URL information for recursive information extraction.
29 | 4. Support simple format extraction and splicing of webpack's JS files (limited format but high accuracy).
30 |
31 | Combination format abcd.xxxx.js
32 | 
33 |
34 | Combination format 1234.xxxx.js
35 | 
36 |
37 | ### Notes
38 |
39 | 1. All data is stored and read using SQLite, which is slower than in-memory operations.
40 | 2. When there are too many targets, executing tasks like refreshing unvisited URLs or automatic recursive scanning may consume a significant amount of memory.
41 | 3. Due to the abundance of features, hover over text or buttons to view operation descriptions.
42 |
43 | ### Basic Workflow [Old Version]
44 |
45 | 
46 |
47 |
48 | ### Main Tasks [Updated]
49 |
50 | ```
51 | Scheduled Task Thread:
52 | - Query the ReqDataTable in the database
53 | - Check if there are any unanalyzed messages
54 | - Match and extract sensitive information, URLs, and PATHs from requests/responses based on rule configurations
55 | - Save analysis results to the AnalyseUrlResultTable in the database
56 | - Query the AnalyseUrlResultTable in the database
57 | - Categorize new results in the AnalyseUrlResultTable by RootUrl and insert them into the AnalyseHostResultTable
58 |
59 | - autoPathsToUrlsIsOpen: Enable automatic URL calculation based on paths (disabled by default, supports manual operation)
60 | - Query the RecordPathTable in the database
61 | - Check if there are valid request PATHs not yet added to the website path tree
62 | - Calculate/update the PathTree based on recorded URL paths
63 | - Save analysis results to the PathTree table
64 |
65 | - Query the database by combining PathTreeTable and AnalyseHostResultTable
66 | - Check if there are updated PathTrees that have not been recalculated for PATH URLs
67 | - Calculate possible prefixes for new PATHs based on the updated PathTree
68 | - Save analysis results to the PATH-calculated URL in the AnalyseHostResultTable
69 |
70 | - autoRecursiveIsOpen: Enable automatic access to unvisited URLs
71 | - Query the AnalyseHostResultTable in the database
72 | - Check if all URLs have been visited
73 | - Construct HTTP requests for unvisited URLs
74 | ```
75 |
76 | ### Internal Rule Explanation
77 | ```
78 | Note: Rules starting with CONF_ and located in "config" are internal rules and are not used for information matching.
79 |
80 | CONF_DEFAULT_PERFORMANCE: Default performance configuration
81 | "maxPatterChunkSizeDefault=1000000": Maximum response length for regex matching in one operation. Changes take effect immediately after saving.
82 | "maxStoreRespBodyLenDefault=1000000": Maximum size of response body saved in the database. Changes take effect immediately after saving.
83 | "monitorExecutorIntervalsDefault=4": Interval (in seconds) for executing extraction checks. Changes take effect immediately after saving.
84 | Other default UI button-related parameters take effect after saving and restarting the plugin.
85 |
86 | Custom automatic request scanning methods:
87 | 1. CONF_RECURSE_REQ_HTTP_METHODS: Customize supported HTTP request methods (one method per line).
88 | 2. CONF_RECURSE_REQ_HTTP_PARAMS: Configure keywords to prevent automatic scanning of URLs, such as [logout, del], to avoid accidental deletions.
89 | 3. CONF_BLACK_RECURSE_REQ_PATH_KEYS: Support custom request parameters (write one set of request parameters per line; multiple lines will be iterated).
90 | Note: The request headers for the current request are dynamically obtained from the current URL request body. Custom request header functionality will be added based on user needs.
91 |
92 |
93 | CONF_WHITE_ROOT_URL: Keywords for allowed RootUrls to scan.
94 | CONF_BLACK_ROOT_URL: Keywords for RootUrls prohibited from [monitoring scans | URL extraction | PATH extraction].
95 | CONF_BLACK_URI_PATH_KEYS: Keywords for URI paths prohibited from [monitoring scans | URL extraction | PATH extraction].
96 | CONF_BLACK_URI_EXT_EQUAL: File extensions prohibited from [monitoring scans | URL extraction | PATH extraction].
97 | CONF_BLACK_AUTO_RECORD_PATH: Keywords for RootUrls prohibited from automatic PATH recording.
98 | CONF_BLACK_AUTO_RECURSE_SCAN: Keywords for RootUrls prohibited from automatic scanning of unvisited URLs.
99 | CONF_WHITE_RECORD_PATH_STATUS: Response status codes allowed for automatic PATH recording.
100 | CONF_BLACK_RECORD_PATH_TITLE: Response titles prohibited from automatic PATH recording.
101 | CONF_BLACK_EXTRACT_PATH_EQUAL: URI paths prohibited from extraction [equal to any element in this list].
102 | CONF_BLACK_EXTRACT_INFO_KEYS: Sensitive information prohibited from extraction [containing any element in this list].
103 | CONF_REGULAR_EXTRACT_URIS: Regular expressions for extracting response URIs/URLs.
104 | ```
105 |
106 | ### Matching Rule Explanation
107 |
108 | ```
109 |
110 | Matching Location ("location" field):
111 | locations =
112 | CONFIG("config"): Configuration rules, not involved in matching.
113 | PATH("path"): Request path.
114 | TITLE("title"): Response title
(.*).
115 | BODY("body"): Response body.
116 | HEADER("header"): Response header.
117 | RESPONSE("response"): Entire response content.
118 | ICON_HASH("icon_hash"): Hash of the ico file, only obtained when the file is of ico type.
119 |
120 |
121 | Matching Method ("matchType" field):
122 | 1. Keyword Matching
123 | ANY_KEYWORDS("any_keywords"): Match any keyword rule (common). Supports || and && syntax.
124 | ALL_KEYWORDS("all_keywords"): Match all keyword rules (rare). Supports || and && syntax.
125 | 2. Regex Matching
126 | ANY_REGULAR("any_regular"): Match any regex rule (common).
127 | ALL_REGULAR("all_regular"): Match all regex rules (rare).
128 |
129 | Actual Matching Rules ("matchKeys": [] list):
130 | 1. Keyword Matching Rule Writing
131 | Each line is a keyword extraction matching rule.
132 | Each line's content supports multiple keywords joined together using the symbol 【|】.
133 | Example:
134 | "matchType": "any_keywords" + "matchKeys": ["fzhm1&&total1&&rows1", "fzhm2&&total2&&rows2"],
135 | Indicates that it requires fzhm1, total1, and rows1 keywords simultaneously or fzhm2, total2, and rows2 simultaneously.
136 |
137 | "matchType": "all_keywords" + "matchKeys": ["fzhm1||fzhm2","total1||total2"],
138 | Indicates that it requires at least one fzhm* and at least one total* keyword simultaneously.
139 | Note:
140 | 1. This rule differs from the original version.
141 | 2. Since it uses syntax symbols 【|| &&】, matching keywords cannot contain 【|| &&】.
142 |
143 | 2. Regex Matching Rule Writing
144 | Each line is a regex extraction matching rule.
145 |
146 |
147 |
148 | Other Keywords:
149 | "accuracy": Rule accuracy.
150 | "describe": Rule description.
151 | "isImportant": Whether the matching result is important.
152 | "isOpen": Whether the rule is enabled.
153 | "type": Rule category.
154 |
155 | ```
156 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BurpAPIFinder-Refactor
2 |
3 | 本插件参考 APIFinder UI 进行重构开发 [https://github.com/shuanx/BurpAPIFinder]
4 |
5 |
6 | ### TODO规划
7 |
8 | ```
9 | 优先修复BUG
10 | 优先增强功能
11 | 延后文档编写
12 | ```
13 |
14 | 欢迎提交增强功能思路.
15 |
16 |
17 | ### 使用提示:
18 |
19 | 目前没有文档描述, 暂时没有时间进行详细文档编写, 希望用户可以提交初步使用文档,我将进行更新
20 |
21 | 按钮功能说明: 请将鼠标悬浮在按钮上,会进行提示
22 |
23 | 数据库占用大: 为了数据完整性基本保存了所有响应体数据 如果没有指定白名单的话,建议每个新项目都清理一次数据库,否则将会占用大量空间
24 |
25 | 自定义配置文件: 将自己的配置文件存放在插件所在目录即可.
26 |
27 | ### 下载地址
28 |
29 | 访问 https://github.com/winezer0/APIFinderPlus/actions
30 | ```
31 | -> 最新工作流【绿色表示有效】
32 | -> Summary
33 | -> 最下面下载 jar-with-dependencies
34 | ```
35 |
36 | ### 插件目标
37 |
38 | 做最全面的API挖掘工具、
39 | 减少手动拼接path的提取测试、
40 | 补充无法自动处理的操作、
41 |
42 | 1、支持 响应 信息中的敏感信息、URL、URI信息提取.
43 | 2、支持 自动基于 已知路径信息 计算PATH 对应的实际URL.
44 | 3、支持 自动访问 挖掘出来的URL信息 进行递归式的信息提取.
45 | 4、支持 对webpack的js的简单格式的拼接提取 (限制格式,但准确度高)
46 |
47 | 组合形式 abcd.xxxx.js
48 | 
49 |
50 | 组合形式 1234.xxxx.js
51 | 
52 |
53 | ### 注意事项
54 |
55 | 1、所有数据都是存储sqlite进行读写、比内存操作慢一些.
56 | 2、当目标数量过多时、执行 刷新未访问URL、自动递归扫描 任务时,占用的内存应该是较大的。
57 | 3、因为功能过多,使用请将鼠标悬浮到文本或按钮上,查看操作描述
58 |
59 | ### 基本流程 【旧版】
60 |
61 | 
62 |
63 |
64 | ### 主要任务 【更新】
65 |
66 | ```
67 | 定时任务线程:
68 | - 查询数据库 ReqDataTable 表
69 | - 是否存在未分析的 消息
70 | - 根据规则配置 匹配 提取 请求|响应中的敏感信息和URL、PATH
71 | - 分析结果存入数据库 AnalyseUrlResultTable 表
72 | - 查询数据库AnalyseUrlResultTable 表
73 | - 将 AnalyseUrlResultTable 表中的新结果按照RootUrl分类插入到 AnalyseHostResultTable 表
74 |
75 | - autoPathsToUrlsIsOpen 开启自动基于路径计算URL功能 (默认关闭、支持手动)
76 | - 查询数据库 RecordPathTable
77 | - 检查是否存在没有加入到 网站路径树 的有效请求PATH
78 | - 根据已记录的URL路径计算/更新Pathree
79 | - 分析结果存入 PathTree 表
80 |
81 | - 查询数据库 联合分析 PathTreeTable 和 AnalyseHostResultTable 表
82 | - 检查是否存在已经更新的PathTree 但是还没有重新计算过PATH URL的数据
83 | - 根据已更新的Pathree计算新的PATH可能的前缀
84 | - 分析结果存入 AnalyseHostResultTable 的 PATH计算URL
85 |
86 | - autoRecursiveIsOpen 开启自动访问未访问的URL
87 | - 查询数据库 AnalyseHostResultTable 表
88 | - 判断是否URL是否都已经被访问
89 | - 对未访问URL构造HTTP请求
90 | ```
91 | ### 内部规则说明
92 | ```
93 | 注意:对于CONF_开头和location为config的规则,属于内部规则,不用于信息匹配。
94 |
95 | CONF_DEFAULT_PERFORMANCE: 默认的性能配置
96 | "maxPatterChunkSizeDefault=1000000", 正则匹配一次性处理的响应长度 修改保存后立即生效
97 | "maxStoreRespBodyLenDefault=1000000", 数据库保存响应体的最大大小 修改保存后立即生效
98 | "monitorExecutorIntervalsDefault=4", 几秒钟执行一次检查提取操作 修改保存后立即生效
99 | 其他默认UI按钮相关的参数,修改保存后,重启插件生效
100 |
101 | 自定义自动请求扫描的方法
102 | 1、CONF_RECURSE_REQ_HTTP_METHODS 自定义 任意 请求方法列表支持 【每行一种请求方法】
103 | 2、CONF_RECURSE_REQ_HTTP_PARAMS 配置禁止自动扫描的URL关键字 如【logout、del】等 防止误删除
104 | 3、CONF_BLACK_RECURSE_REQ_PATH_KEYS 支持自定义请求参数 【一次的请求参数写在一行即可,多行会遍历】
105 | 注意:当前请求的请求头是基于当前URL请求体中动态获取的,后续根据用户需求添加自定义请求头功能。
106 |
107 |
108 | CONF_WHITE_ROOT_URL: 允许扫描的目标RootUrl关键字
109 | CONF_BLACK_ROOT_URL: 禁止进行[监听扫描|URL提取|PATH提取]的 RootUrl关键字
110 | CONF_BLACK_URI_PATH_KEYS: 禁止进行[监听扫描|URL提取|PATH提取]的 URI 路径关键字
111 | CONF_BLACK_URI_EXT_EQUAL: 禁止进行[监听扫描|URL提取|PATH提取]的 URI 文件扩展名
112 | CONF_BLACK_AUTO_RECORD_PATH: 禁止自动进行有效PATH记录的目标RootUrl关键字
113 | CONF_BLACK_AUTO_RECURSE_SCAN: 禁止自动进行未访问URL扫描的目标RootUrl关键字
114 | CONF_WHITE_RECORD_PATH_STATUS: 允许自动进行有效PATH记录的响应状态码
115 | CONF_BLACK_RECORD_PATH_TITLE: 禁止自动进行有效PATH记录的响应标题
116 | CONF_BLACK_EXTRACT_PATH_EQUAL: 禁止提取的URI路径[等于]此项任一元素
117 | CONF_BLACK_EXTRACT_INFO_KEYS: 禁止提取的敏感信息[包含]此项任一元素
118 | CONF_REGULAR_EXTRACT_URIS: 提取响应URI|URL的正则表达式
119 | ```
120 |
121 | ### 匹配规则说明
122 |
123 | ```
124 |
125 | 匹配位置("location" 字段):
126 | locations =
127 | CONFIG("config"), //配置规则、不参与匹配
128 | PATH("path"), //请求路径
129 | TITLE("title"), //响应标题 (.*)
130 | BODY("body"), //响应正文
131 | HEADER("header"), //响应头部
132 | RESPONSE("response"), //全部响应内容
133 | ICON_HASH("icon_hash"); //ico文件hash 只有在文件是ico类型时才会获取
134 |
135 |
136 | 匹配方法("matchType"字段):
137 | 1、关键字匹配
138 | ANY_KEYWORDS("any_keywords"), //匹配任意关键字规则 常见 支持|| &&语法
139 | ALL_KEYWORDS("all_keywords"), //匹配所有关键字规则 少见 支持|| &&语法
140 | 2、正则匹配
141 | ANY_REGULAR("any_regular"), //要求匹配任意正则 常见
142 | ALL_REGULAR("all_regular"); //要求匹配所有正则 少见
143 |
144 | 实际匹配规则("matchKeys" : [] 列表):
145 | 1、关键字匹配规则编写
146 | 每行是一个关键字提取匹配规则、
147 | 每行的内容支持由多个关键字拼接组成,拼接符号是 【|】
148 | 举例:
149 | "matchType": "any_keywords" + "matchKeys": ["fzhm1&&total1&&rows1", "fzhm2&&total2&&rows2"],
150 | 表示要求 同时含有fzhm1、total1、rows1 关键字 或 同时完全含有fzhm2、total2、rows2
151 |
152 | "matchType": "all_keywords" + "matchKeys": ["fzhm1||fzhm2","total1||total2"],
153 | 表示要求 同时含有 至少一个fzhm*、 至少一个total* 关键字
154 | 注意:
155 | 1、本规则和原版的有差异,
156 | 2、由于使用了语法符号 【|| && 】 ,因此不能让匹配关键字中包含【|| &&】
157 |
158 | 2、正则匹配规则编写
159 | 每行是一个正则提取匹配规则
160 |
161 |
162 |
163 | 其他关键字:
164 | "accuracy": 规则准确度
165 | "describe": 规则描述
166 | "isImportant": 匹配结果是否重要信
167 | "isOpen": 是否启用规则
168 | "type": 规则分类
169 |
170 | ```
171 |
--------------------------------------------------------------------------------
/doc/APIFinder运行流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winezer0/APIFinderPlus/a5336834bbb87b05427a5c6e5fb460d380d910ca/doc/APIFinder运行流程.png
--------------------------------------------------------------------------------
/doc/webpack简单格式.字符型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winezer0/APIFinderPlus/a5336834bbb87b05427a5c6e5fb460d380d910ca/doc/webpack简单格式.字符型.png
--------------------------------------------------------------------------------
/doc/webpack简单格式.数字型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winezer0/APIFinderPlus/a5336834bbb87b05427a5c6e5fb460d380d910ca/doc/webpack简单格式.数字型.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | APIFinderPlus
9 | 1.1.8
10 |
11 |
12 | 8
13 | 8
14 |
15 |
16 |
17 |
18 |
19 | net.portswigger.burp.extender
20 | burp-extender-api
21 | 2.3
22 |
23 |
24 |
25 | com.alibaba.fastjson2
26 | fastjson2
27 | 2.0.40
28 |
29 |
30 |
31 |
32 | org.xerial
33 | sqlite-jdbc
34 | 3.45.3.0
35 |
36 |
37 |
38 |
39 | org.slf4j
40 | slf4j-nop
41 | 1.6.1
42 | compile
43 |
44 |
45 |
46 |
47 |
48 | commons-net
49 | commons-net
50 | 3.10.0
51 |
52 |
53 | com.github.seancfoley
54 | ipaddress
55 | 5.3.3
56 |
57 |
58 |
59 |
60 | com.google.guava
61 | guava
62 | 33.1.0-jre
63 |
64 |
65 |
66 | dnsjava
67 | dnsjava
68 | 2.1.9
69 |
70 |
71 |
72 | commons-io
73 | commons-io
74 | 2.16.1
75 |
76 |
77 |
78 | org.apache.commons
79 | commons-text
80 | 1.12.0
81 |
82 |
83 |
84 |
85 |
86 | org.apache.maven.plugins
87 | maven-assembly-plugin
88 |
89 |
90 | package
91 |
92 | single
93 |
94 |
95 |
96 |
97 |
98 | jar-with-dependencies
99 |
100 |
101 |
102 |
103 | org.apache.maven.plugins
104 | maven-compiler-plugin
105 | 3.1
106 |
107 | 8
108 | 8
109 | utf-8
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/src/main/java/EnumType/LocationType.java:
--------------------------------------------------------------------------------
1 | package EnumType;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public enum LocationType {
7 | CONFIG("config"),
8 | PATH("path"),
9 | TITLE("title"),
10 | BODY("body"),
11 | HEADER("header"),
12 | RESPONSE("response"),
13 | ICON_HASH("icon_hash");
14 |
15 | private final String value;
16 |
17 | // 构造方法
18 | LocationType(String value) {
19 | this.value = value;
20 | }
21 |
22 | // 获取枚举对应的字符串值
23 | public String getValue() {
24 | return value;
25 | }
26 |
27 | // 根据字符串值获取对应的枚举
28 | public static LocationType fromValue(String value) {
29 | for (LocationType type : LocationType.values()) {
30 | if (type.getValue().equalsIgnoreCase(value)) {
31 | return type;
32 | }
33 | }
34 | throw new IllegalArgumentException("Invalid location type: " + value);
35 | }
36 |
37 | /**
38 | * 将 LocationType 枚举的所有值转换为 List。
39 | *
40 | * @return 包含所有 LocationType 值的列表
41 | */
42 | public static String[] getValues() {
43 | // 创建一个可变的列表来存储字符串值
44 | List locationTypes = new ArrayList<>();
45 | // 遍历枚举值并提取其对应的字符串值
46 | for (LocationType type : LocationType.values()) {
47 | locationTypes.add(type.getValue());
48 | }
49 | // 将 List 转换为 String[]
50 | return locationTypes.toArray(new String[0]);
51 | }
52 | }
--------------------------------------------------------------------------------
/src/main/java/EnumType/MatchType.java:
--------------------------------------------------------------------------------
1 | package EnumType;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public enum MatchType {
7 | CONFIG("config"),
8 | ANY_KEYWORDS("any_keywords"), //匹配任意关键字规则 常见 支持||&&语法
9 | ALL_KEYWORDS("all_keywords"), //匹配所有关键字规则 少见 支持||&&语法
10 | ANY_REGULAR("any_regular"), //要求匹配任意正则 常见
11 | ALL_REGULAR("all_regular"); //要求匹配所有正则 少见
12 |
13 | private final String value;
14 |
15 | // 构造方法
16 | MatchType(String value) {
17 | this.value = value;
18 | }
19 |
20 | // 获取枚举对应的字符串值
21 | public String getValue() {
22 | return value;
23 | }
24 |
25 | // 根据字符串值获取对应的枚举
26 | public static MatchType fromValue(String value) {
27 | for (MatchType type : MatchType.values()) {
28 | if (type.getValue().equalsIgnoreCase(value)) {
29 | return type;
30 | }
31 | }
32 | throw new IllegalArgumentException("Invalid match type: " + value);
33 | }
34 |
35 | /**
36 | * 将 matchType 枚举的所有值转换为 List。
37 | *
38 | * @return 包含所有 matchType 值的列表
39 | */
40 | public static String[] getValues() {
41 | // 创建一个可变的列表来存储字符串值
42 | List matchTypes = new ArrayList<>();
43 | // 遍历枚举值并提取其对应的字符串值
44 | for (MatchType type : MatchType.values()) {
45 | matchTypes.add(type.getValue());
46 | }
47 | // 将 List 转换为 String[]
48 | return matchTypes.toArray(new String[0]);
49 | }
50 | }
--------------------------------------------------------------------------------
/src/main/java/EnumType/RiskLevel.java:
--------------------------------------------------------------------------------
1 | package EnumType;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public enum RiskLevel {
7 | CONFIG("config"), //要求全匹配任意关键字规则 少见
8 | HIGH("high"),
9 | MEDIUM("medium"),
10 | LOWER("lower");
11 |
12 | private final String value;
13 |
14 | // 构造方法
15 | RiskLevel(String value) {
16 | this.value = value;
17 | }
18 |
19 | // 获取枚举对应的字符串值
20 | public String getValue() {
21 | return value;
22 | }
23 |
24 | // 根据字符串值获取对应的枚举
25 | public static RiskLevel fromValue(String value) {
26 | for (RiskLevel type : RiskLevel.values()) {
27 | if (type.getValue().equalsIgnoreCase(value)) {
28 | return type;
29 | }
30 | }
31 | throw new IllegalArgumentException("Invalid match type: " + value);
32 | }
33 |
34 | /**
35 | * 将 riskLevel 枚举的所有值转换为 List。
36 | *
37 | * @return 包含所有 riskLevel 值的列表
38 | */
39 | public static String[] getValues() {
40 | // 创建一个可变的列表来存储字符串值
41 | List riskLevels = new ArrayList<>();
42 | // 遍历枚举值并提取其对应的字符串值
43 | for (RiskLevel type : RiskLevel.values()) {
44 | riskLevels.add(type.getValue());
45 | }
46 | // 将 List 转换为 String[]
47 | return riskLevels.toArray(new String[0]);
48 | }
49 | }
--------------------------------------------------------------------------------
/src/main/java/database/AnalyseHostUnVisitedUrls.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import com.alibaba.fastjson2.JSONArray;
4 | import model.UnVisitedUrlsModel;
5 | import utils.CastUtils;
6 |
7 | import java.sql.Connection;
8 | import java.sql.PreparedStatement;
9 | import java.sql.ResultSet;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import static utils.BurpPrintUtils.LOG_ERROR;
14 | import static utils.BurpPrintUtils.stderr_println;
15 |
16 | public class AnalyseHostUnVisitedUrls {
17 |
18 | /**
19 | * 实现 基于 rootUrls 列表 删除 unvisitedUrls
20 | */
21 | public static synchronized int clearUnVisitedUrlsByRootUrls(List rootUrls) {
22 | int totalRowsAffected = 0;
23 | if (rootUrls.isEmpty()) return totalRowsAffected;
24 |
25 | // 构建SQL语句
26 | String updateSQL = ("UPDATE "+ AnalyseHostResultTable.tableName + " SET unvisited_url = ?, unvisited_url_num = 0" +
27 | " WHERE root_url IN $buildInParamList$;")
28 | .replace("$buildInParamList$", DBService.buildInParamList(rootUrls.size()));
29 |
30 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(updateSQL)) {
31 | // 设置第一个参数为JSON数组的toJSONString()
32 | stmt.setString(1, new JSONArray().toJSONString());
33 |
34 | // 循环设置参数 // 开始于第二个参数位置,第一个参数已被设置
35 | for (int i = 0; i < rootUrls.size(); i++) {
36 | stmt.setString(i + 2, rootUrls.get(i));
37 | }
38 | // 执行更新操作并获取受影响行数
39 | totalRowsAffected = stmt.executeUpdate();
40 |
41 | } catch (Exception e) {
42 | stderr_println(LOG_ERROR, String.format("[-] Error clearing unvisited URLs by RootUrls: %s", e.getMessage()));
43 | }
44 | return totalRowsAffected;
45 | }
46 |
47 | /**
48 | * 获取 所有未访问URl (unvisited_url_num > 0)
49 | * @return
50 | */
51 | public static synchronized List fetchAllUnVisitedUrlsWithLimit(Integer limit){
52 | List list = new ArrayList<>();
53 |
54 | String selectSQL = "SELECT id,root_url,unvisited_url FROM "+ AnalyseHostResultTable.tableName + " WHERE unvisited_url_num > 0 ORDER BY id ASC Limit ?;";
55 |
56 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
57 | stmt.setInt(1, limit);
58 | ResultSet rs = stmt.executeQuery();
59 | while (rs.next()) {
60 | UnVisitedUrlsModel unVisitedUrlsModel = new UnVisitedUrlsModel(
61 | rs.getInt("id"),
62 | rs.getString("root_url"),
63 | rs.getString("unvisited_url")
64 | );
65 | list.add(unVisitedUrlsModel);
66 | }
67 | } catch (Exception e) {
68 | stderr_println(LOG_ERROR, String.format("[-] Error fetch [%s] All UnVisited Urls: %s", AnalyseHostResultTable.tableName, e.getMessage()));
69 | }
70 | return list;
71 | }
72 |
73 | /**
74 | * 基于rootUrls查询对应的未访问URl
75 | */
76 | public static List fetchUnVisitedUrlsByRootUrls(List rootUrls) {
77 | List arrayList = new ArrayList<>();
78 |
79 | if (rootUrls.isEmpty()) return arrayList;
80 |
81 | String selectSQL = ("SELECT id,root_url,unvisited_url FROM " + AnalyseHostResultTable.tableName +
82 | " WHERE root_url IN $buildInParamList$;")
83 | .replace("$buildInParamList$", DBService.buildInParamList(rootUrls.size()));
84 |
85 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
86 | for (int i = 0; i < rootUrls.size(); i++) {
87 | stmt.setString(i + 1, rootUrls.get(i));
88 | }
89 |
90 | ResultSet rs = stmt.executeQuery();
91 | while (rs.next()) {
92 | UnVisitedUrlsModel tabDataModel = new UnVisitedUrlsModel(
93 | rs.getInt("id"),
94 | rs.getString("root_url"),
95 | rs.getString("unvisited_url")
96 | );
97 | arrayList.add(tabDataModel);
98 | }
99 | } catch (Exception e) {
100 | stderr_println(LOG_ERROR, String.format("[-] Error fetch [%s] Result Data By MsgHash List: %s", AnalyseHostResultTable.tableName, e.getMessage()));
101 | }
102 | return arrayList;
103 | }
104 |
105 | /**
106 | * 实现 基于 ID 更新 unvisitedUrls
107 | */
108 | public static synchronized int updateUnVisitedUrlsByModel(UnVisitedUrlsModel unVisitedUrlsModel) {
109 | int affectedRows = -1; // 默认ID值,如果没有生成ID,则保持此值
110 |
111 | String updateSQL = "UPDATE " + AnalyseHostResultTable.tableName +" SET unvisited_url = ?, unvisited_url_num = ? WHERE id = ?;";
112 |
113 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(updateSQL)) {
114 | stmt.setString(1, CastUtils.toJsonString(unVisitedUrlsModel.getUnvisitedUrls()));
115 | stmt.setInt(2, unVisitedUrlsModel.getUnvisitedUrls().size());
116 | stmt.setInt(3, unVisitedUrlsModel.getId());
117 | affectedRows = stmt.executeUpdate();
118 | } catch (Exception e) {
119 | stderr_println(LOG_ERROR, String.format("[-] Error update unvisited Urls By Id: %s", e.getMessage()));
120 | }
121 | return affectedRows;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/database/CommonDeleteLine.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import java.sql.Connection;
4 | import java.sql.PreparedStatement;
5 | import java.util.List;
6 |
7 | import static utils.BurpPrintUtils.stderr_println;
8 | import static utils.CastUtils.isEmptyObj;
9 |
10 | public class CommonDeleteLine {
11 | /**
12 | * 执行删除数据行的SQL语句
13 | */
14 | private static int runDeleteByStringsSQL(String tableName, List stringList, String deleteSQL) {
15 | int totalRowsAffected = 0;
16 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(deleteSQL)) {
17 | // 设置SQL语句中的参数值 i+1表示从第一个?号开始设置
18 | for (int i = 0; i < stringList.size(); i++) {
19 | stmt.setString(i + 1, stringList.get(i));
20 | }
21 | // 执行删除操作
22 | totalRowsAffected = stmt.executeUpdate();
23 | } catch (Exception e) {
24 | stderr_println(String.format("[-] runDeleteSql: [%s] -> Error: %s", tableName, e.getMessage()));
25 | e.printStackTrace();
26 | }
27 | return totalRowsAffected;
28 | }
29 |
30 | //基于 rootUrls 列表 同时删除多行
31 | public static synchronized int deleteLineByRootUrls(String tableName, List rootUrls) {
32 | if (isEmptyObj(rootUrls)) return 0;
33 |
34 | // 构建SQL语句,使用占位符 ? 来代表每个ID
35 | String deleteSQL = ("DELETE FROM "+ tableName +" WHERE root_url IN $buildInParamList$;")
36 | .replace("$buildInParamList$", DBService.buildInParamList(rootUrls.size()));
37 |
38 | return runDeleteByStringsSQL(tableName, rootUrls, deleteSQL);
39 | }
40 |
41 | //基于 msgHash 列表 同时删除多个 行
42 | public static synchronized int deleteLineByMsgHashList(String tableName, List msgHashList) {
43 | if (isEmptyObj(msgHashList)) return 0;
44 |
45 | // 构建SQL语句,使用占位符 ? 来代表每个ID
46 | String deleteSQL = ("DELETE FROM "+ tableName + " WHERE msg_hash IN $buildInParamList$;")
47 | .replace("$buildInParamList$", DBService.buildInParamList(msgHashList.size()));
48 |
49 | return runDeleteByStringsSQL(tableName, msgHashList, deleteSQL);
50 | }
51 |
52 | /**
53 | * 基于 id 列表 同时删除多个 行
54 | */
55 | public static synchronized int deleteLineByIds(String tableName, List ids) {
56 | int totalRowsAffected = 0;
57 |
58 | if (ids.isEmpty()) return totalRowsAffected;
59 |
60 | // 构建SQL语句,使用占位符 ? 来代表每个ID
61 | String deleteSQL = ("DELETE FROM "+ tableName + " WHERE id IN $buildInParamList$;")
62 | .replace("$buildInParamList$", DBService.buildInParamList(ids.size()));
63 |
64 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(deleteSQL)) {
65 | // 设置SQL语句中的参数值 i+1表示从第一个?号开始设置
66 | for (int i = 0; i < ids.size(); i++) {
67 | stmt.setInt(i + 1, ids.get(i));
68 | }
69 | // 执行删除操作
70 | totalRowsAffected = stmt.executeUpdate();
71 |
72 | } catch (Exception e) {
73 | stderr_println(String.format("[-] Error deleting Data By Ids On Table [%s] -> Error:[%s]", tableName, e.getMessage()));
74 | e.printStackTrace();
75 | }
76 |
77 | return totalRowsAffected;
78 | }
79 |
80 | /**
81 | * 基于 多个url前缀 列表 删除行
82 | */
83 | public static synchronized int deleteLineByUrlLikeRootUrls(String tableName, List rootUrlList) {
84 | if (isEmptyObj(rootUrlList)) return 0;
85 |
86 | int totalRowsAffected = 0;
87 | String deleteSQL = "DELETE FROM "+ tableName + " WHERE req_url LIKE ?;";
88 |
89 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(deleteSQL)) {
90 | // 开启批处理
91 | conn.setAutoCommit(false);
92 | // 遍历rootUrlList,为每个rootUrl准备并添加到批处理队列
93 | for (String rootUrl : rootUrlList) {
94 | stmt.setString(1, rootUrl + "%");
95 | stmt.addBatch();
96 | }
97 | // 执行批处理
98 | int[] rowsAffected = stmt.executeBatch();
99 | conn.commit();
100 | // 计算受影响的总行数
101 | for (int row : rowsAffected) {
102 | totalRowsAffected += row;
103 | }
104 | } catch (Exception e) {
105 | stderr_println(String.format("[-] Error deleting [%s] Data By Starts With rootUrl List: %s", tableName, e.getMessage()));
106 | }
107 | return totalRowsAffected;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/database/CommonFetchData.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import java.sql.Connection;
4 | import java.sql.PreparedStatement;
5 | import java.sql.ResultSet;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import static utils.BurpPrintUtils.*;
10 |
11 | public class CommonFetchData {
12 | /**
13 | * 统计数据表行数大小
14 | */
15 | public static synchronized int fetchTableCounts(String tableName) {
16 | int count = 0;
17 |
18 | String selectSQL = "SELECT COUNT(*) FROM "+ tableName +" ;";
19 |
20 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL);
21 | ResultSet rs = stmt.executeQuery()) {
22 |
23 | if (rs.next()) {
24 | count = rs.getInt(1); // 获取第一列的值,即 COUNT(*) 的结果
25 | }
26 | } catch (Exception e) {
27 | stderr_println(String.format("Error Counts [%s]: %s",tableName, e.getMessage() ));
28 | }
29 | return count;
30 | }
31 |
32 | /**
33 | * 统计所有已经识别完成的URL的数量
34 | * @return
35 | */
36 | public static synchronized int fetchTableCountsByStatus(String analyseStatus) {
37 | int count = 0;
38 |
39 | String selectSQL = "SELECT COUNT(*) FROM "+ ReqDataTable.tableName + " WHERE run_status = ?;";
40 |
41 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)){
42 | stmt.setString(1, analyseStatus);
43 | ResultSet rs = stmt.executeQuery();
44 | if (rs.next()) {
45 | count = rs.getInt(1); // 获取第一列的值,即 COUNT(*) 的结果
46 | }
47 | } catch (Exception e) {
48 | stderr_println(String.format("Counts Table [%s] Error: %s", ReqDataTable.tableName, e.getMessage() ));
49 | }
50 | return count;
51 | }
52 |
53 | /**
54 | * 根据运行状态取获取对应 ID list
55 | */
56 | public static synchronized List fetchIdsByRunStatus(String tableName, String analyseStatus, int limit) {
57 | List ids = new ArrayList<>();
58 | String selectSQL = "SELECT id FROM " + tableName + " WHERE run_status = ? ORDER BY id ASC LIMIT ?;";
59 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
60 | stmt.setString(1, analyseStatus);
61 | stmt.setInt(2, limit); // Set the limit parameter
62 | ResultSet rs = stmt.executeQuery();
63 | while (rs.next()) {
64 | int id = rs.getInt("id");
65 | ids.add(id);
66 | }
67 | } catch (Exception e) {
68 | stderr_println(LOG_DEBUG, String.format("[-] Error fetching [%s] ids: %s", tableName, e.getMessage()));
69 | }
70 | return ids;
71 | }
72 |
73 | /**
74 | * 根据运行状态取获取对应请求 msghash list
75 | * @return
76 | */
77 | public static synchronized List fetchMsgHashByRunStatus(String tableName, String analyseStatus, int limit) {
78 | List msgHashList = new ArrayList<>();
79 | String selectSQL = "SELECT msg_hash FROM " + tableName + " WHERE run_status = ? ORDER BY id ASC LIMIT ?;";
80 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
81 | stmt.setString(1, analyseStatus);
82 | stmt.setInt(2, limit); // Set the limit parameter
83 | ResultSet rs = stmt.executeQuery();
84 | while (rs.next()) {
85 | String msgHash = rs.getString("msg_hash");
86 | msgHashList.add(msgHash);
87 | }
88 | } catch (Exception e) {
89 | stderr_println(LOG_DEBUG, String.format("[-] Error fetching [%s] MsgHash List from Analysis: %s",tableName, e.getMessage()));
90 | }
91 | return msgHashList;
92 | }
93 |
94 | /////////////////////////////
95 | /**
96 | * 获取任意表的任意列的字符串列表 【基于msgHashList】
97 | */
98 | public static synchronized List fetchColumnStrListByMsgHashList(String tableName, String columnName, List msgHashList) {
99 | List stringList = new ArrayList<>();
100 |
101 | if (msgHashList.isEmpty())
102 | return stringList;
103 |
104 | String selectSQL = ("SELECT " + columnName + " FROM "+ tableName +" WHERE msg_hash IN $buildInParameterList$;")
105 | .replace("$buildInParameterList$", DBService.buildInParamList(msgHashList.size()));
106 |
107 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
108 | for (int i = 0; i < msgHashList.size(); i++) {
109 | stmt.setString(i + 1, msgHashList.get(i));
110 | }
111 | try (ResultSet rs = stmt.executeQuery()) {
112 | while (rs.next()) {
113 | stringList.add(rs.getString(columnName));
114 | }
115 | }
116 | } catch (Exception e) {
117 | stderr_println(LOG_ERROR, String.format("[-] fetchColumnStrListByMsgHashList: [%s] [%s] -> Error: %s",tableName, columnName, e.getMessage()));
118 | }
119 | return stringList;
120 | }
121 | /////////////////////////////
122 | /**
123 | * 获取任意表的任意列的字符串拼接 【获取所有行】
124 | */
125 | public static synchronized String fetchColumnGroupConcatString(String tableName, String columnName) {
126 | String concatenatedURLs = null;
127 |
128 | String concatSQL = ("SELECT GROUP_CONCAT($columnName$,',') AS concatenated_urls FROM "+ tableName +";")
129 | .replace("$columnName$",columnName);
130 |
131 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(concatSQL)) {
132 | ResultSet rs = stmt.executeQuery();
133 | if (rs.next()) {
134 | concatenatedURLs = rs.getString("concatenated_urls");
135 | }
136 | } catch (Exception e) {
137 | stderr_println(LOG_ERROR, String.format("[-] Error fetching [%s] concatenating [%s]: %s",tableName, columnName, e.getMessage()));
138 | e.printStackTrace();
139 | }
140 | return concatenatedURLs;
141 | }
142 |
143 | /**
144 | * 获取任意表的任意列的字符串拼接 InRootUrls
145 | */
146 | public static synchronized String fetchColumnGroupConcatStringInRootUrls(String tableName, String columnName, List rootUrls) {
147 | String concatenatedURLs = null;
148 |
149 | String concatSQL = ("SELECT GROUP_CONCAT($columnName$,',') AS concatenated_urls FROM "+ tableName +
150 | " WHERE root_url IN $buildInParameterList$;")
151 | .replace("$columnName$",columnName)
152 | .replace("$buildInParameterList$", DBService.buildInParamList(rootUrls.size()));
153 |
154 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(concatSQL)) {
155 | for (int i = 0; i < rootUrls.size(); i++) {
156 | stmt.setString(i + 1, rootUrls.get(i));
157 | }
158 |
159 | ResultSet rs = stmt.executeQuery();
160 | if (rs.next()) {
161 | concatenatedURLs = rs.getString("concatenated_urls");
162 | }
163 | } catch (Exception e) {
164 | stderr_println(LOG_ERROR, String.format("[-] Error fetch [%s] [%s] Column Group Concat String In RootUrls concatenating: %s",tableName, columnName, e.getMessage()));
165 | e.printStackTrace();
166 | }
167 | return concatenatedURLs;
168 | }
169 |
170 | /**
171 | * 获取任意表的任意列的字符串拼接 NotInRootUrls
172 | */
173 | public static synchronized String fetchColumnGroupConcatStringNotInRootUrls(String tableName, String columnName, List rootUrls) {
174 | String concatenatedURLs = null;
175 |
176 | String concatSQL = ("SELECT GROUP_CONCAT($columnName$,',') AS concatenated_urls FROM "+ tableName +
177 | " WHERE root_url NOT IN $buildInParameterList$;")
178 | .replace("$columnName$",columnName)
179 | .replace("$buildInParameterList$", DBService.buildInParamList(rootUrls.size()));
180 |
181 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(concatSQL)) {
182 | for (int i = 0; i < rootUrls.size(); i++) {
183 | stmt.setString(i + 1, rootUrls.get(i));
184 | }
185 |
186 | ResultSet rs = stmt.executeQuery();
187 | if (rs.next()) {
188 | concatenatedURLs = rs.getString("concatenated_urls");
189 | }
190 | } catch (Exception e) {
191 | stderr_println(LOG_ERROR, String.format("[-] Error fetch [%s] [%s] Column Group Concat String Not In RootUrls concatenating: %s",tableName, columnName, e.getMessage()));
192 | e.printStackTrace();
193 | }
194 | return concatenatedURLs;
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/main/java/database/CommonUpdateStatus.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import java.sql.Connection;
4 | import java.sql.PreparedStatement;
5 | import java.util.List;
6 |
7 | import static utils.BurpPrintUtils.*;
8 |
9 | public class CommonUpdateStatus {
10 | /**
11 | * 更新多个 ID列表 的状态
12 | */
13 | public static synchronized int updateStatusByIds(String tableName, List ids, String updateStatus) {
14 | int updatedCount = -1;
15 |
16 | String updateSQL = ("UPDATE " + tableName + " SET run_status = ? WHERE id IN $buildInParamList$;")
17 | .replace("$buildInParamList$", DBService.buildInParamList(ids.size()));
18 |
19 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmtUpdate = conn.prepareStatement(updateSQL)) {
20 | stmtUpdate.setString(1, updateStatus);
21 |
22 | for (int i = 0; i < ids.size(); i++) {
23 | stmtUpdate.setInt(i + 2, ids.get(i));
24 | }
25 |
26 | updatedCount = stmtUpdate.executeUpdate();
27 |
28 | if (updatedCount != ids.size()) {
29 | stderr_println(LOG_DEBUG, "[!] Number of updated rows does not match number of selected rows.");
30 | }
31 | } catch (Exception e) {
32 | stderr_println(LOG_ERROR, String.format("[-] Error updating [%s] Data Status: %s", tableName, e.getMessage()));
33 | }
34 | return updatedCount;
35 | }
36 |
37 | /**
38 | * 更新多个 msgHash 的状态
39 | */
40 | public static synchronized int updateStatusByMsgHashList(String tableName, List msgHashList, String updateStatus) {
41 | int updatedCount = -1;
42 |
43 | String updateSQL = ("UPDATE " + tableName + " SET run_status = ? WHERE msg_hash IN $buildInParamList$;")
44 | .replace("$buildInParamList$", DBService.buildInParamList(msgHashList.size()));
45 |
46 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmtUpdate = conn.prepareStatement(updateSQL)) {
47 | stmtUpdate.setString(1, updateStatus);
48 |
49 | for (int i = 0; i < msgHashList.size(); i++) {
50 | stmtUpdate.setString(i + 2, msgHashList.get(i));
51 | }
52 |
53 | updatedCount = stmtUpdate.executeUpdate();
54 |
55 | if (updatedCount != msgHashList.size()) {
56 | stderr_println(LOG_DEBUG, "[!] Number of updated rows does not match number of selected rows.");
57 | }
58 | } catch (Exception e) {
59 | stderr_println(LOG_ERROR, String.format("[-] Error updating [%s] Data Status: %s",tableName, e.getMessage()));
60 | }
61 | return updatedCount;
62 | }
63 |
64 | /**
65 | * 基于 msgDataIndexList 更新 状态
66 | */
67 | public static synchronized int updateStatusByMsgDataIndexList(String tableName, List msgDataIndexList, String updateStatus) {
68 | int updatedCount = -1;
69 |
70 | String updateSQL = ("UPDATE " + tableName + " SET run_status = ? WHERE msg_data_index IN $buildInParamList$;")
71 | .replace("$buildInParamList$", DBService.buildInParamList(msgDataIndexList.size()));
72 |
73 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmtUpdate = conn.prepareStatement(updateSQL)) {
74 | stmtUpdate.setString(1, updateStatus);
75 |
76 | for (int i = 0; i < msgDataIndexList.size(); i++) {
77 | stmtUpdate.setInt(i + 2, msgDataIndexList.get(i));
78 | }
79 |
80 | updatedCount = stmtUpdate.executeUpdate();
81 |
82 | if (updatedCount != msgDataIndexList.size()) {
83 | stderr_println(LOG_DEBUG, "[!] Number of updated rows does not match number of selected rows.");
84 | }
85 | } catch (Exception e) {
86 | stderr_println(LOG_ERROR, String.format("[-] Error updating Req Data Status: %s", e.getMessage()));
87 | }
88 | return updatedCount;
89 | }
90 |
91 | /**
92 | * 当达到某个状态条件时 更新 msgHash 对应数据 的状态
93 | */
94 | public static synchronized int updateStatusWhenStatusByMsgHash(String tableName, String msgHash, String updateStatus, String whenStatus) {
95 | int updatedCount = -1;
96 |
97 | String updateSQL = "UPDATE " + tableName + " SET run_status = ? WHERE run_status = ? and msg_hash = ?;";
98 |
99 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmtUpdate = conn.prepareStatement(updateSQL)) {
100 | stmtUpdate.setString(1, updateStatus);
101 | stmtUpdate.setString(2, whenStatus);
102 | stmtUpdate.setString(3, msgHash);
103 |
104 | updatedCount = stmtUpdate.executeUpdate();
105 | } catch (Exception e) {
106 | stderr_println(LOG_ERROR, String.format("[-] updateStatusWhenStatusByMsgHash: [%s] Error->: %s",tableName, e.getMessage()));
107 | }
108 | return updatedCount;
109 | }
110 |
111 | /**
112 | * 当达到某个状态条件时 更新 root_url 对应数据 的状态
113 | */
114 | public static synchronized int updateStatusWhenStatusByRootUrl(String tableName, String rootUrl, String updateStatus, String whenStatus) {
115 | int updatedCount = -1;
116 |
117 | String updateSQL = "UPDATE " + tableName + " SET run_status = ? WHERE run_status = ? and root_url = ?;";
118 |
119 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmtUpdate = conn.prepareStatement(updateSQL)) {
120 | stmtUpdate.setString(1, updateStatus);
121 | stmtUpdate.setString(2, whenStatus);
122 | stmtUpdate.setString(3, rootUrl);
123 |
124 | updatedCount = stmtUpdate.executeUpdate();
125 | } catch (Exception e) {
126 | stderr_println(LOG_ERROR, String.format("[-] updateStatusWhenStatusByRootUrl: [%s] Error->: %s",tableName, e.getMessage()));
127 | }
128 | return updatedCount;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/database/Constants.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | public class Constants {
4 | public static final String ANALYSE_WAIT = "Waiting"; //等待自动处理
5 | public static final String ANALYSE_ING = "Analysing"; //自动处理中
6 | public static final String ANALYSE_END = "Analysed"; //自动处理完毕
7 |
8 | public static final String HANDLE_WAIT = "Pending"; //等待手动处理
9 | public static final String HANDLE_ING = "Handling"; //手动处理中
10 | public static final String HANDLE_END = "Handled"; //手动处理完毕
11 |
12 | public static final String SPLIT_SYMBOL = "<->";
13 | public static final String RULE_CONF_PREFIX = "CONF_"; //配置文件中 配置规则的开头
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/database/DBService.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import burp.BurpExtender;
4 | import org.sqlite.SQLiteConfig;
5 | import utils.BurpFileUtils;
6 |
7 | import java.io.File;
8 | import java.nio.file.Path;
9 | import java.sql.*;
10 |
11 | import static utils.BurpPrintUtils.*;
12 |
13 | public class DBService {
14 | private static DBService instance;
15 | private Connection connection;
16 | private String CONNECTION_STRING; //数据库链接字符串
17 |
18 | //指定sqlite数据库配置文件路径
19 | private String DBFileName = "APIFinderPlus.db";
20 |
21 | private DBService() {
22 | Path DBFilePath = BurpFileUtils.getPluginDirFilePath(BurpExtender.getCallbacks(), DBFileName);
23 | CONNECTION_STRING = String.format("jdbc:sqlite:%s?journal_mode=WAL", DBFilePath);
24 | }
25 |
26 | public static synchronized DBService getInstance() {
27 | // 单例模式配置
28 | if (instance == null) {
29 | instance = new DBService();
30 | }
31 | return instance;
32 | }
33 |
34 | //创建数据库链接
35 | public synchronized void initDBConnection() {
36 | try {
37 | // 自动注册 SQLite 驱动程序
38 | Class.forName("org.sqlite.JDBC");
39 |
40 | // 建立数据库连接
41 | connection = DriverManager.getConnection(CONNECTION_STRING);
42 |
43 | // 启用外键支持
44 | try (Statement stmt = connection.createStatement()) {
45 | stmt.execute("PRAGMA foreign_keys = ON");
46 | } catch (SQLException e) {
47 | stderr_println(String.format("[!] set foreign_keys error. -> %s", e.getMessage()));
48 | e.printStackTrace();
49 | }
50 |
51 | stdout_println(LOG_INFO, "[+] SQLite database connection initialized successfully. ");
52 | } catch (ClassNotFoundException e) {
53 | stderr_println(String.format("[!] JDBC driver not found. -> %s", e.getMessage()));
54 | e.printStackTrace();
55 | } catch (SQLException e) {
56 | stderr_println(String.format("[!] Failed to connect db. -> %s", e.getMessage()));
57 | e.printStackTrace();
58 | }
59 | }
60 |
61 | //创建数据表结构
62 | public synchronized void initCreateTables() {
63 | // RecordUrlTable URL 访问记录表 用于后续排除已访问过的UR了
64 | execCreatTableSql(RecordUrlTable.creatTableSQL, RecordUrlTable.tableName);
65 |
66 | // RecordPathTable URL PATH记录表 用于后续路径猜测记录
67 | execCreatTableSql(RecordPathTable.creatTableSQL, RecordPathTable.tableName);
68 |
69 | // ReqMsgDataTable 用于存储 实际的请求体和响应体
70 | execCreatTableSql(ReqMsgDataTable.creatTableSQL, ReqMsgDataTable.tableName);
71 |
72 | // ReqDataTable 存储需要提取敏感信息的数据
73 | execCreatTableSql(ReqDataTable.creatTableSQL, ReqDataTable.tableName);
74 |
75 | // AnalyseUrlResultTable 存储分析后的数据
76 | execCreatTableSql(AnalyseUrlResultTable.creatTableSQL, AnalyseUrlResultTable.tableName);
77 |
78 | // AnalyseHostResultTable 存储分析后的数据 的 集合
79 | execCreatTableSql(AnalyseHostResultTable.creatTableSQL, AnalyseHostResultTable.tableName);
80 |
81 | // 创建存储根树的表
82 | execCreatTableSql(PathTreeTable.creatTableSQL, PathTreeTable.tableName);
83 | }
84 |
85 | //创建数据表的语句
86 | private void execCreatTableSql(String creatTableSql, String tableName) {
87 | try (Statement stmt = connection.createStatement()) {
88 | stmt.execute(creatTableSql);
89 | stdout_println(LOG_INFO, String.format("[+] create db %s success ...", tableName));
90 | } catch (Exception e) {
91 | stderr_println(String.format("[!] create db %s failed -> %s", tableName, e.getMessage()));
92 | e.printStackTrace();
93 | }
94 | }
95 |
96 | //获取一个数据库语句
97 | public Connection getNewConn() throws SQLException {
98 | //勉强解决 [SQLITE_BUSY] The database file is locked (database is locked) 错误
99 | SQLiteConfig config = new SQLiteConfig();
100 | config.setBusyTimeout(5000); // 设置超时时间,单位是毫秒
101 | return DriverManager.getConnection(CONNECTION_STRING, config.toProperties());
102 | }
103 |
104 | // 关闭数据库连接的方法
105 | public void closeConnection() {
106 | try {
107 | if (this.connection != null && !this.connection.isClosed()) {
108 | this.connection.close();
109 | }
110 | } catch (SQLException e) {
111 | stderr_println(String.format("关闭数据库连接时发生错误: %s", e.getMessage()));
112 | e.printStackTrace();
113 | }
114 | }
115 |
116 | /**
117 | * 清空表数据
118 | * @param tableName
119 | */
120 | private static void clearTable(String tableName) {
121 | // 用 DELETE 语句来清空表
122 | String deleteSql = "DELETE FROM "+ tableName +" ;";
123 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(deleteSql)) {
124 | stmt.executeUpdate();
125 | stdout_println(String.format("[-] table [%s] has been cleared.", tableName));
126 | } catch (Exception e) {
127 | stderr_println(String.format("Error clearing table [%s] -> Error: %s",tableName, e.getMessage()));
128 | }
129 | }
130 |
131 | /**
132 | * 清空常用表的数据
133 | */
134 | public static void clearModelTables(){
135 | clearTable(AnalyseUrlResultTable.tableName);
136 | clearTable(ReqDataTable.tableName);
137 | clearTable(ReqMsgDataTable.tableName);
138 | }
139 |
140 |
141 | /**
142 | * 清空记录表的数据
143 | */
144 | public static void clearRecordTables(){
145 | clearTable(PathTreeTable.tableName);
146 | clearTable(RecordPathTable.tableName);
147 | clearTable(RecordUrlTable.tableName);
148 | clearTable(AnalyseHostResultTable.tableName);
149 | }
150 |
151 |
152 | /**
153 | * 清空所有表的数据
154 | */
155 | public static void clearAllTables(){
156 | clearModelTables();
157 | clearRecordTables();
158 | }
159 |
160 |
161 | /**
162 | * 清空已访问URL表
163 | */
164 | public static void clearRecordUrlTable(){
165 | clearTable(RecordUrlTable.tableName);
166 | }
167 |
168 | /**
169 | * 构建一个函数,实现根据参数列表数量自动拼接 IN (?,?,?)语句
170 | * @param size
171 | * @return
172 | */
173 | public static String buildInParamList(int size) {
174 | StringBuilder inParameterList = new StringBuilder(" (");
175 | for (int i = 0; i < size; i++) {
176 | inParameterList.append("?");
177 | if (i < size - 1) {
178 | inParameterList.append(", ");
179 | }
180 | }
181 | inParameterList.append(") ");
182 | return inParameterList.toString();
183 | }
184 |
185 |
186 | //判断大小是否超过 x G 超过就清空数据库文件
187 | public boolean clearBigDB(int limit) {
188 | try {
189 | Path sqliteDBFilePath = BurpFileUtils.getPluginDirFilePath(BurpExtender.getCallbacks(), DBFileName);
190 | File dbFile = new File(sqliteDBFilePath.toString());
191 | if (dbFile.length() > 1024 * 1024 * 1024 * limit){
192 | DBService.clearModelTables();
193 | stdout_println(String.format("DB File Is Big, Clear Model Tables Success ..."));
194 | return true;
195 | }
196 | } catch (Exception e){
197 | stderr_println(String.format("DB File Is Big, Clear Model Tables Occur Error: [%s]", e.getMessage()));
198 | }
199 | return false;
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/database/PathTreeTable.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import com.alibaba.fastjson2.JSONObject;
4 | import model.PathTreeModel;
5 |
6 | import java.sql.*;
7 | import java.util.ArrayList;
8 | import java.util.HashSet;
9 | import java.util.List;
10 | import java.util.Set;
11 |
12 | import static utils.CastUtils.isNotEmptyObj;
13 | import static utils.PathTreeUtils.deepMergeJsonTree;
14 | import static utils.BurpPrintUtils.*;
15 |
16 | public class PathTreeTable {
17 | //数据表名称
18 | public static String tableName = "PATH_TREE";
19 |
20 | //创建 基于 record_urls 生成的每个域名的 路径结构 树
21 | static String creatTableSQL = "CREATE TABLE IF NOT EXISTS "+ tableName +" (\n"
22 | + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" //自增的id
23 | + " root_url TEXT NOT NULL,\n"
24 | + " path_tree TEXT NOT NULL,\n" //根树的序列化Json数据
25 | + " basic_path_num INTEGER NOT NULL DEFAULT 0\n" //基于多少个路径计算出来的根树,最好使用根树的稳定 hash
26 | + ");";
27 |
28 | //插入数据库
29 | public static synchronized int insertOrUpdatePathTree(PathTreeModel pathTreeModel) {
30 | String rootUrl = pathTreeModel.getRootUrl();
31 | Integer newBasicPathNum = pathTreeModel.getBasicPathNum();
32 | JSONObject newPathTree = pathTreeModel.getPathTree();
33 |
34 | int generatedId = -1; // 默认ID值,如果没有生成ID,则保持此值
35 |
36 | //查询 是否已存在记录
37 | String checkSql = "SELECT id,path_tree,basic_path_num FROM " + tableName + " WHERE root_url = ?;";
38 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement checkStmt = conn.prepareStatement(checkSql)) {
39 | checkStmt.setString(1, rootUrl);
40 |
41 | ResultSet rs = checkStmt.executeQuery();
42 | if (rs.next()) {
43 | int selectedId = rs.getInt("id");
44 | String oldPathTree = rs.getString("path_tree");
45 | int oldBasicPathNum = rs.getInt("basic_path_num");
46 |
47 | //合并新旧pathNum 输入的PATH TREE 是基于新找到的PATH 因此是增量的
48 | newBasicPathNum = Math.max(0, oldBasicPathNum) + Math.max(0, newBasicPathNum);
49 |
50 | //合并新旧Json树
51 | if (isNotEmptyObj(oldPathTree)){
52 | JSONObject oldTree = JSONObject.parse(oldPathTree);
53 | newPathTree = deepMergeJsonTree(oldTree, newPathTree);
54 | }
55 |
56 | //更新索引对应的数据
57 | String updateSQL = "UPDATE "+ tableName +" SET path_tree = ?, basic_path_num = ? WHERE id = ?;";
58 | try (PreparedStatement updateStatement = conn.prepareStatement(updateSQL)) {
59 | updateStatement.setString(1, newPathTree.toJSONString());
60 | updateStatement.setInt(2, newBasicPathNum);
61 | updateStatement.setInt(3, selectedId);
62 | int affectedRows = updateStatement.executeUpdate();
63 | if (affectedRows > 0) {
64 | generatedId = selectedId;
65 | }
66 | }
67 | } else {
68 | // 记录不存在,插入新记录
69 | String insertSql = "INSERT INTO "+ tableName +" (root_url, path_tree, basic_path_num) VALUES (?, ?, ?);";
70 | try (PreparedStatement insertStmt = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
71 | insertStmt.setString(1, rootUrl);
72 | insertStmt.setString(2, newPathTree.toJSONString());
73 | insertStmt.setInt(3, newBasicPathNum);
74 |
75 | int affectedRows = insertStmt.executeUpdate();
76 |
77 | if (affectedRows > 0) {
78 | // 获取生成的键值
79 | try (ResultSet generatedKeys = insertStmt.getGeneratedKeys()) {
80 | if (generatedKeys.next()) {
81 | generatedId = generatedKeys.getInt(1);
82 | } else {
83 | throw new SQLException("Creating user failed, no ID obtained.");
84 | }
85 | }
86 | }
87 | }
88 | }
89 | } catch (Exception e) {
90 | stderr_println(String.format("[-] Error inserting or updating table [%s] -> Error:[%s]", tableName, e.getMessage()));
91 | e.printStackTrace();
92 | }
93 |
94 | return generatedId; // 返回ID值,无论是更新还是插入
95 | }
96 |
97 | //根据域名查询对应的路径树
98 | public static synchronized List fetchPathTreeByRootUrls(List rootUrls) {
99 | List pathTreeModels = new ArrayList<>();
100 |
101 | if (rootUrls.isEmpty()) return pathTreeModels;
102 |
103 | //查询
104 | String selectSql = ("SELECT root_url, path_tree, basic_path_num FROM "+ tableName +
105 | " WHERE root_url IN $buildInParamList$;")
106 | .replace("$buildInParamList$", DBService.buildInParamList(rootUrls.size()));
107 |
108 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSql)) {
109 |
110 | for (int i = 0; i < rootUrls.size(); i++) {
111 | stmt.setString(i + 1, rootUrls.get(i));
112 | }
113 |
114 | ResultSet rs = stmt.executeQuery();
115 | while (rs.next()) {
116 | PathTreeModel pathTreeModel = new PathTreeModel(
117 | rs.getString("root_url"),
118 | rs.getInt("basic_path_num"),
119 | rs.getString("path_tree")
120 | );
121 | pathTreeModels.add(pathTreeModel);
122 | }
123 | } catch (Exception e) {
124 | stderr_println(String.format("[-] Error Fetch [%s] Data By Req Host Port List: %s", tableName, e.getMessage()));
125 | e.printStackTrace();
126 | }
127 |
128 | return pathTreeModels;
129 | }
130 |
131 |
132 | //根据 rootUrl 查询对应的path_tree
133 | public static synchronized PathTreeModel fetchPathTreeByRootUrl(String rootUrl) {
134 | PathTreeModel pathTreeModel= null;
135 |
136 | //查询
137 | String selectSql = "SELECT root_url, path_tree, basic_path_num FROM "+ tableName +" WHERE root_url = ? LIMIT 1;";
138 |
139 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSql)) {
140 | stmt.setString(1, rootUrl);
141 |
142 | ResultSet rs = stmt.executeQuery();
143 | if (rs.next()) {
144 | pathTreeModel = new PathTreeModel(
145 | rs.getString("root_url"),
146 | rs.getInt("basic_path_num"),
147 | rs.getString("path_tree")
148 | );
149 | }
150 | } catch (Exception e) {
151 | stderr_println(String.format("[-] Error Fetch [%s] Data: %s", tableName, e.getMessage()));
152 | e.printStackTrace();
153 | }
154 |
155 | return pathTreeModel;
156 | }
157 |
158 |
159 | /**
160 | * 获取 所有 表中记录的 URL前置
161 | * @return
162 | */
163 | public static synchronized Set fetchAllRecordPathRootUrls(){
164 | Set set = new HashSet<>();
165 | String selectSQL = "SELECT DISTINCT root_url FROM "+ tableName + ";";
166 |
167 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
168 | ResultSet rs = stmt.executeQuery();
169 | while (rs.next()) {
170 | String urlPrefix = rs.getString("root_url");
171 | set.add(urlPrefix);
172 | }
173 | } catch (Exception e) {
174 | stderr_println(String.format("[-] Error fetch [%s] All Root URL: %s", tableName, e.getMessage()));
175 | e.printStackTrace();
176 | }
177 |
178 | return set;
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/src/main/java/database/RecordPathTable.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.HttpMsgInfo;
4 | import model.HttpUrlInfo;
5 | import model.RecordPathDirsModel;
6 | import model.RecordPathModel;
7 |
8 | import java.sql.*;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import static utils.BurpPrintUtils.*;
13 |
14 | public class RecordPathTable {
15 | //数据表名称
16 | public static String tableName = "RECORD_PATH";
17 |
18 | //创建用于存储所有 访问成功的 URL的数据库 record_urls
19 | static String creatTableSQL = "CREATE TABLE IF NOT EXISTS "+ tableName +" (\n"
20 | + "id INTEGER PRIMARY KEY AUTOINCREMENT,\n" //自增的id
21 | + "req_hash TEXT UNIQUE, \n" // 添加一列 req_hash 作为 root_url req_path_dir resp_status_code 的 特征值
22 | + "root_url TEXT NOT NULL,\n"
23 | + "req_path_dir TEXT NOT NULL,\n"
24 | + "resp_status_code TEXT NOT NULL, \n"
25 | + "run_status TEXT NOT NULL DEFAULT 'RUN_STATUS'".replace("RUN_STATUS", Constants.ANALYSE_WAIT)
26 | + ");";
27 |
28 |
29 | /**
30 | * 插入一条路径记录
31 | */
32 | public static synchronized int insertOrUpdateRecordPath(RecordPathModel recordPathModel) {
33 | int generatedId = -1; // 默认ID值,如果没有生成ID,则保持此值
34 | String selectSql = "SELECT id FROM "+ tableName +" WHERE req_hash = ?;";
35 |
36 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSql))
37 | {
38 | // 检查记录是否存在
39 | stmt.setString(1, recordPathModel.getReqHash());
40 | ResultSet rs = stmt.executeQuery();
41 | if (rs.next()) {
42 | // 记录存在,忽略操作
43 | return 0;
44 | } else {
45 | // 记录不存在,插入新记录
46 | String insertSql = "INSERT INTO "+ tableName +
47 | " (root_url, req_path_dir, resp_status_code, req_hash)" +
48 | " VALUES (?, ?, ?, ?);";
49 | try (PreparedStatement insertStmt = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
50 | insertStmt.setString(1, recordPathModel.getRootUrl());
51 | insertStmt.setString(2, recordPathModel.getReqPathDir());
52 | insertStmt.setInt(3, recordPathModel.getRespStatusCode());
53 | insertStmt.setString(4, recordPathModel.getReqHash());
54 | insertStmt.executeUpdate();
55 |
56 | // 获取生成的键值
57 | try (ResultSet generatedKeys = insertStmt.getGeneratedKeys()) {
58 | if (generatedKeys.next()) {
59 | generatedId = generatedKeys.getInt(1); // 获取生成的ID
60 | }
61 | }
62 | }
63 | }
64 | } catch (Exception e) {
65 | stderr_println(String.format("[-] Error inserting or updating table -> Error:[%s]", tableName, e.getMessage()));
66 | e.printStackTrace();
67 | }
68 |
69 | return generatedId; // 返回ID值,无论是更新还是插入
70 | }
71 |
72 |
73 | /**
74 | * 插入一条路径记录 复用insertOrUpdateRecordPath
75 | */
76 | public static synchronized int insertOrUpdateRecordPath(HttpMsgInfo msgInfo) {
77 | RecordPathModel recordPathModel = new RecordPathModel(msgInfo.getUrlInfo(), msgInfo.getRespStatusCode());
78 | return insertOrUpdateRecordPath(recordPathModel);
79 | }
80 |
81 | /**
82 | * 插入一条路径记录 复用insertOrUpdateRecordPath
83 | */
84 | public static synchronized int insertOrUpdateRecordPath(String reqUrl, int respStatusCode) {
85 | RecordPathModel recordPathModel = new RecordPathModel(new HttpUrlInfo(reqUrl), respStatusCode );
86 | return insertOrUpdateRecordPath(recordPathModel);
87 | }
88 |
89 | /**
90 | * 批量插入 recordPathModels
91 | */
92 | public static int[] insertOrUpdateRecordPathsBatch(List recordPathModels) {
93 | int[] generatedIds = null;
94 |
95 | String insertSql = "INSERT INTO "+ tableName +
96 | " (root_url, req_path_dir, resp_status_code, req_hash)" +
97 | " VALUES (?, ?, ?, ?)" +
98 | " ON CONFLICT(req_hash) DO NOTHING";
99 |
100 | // 这个语句的作用是在尝试向表中插入一条记录时,如果发现有与之冲突的唯一约束
101 | // (即在 root_url, req_path_dir, resp_status_code 这些字段上已经存在相同的值组合),
102 | // 那么数据库将不会执行任何操作,也不会抛出错误,而是简单地跳过这条记录的插入。
103 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement insertStmt = conn.prepareStatement(insertSql)) {
104 | conn.setAutoCommit(false); // 开启事务处理
105 | for (RecordPathModel record : recordPathModels) {
106 | insertStmt.setString(1, record.getRootUrl());
107 | insertStmt.setString(2, record.getReqPathDir());
108 | insertStmt.setInt(3, record.getRespStatusCode());
109 | insertStmt.setString(4, record.getReqHash());
110 | insertStmt.addBatch(); // 添加到批处理
111 | }
112 | generatedIds = insertStmt.executeBatch();
113 | conn.commit(); // 提交事务
114 | } catch (Exception e) {
115 | stderr_println(String.format("[-] Error [%s] executing batch insert/update: %s",tableName, e.getMessage()));
116 | e.printStackTrace();
117 | }
118 | return generatedIds;
119 | }
120 |
121 | /**
122 | * 实现URL批量插入 复用batchInsertOrUpdateRecordPath
123 | */
124 | public static int[] insertOrUpdateRecordPathsBatch(List findUrls, int respStatusCode) {
125 | List recordPathModels = new ArrayList<>();
126 | for (String findUrl: findUrls){
127 | HttpUrlInfo urlInfo = new HttpUrlInfo(findUrl);
128 | RecordPathModel recordPathModel = new RecordPathModel(
129 | urlInfo.getRootUrlUsual(),
130 | urlInfo.getPathToDir(),
131 | respStatusCode
132 | );
133 | recordPathModels.add(recordPathModel);
134 | }
135 | return insertOrUpdateRecordPathsBatch(recordPathModels);
136 | }
137 |
138 | /**
139 | * 获取 指定状态的数据 并封装为 路径模型
140 | */
141 | public static List fetchPathRecordsByStatus(String analyseStatus) {
142 | // 创建一个列表或集合来存储查询结果
143 | List recordPathModels = new ArrayList<>();
144 |
145 | String selectSQL = "SELECT root_url,GROUP_CONCAT(req_path_dir, ?) AS req_path_dirs " +
146 | "FROM "+ tableName +" WHERE run_status = ? GROUP BY root_url;";
147 |
148 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)){
149 | //2、获取 解析中 状态的 Host、数据、ID列表
150 | stmt.setString(1, Constants.SPLIT_SYMBOL);
151 | stmt.setString(2, analyseStatus);
152 |
153 | //获取查询数据
154 | ResultSet rs = stmt.executeQuery();
155 | while (rs.next()) {
156 | RecordPathDirsModel recordPathDirsModel = new RecordPathDirsModel(
157 | rs.getString("root_url"),
158 | rs.getString("req_path_dirs")
159 | );
160 | recordPathModels.add(recordPathDirsModel);
161 | }
162 |
163 | } catch (Exception e) {
164 | stderr_println(String.format("[-] Error fetch [%s] Data To Analysis: %s", tableName, e.getMessage()));
165 | e.printStackTrace();
166 | }
167 | return recordPathModels;
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/src/main/java/database/RecordUrlTable.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.AccessedUrlInfo;
4 | import model.HttpMsgInfo;
5 | import model.HttpUrlInfo;
6 | import utils.RespHashUtils;
7 |
8 | import java.sql.*;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class RecordUrlTable {
13 | //数据表名称
14 | public static String tableName = "RECORD_URL";
15 | public static String urlHashName = "url_hash";
16 |
17 | //创建用于存储所有 访问成功的 URL的数据库 record_urls
18 | static String creatTableSQL = "CREATE TABLE IF NOT EXISTS "+ tableName +" (\n"
19 | + "id INTEGER PRIMARY KEY AUTOINCREMENT,\n" //自增的id
20 | + "url_hash TEXT UNIQUE,\n"
21 | + "root_url TEXT NOT NULL,\n"
22 | + "req_url TEXT NOT NULL,\n" //记录访问过的URL
23 | + "resp_status_code INTEGER\n" //记录访问过的URL状态
24 | + ");";
25 |
26 |
27 | //插入访问的URl
28 | public static synchronized int insertOrUpdateAccessedUrl(String reqUrl,String rootUrl, int respStatusCode, String urlHash) {
29 | int generatedId = -1;
30 | String upsertSql = "INSERT INTO "+ tableName +
31 | " (req_url, root_url, resp_status_code, url_hash)" +
32 | " VALUES (?,?, ?, ?)" +
33 | " ON CONFLICT(url_hash) DO UPDATE SET resp_status_code = EXCLUDED.resp_status_code;";
34 |
35 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(upsertSql, Statement.RETURN_GENERATED_KEYS)) {
36 | stmt.setString(1, reqUrl);
37 | stmt.setString(2, rootUrl);
38 | stmt.setInt(3, respStatusCode);
39 | stmt.setString(4, urlHash);
40 |
41 | stmt.executeUpdate();
42 |
43 | try (ResultSet generatedKeys = stmt.getGeneratedKeys()) {
44 | if (generatedKeys.next()) {
45 | generatedId = generatedKeys.getInt(1);
46 | }
47 | }
48 | } catch (SQLException e) {
49 | System.err.println(String.format("Error insert Or Update Accessed Url On table [%s] -> Error:[%s]", tableName, e.getMessage()));
50 | }
51 |
52 | return generatedId;
53 | }
54 |
55 | //插入访问的URl 复用
56 | public static synchronized int insertOrUpdateAccessedUrl(HttpMsgInfo msgInfo) {
57 | return insertOrUpdateAccessedUrl(
58 | msgInfo.getUrlInfo().getRawUrlUsual(),
59 | msgInfo.getUrlInfo().getRootUrlUsual(),
60 | msgInfo.getRespStatusCode(),
61 | RespHashUtils.calcCRC32(msgInfo.getUrlInfo().getRawUrlUsual())
62 | );
63 | }
64 |
65 | //插入访问的URl 复用
66 | public static synchronized int insertOrUpdateAccessedUrl(String reqUrl, int respStatusCode) {
67 | return insertOrUpdateAccessedUrl(
68 | reqUrl,
69 | new HttpUrlInfo(reqUrl).getRootUrlUsual(),
70 | respStatusCode,
71 | RespHashUtils.calcCRC32(reqUrl)
72 | );
73 | }
74 |
75 | //实现批量插入访问信息
76 | public static synchronized int[] insertOrUpdateAccessedUrlsBatch(List accessedUrlInfos) {
77 | int[] generatedIds = null;
78 |
79 | String upsertSql = "INSERT INTO "+ tableName +
80 | " (req_url, root_url, resp_status_code, url_hash)" +
81 | " VALUES (?, ?, ?, ?)" +
82 | " ON CONFLICT(url_hash) DO UPDATE SET resp_status_code = EXCLUDED.resp_status_code;";
83 |
84 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(upsertSql, Statement.RETURN_GENERATED_KEYS)) {
85 | // 添加到批处理队列
86 | conn.setAutoCommit(false); // 开启事务
87 | for (AccessedUrlInfo accessedUrlInfo : accessedUrlInfos) {
88 | stmt.setString(1, accessedUrlInfo.getReqUrl());
89 | stmt.setString(2, accessedUrlInfo.getRootUrl());
90 | stmt.setInt(3, accessedUrlInfo.getRespStatusCode());
91 | stmt.setString(4, accessedUrlInfo.getUrlHash());
92 | stmt.addBatch();
93 | }
94 | // 执行批处理
95 | generatedIds = stmt.executeBatch();
96 | conn.commit(); // 提交事务
97 |
98 | } catch (Exception e) {
99 | System.err.println(String.format("Error [%s] batch insert Or Update Accessed Urls: %s", tableName, e.getMessage()));
100 | }
101 |
102 | return generatedIds;
103 | }
104 |
105 |
106 | //实现批量插入访问信息 复用
107 | public static synchronized int[] insertOrUpdateAccessedUrlsBatch(List accessedUrls, int respStatusCode){
108 | List accessedUrlInfos = new ArrayList<>();
109 | for (String reqUrl : accessedUrls){
110 | AccessedUrlInfo accessedUrlInfo = new AccessedUrlInfo(reqUrl, new HttpUrlInfo(reqUrl).getRootUrlUsual(),respStatusCode);
111 | accessedUrlInfos.add(accessedUrlInfo);
112 | }
113 | return insertOrUpdateAccessedUrlsBatch(accessedUrlInfos);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/database/ReqDataTable.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.HttpMsgInfo;
4 | import model.ReqUrlRespStatusModel;
5 |
6 | import java.sql.Connection;
7 | import java.sql.PreparedStatement;
8 | import java.sql.ResultSet;
9 | import java.sql.Statement;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import static utils.BurpPrintUtils.*;
14 |
15 | public class ReqDataTable {
16 | //数据表名称
17 | public static String tableName = "REQ_DATA";
18 |
19 | //创建用于存储 需要处理的URL的原始请求响应
20 | static String creatTableSQL = "CREATE TABLE IF NOT EXISTS "+ tableName +" ("
21 | + "id INTEGER PRIMARY KEY AUTOINCREMENT,"
22 |
23 | + "msg_hash TEXT UNIQUE," //作为实际的消息独立标记
24 |
25 | + "req_url TEXT NOT NULL,"
26 | + "req_method TEXT NOT NULL,"
27 |
28 | + "resp_status_code INTEGER NOT NULL,"
29 | + "resp_length INTEGER NOT NULL," //响应长度
30 |
31 | + "msg_data_index INTEGER NOT NULL,"
32 | + "req_source TEXT NOT NULL," //请求来源
33 | + "run_status TEXT NOT NULL DEFAULT 'RUN_STATUS'".replace("RUN_STATUS", Constants.ANALYSE_WAIT)
34 |
35 | + ");";
36 |
37 |
38 | //插入请求消息到数据库
39 | public static synchronized int insertOrUpdateReqData(HttpMsgInfo msgInfo, int msgDataIndex, String reqSource) {
40 | int generatedId = -1; // 默认ID值,如果没有生成ID,则保持此值
41 |
42 | String checkSql = "SELECT id FROM "+ tableName +" WHERE msg_hash = ? ;";
43 |
44 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement checkStmt = conn.prepareStatement(checkSql)) {
45 | // 检查记录是否存在
46 | checkStmt.setString(1, msgInfo.getMsgHash());
47 | ResultSet rs = checkStmt.executeQuery();
48 | if (rs.next()) {
49 | // 记录存在,忽略操作
50 | //stdout_println(LOG_INFO, String.format("[*] Ignore Update [%s] %s -> %s", tableName, msgInfo.getUrlInfo().getReqUrl(), msgInfo.getMsgHash()));
51 | return 0;
52 | } else {
53 | // 记录不存在,插入新记录
54 | String insertSql = "INSERT INTO "+ tableName +
55 | " (msg_hash, req_url, req_method, resp_status_code, msg_data_index, req_source, resp_length)" +
56 | " VALUES (?, ?, ?, ?, ?, ?, ?)";
57 |
58 | try (PreparedStatement insertStmt = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
59 | insertStmt.setString(1, msgInfo.getMsgHash());
60 | insertStmt.setString(2, msgInfo.getUrlInfo().getRawUrlUsual());
61 | insertStmt.setString(3, msgInfo.getReqMethod());
62 | insertStmt.setInt(4, msgInfo.getRespStatusCode());
63 | insertStmt.setInt(5, msgDataIndex);
64 | insertStmt.setString(6, reqSource);
65 | insertStmt.setInt(7, msgInfo.getRespBytes().length);
66 | insertStmt.executeUpdate();
67 |
68 | // 获取生成的键值
69 | try (ResultSet generatedKeys = insertStmt.getGeneratedKeys()) {
70 | if (generatedKeys.next()) {
71 | generatedId = generatedKeys.getInt(1); // 获取生成的ID
72 | }
73 | }
74 | }
75 | }
76 | } catch (Exception e) {
77 | stderr_println(String.format("[-] Error inserting or updating table [%s] -> Error:[%s]", tableName, e.getMessage()));
78 | e.printStackTrace();
79 | }
80 |
81 | return generatedId; // 返回ID值,无论是更新还是插入
82 | }
83 |
84 | /**
85 | * 根据运行状态取获取对应请求的实际消息ID
86 | */
87 | public static synchronized List fetchMsgDataIndexListByRunStatus(int limit, String analyseStatus) {
88 | List msgDataIndexList = new ArrayList<>();
89 | String selectSQL = "SELECT msg_data_index FROM " + tableName + " WHERE run_status = ? ORDER BY msg_data_index ASC LIMIT ?;";
90 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
91 | stmt.setString(1, analyseStatus);
92 | stmt.setInt(2, limit); // Set the limit parameter
93 | ResultSet rs = stmt.executeQuery();
94 | while (rs.next()) {
95 | int msgDataIndex = rs.getInt("msg_data_index");
96 | msgDataIndexList.add(msgDataIndex);
97 | }
98 | } catch (Exception e) {
99 | stderr_println(LOG_DEBUG, String.format("[-] Error fetching [%s] Req Data Index: %s",tableName, e.getMessage()));
100 | }
101 | return msgDataIndexList;
102 | }
103 |
104 | /**
105 | * 根据运行状态取获取对应请求的实际消息ID
106 | */
107 | public static synchronized List fetchReqUrlRespStatusByUrls(List urls) {
108 | List requestStatusModels = new ArrayList<>();
109 |
110 | String selectSQL = ("SELECT * FROM " + tableName + " WHERE req_url IN $buildInParamList$;")
111 | .replace("$buildInParamList$", DBService.buildInParamList(urls.size()));
112 |
113 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
114 | for (int i = 0; i < urls.size(); i++) {
115 | stmt.setString(i + 1, urls.get(i));
116 | }
117 |
118 | ResultSet rs = stmt.executeQuery();
119 | while (rs.next()) {
120 | ReqUrlRespStatusModel requestStatusModel = new ReqUrlRespStatusModel(
121 | rs.getInt("id"),
122 | rs.getString("req_url"),
123 | rs.getString("req_method"),
124 | rs.getInt("resp_status_code"),
125 | rs.getInt("resp_length")
126 | );
127 | requestStatusModels.add(requestStatusModel);
128 | }
129 | } catch (Exception e) {
130 | stderr_println(LOG_DEBUG, String.format("[-] Error fetching [%s] Request Status By Urls: %s",tableName, e.getMessage()));
131 | }
132 | return requestStatusModels;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/main/java/database/ReqMsgDataTable.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.HttpMsgInfo;
4 | import model.ReqMsgDataModel;
5 |
6 | import java.sql.Connection;
7 | import java.sql.PreparedStatement;
8 | import java.sql.ResultSet;
9 | import java.sql.Statement;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import static utils.BurpPrintUtils.*;
14 |
15 | public class ReqMsgDataTable {
16 | //数据表名称
17 | public static String tableName = "REQ_MSG_DATA";
18 |
19 | //创建用于存储 需要处理的URL的原始请求响应
20 | static String creatTableSQL = "CREATE TABLE IF NOT EXISTS "+ tableName +" (\n"
21 | + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
22 | + " msg_hash TEXT UNIQUE,\n"
23 | + " req_url TEXT NOT NULL,\n"
24 | + " req_bytes BLOB,\n"
25 | + " resp_bytes BLOB\n"
26 | + ");";
27 |
28 | //插入数据库
29 | public static synchronized int insertOrUpdateMsgData(HttpMsgInfo msgInfo) {
30 | int generatedId = -1; // 默认ID值,如果没有生成ID,则保持此值
31 | String checkSql = "SELECT id FROM "+ tableName +" WHERE msg_hash = ? ;";
32 |
33 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(checkSql)) {
34 | // 检查记录是否存在
35 | stmt.setString(1, msgInfo.getMsgHash());
36 | ResultSet rs = stmt.executeQuery();
37 | if (rs.next()) {
38 | // 记录存在,忽略操作
39 | // stdout_println(LOG_INFO, String.format("[*] Ignore Update [%s] %s -> %s", tableName, msgInfo.getUrlInfo().getReqUrl(), msgInfo.getMsgHash()));
40 | return 0;
41 | } else {
42 | // 记录不存在,插入新记录
43 | String insertSql = "INSERT INTO "+ tableName +
44 | " (msg_hash, req_url, req_bytes, resp_bytes)" +
45 | " VALUES (?, ?, ?, ?)";
46 | try (PreparedStatement insertStmt = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
47 | insertStmt.setString(1, msgInfo.getMsgHash());
48 | insertStmt.setString(2, msgInfo.getUrlInfo().getRawUrlUsual());
49 | insertStmt.setBytes(3, msgInfo.getReqBytes());
50 | insertStmt.setBytes(4, msgInfo.getRespBytes());
51 | insertStmt.executeUpdate();
52 |
53 | // 获取生成的键值
54 | try (ResultSet generatedKeys = insertStmt.getGeneratedKeys()) {
55 | if (generatedKeys.next()) {
56 | generatedId = generatedKeys.getInt(1); // 获取生成的ID
57 | }
58 | }
59 | }
60 | }
61 | } catch (Exception e) {
62 | stderr_println(String.format("[-] Error inserting or updating table [%s] -> Error:[%s]", tableName, e.getMessage()));
63 | e.printStackTrace();
64 | }
65 |
66 | return generatedId; // 返回ID值,无论是更新还是插入
67 | }
68 |
69 | /**
70 | * 基于id获取对应的数据 考虑更换为msg_hash
71 | * @return
72 | */
73 | public static synchronized ReqMsgDataModel fetchMsgDataById(Integer msgDataIndex){
74 | ReqMsgDataModel msgData = null;
75 |
76 | String sql = "SELECT * FROM "+ tableName +" WHERE id = ?;";
77 |
78 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(sql)) {
79 | stmt.setInt(1, msgDataIndex);
80 | try (ResultSet rs = stmt.executeQuery()) {
81 | if (rs.next()) {
82 | msgData = new ReqMsgDataModel(
83 | rs.getString("msg_hash"),
84 | rs.getString("req_url"),
85 | rs.getBytes("req_bytes"),
86 | rs.getBytes("resp_bytes")
87 | );
88 | }
89 | }
90 | } catch (Exception e) {
91 | stderr_println(LOG_ERROR, String.format("[-] Error Select Msg Data By Id: %s -> %s", msgDataIndex, e.getMessage()));
92 | }
93 | return msgData;
94 | }
95 |
96 | /**
97 | * 根据消息ID查询请求内容
98 | * @return
99 | */
100 | public static synchronized ReqMsgDataModel fetchMsgDataByMsgHash(String msgHash){
101 | ReqMsgDataModel msgData = null;
102 |
103 | String sql = "SELECT * FROM "+ tableName + " WHERE msg_hash = ?;";
104 |
105 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(sql)) {
106 | stmt.setString(1, msgHash);
107 | try (ResultSet rs = stmt.executeQuery()) {
108 | if (rs.next()) {
109 | msgData = new ReqMsgDataModel(
110 | rs.getString("msg_hash"),
111 | rs.getString("req_url"),
112 | rs.getBytes("req_bytes"),
113 | rs.getBytes("resp_bytes")
114 | );
115 | }
116 | }
117 | } catch (Exception e) {
118 | stderr_println(LOG_ERROR, String.format("[-] Error Select Msg Data By Msg Hash: %s -> %s", msgHash, e.getMessage()));
119 | }
120 | return msgData;
121 | }
122 |
123 | /**
124 | * 根据 RootUrl 查询请求内容 最新的一条
125 | * @return
126 | */
127 | public static ReqMsgDataModel fetchMsgDataByRootUrlDesc(String rootUrl) {
128 | ReqMsgDataModel msgData = null;
129 |
130 | String sql = "SELECT * FROM "+ tableName + " WHERE req_url like ? ORDER BY id DESC Limit 1;";
131 |
132 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(sql)) {
133 | stmt.setString(1, rootUrl + '%');
134 |
135 | ResultSet rs = stmt.executeQuery();
136 | if (rs.next()) {
137 | msgData = new ReqMsgDataModel(
138 | rs.getString("msg_hash"),
139 | rs.getString("req_url"),
140 | rs.getBytes("req_bytes"),
141 | rs.getBytes("resp_bytes")
142 | );
143 | }
144 | } catch (Exception e) {
145 | stderr_println(LOG_ERROR, String.format("[-] Error Select Msg Data By rootUrl: %s -> %s", rootUrl, e.getMessage()));
146 | }
147 | return msgData;
148 | }
149 |
150 | /**
151 | * 根据消息ID查询请求内容
152 | */
153 | public static synchronized List fetchMsgDataByMsgHashList(List msgHashList){
154 | List reqMsgDataModelList = new ArrayList<>();
155 | if (msgHashList.isEmpty()) return reqMsgDataModelList;
156 |
157 | String selectSQL = ("SELECT * FROM "+ tableName + " WHERE msg_hash IN $buildInParameterList$;")
158 | .replace("$buildInParameterList$", DBService.buildInParamList(msgHashList.size()));
159 |
160 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
161 | for (int i = 0; i < msgHashList.size(); i++) {
162 | stmt.setString(i + 1, msgHashList.get(i));
163 | }
164 |
165 | try (ResultSet rs = stmt.executeQuery()) {
166 | while (rs.next()) {
167 | ReqMsgDataModel msgData = new ReqMsgDataModel(
168 | rs.getString("msg_hash"),
169 | rs.getString("req_url"),
170 | rs.getBytes("req_bytes"),
171 | rs.getBytes("resp_bytes")
172 | );
173 | reqMsgDataModelList.add(msgData);
174 | }
175 | }
176 | } catch (Exception e) {
177 | stderr_println(LOG_ERROR, String.format("[-] Error Batch Select Msg Data By Msg Hash: %s -> %s", msgHashList, e.getMessage()));
178 | }
179 | return reqMsgDataModelList;
180 | }
181 |
182 | }
183 |
--------------------------------------------------------------------------------
/src/main/java/database/TableLineDataModelBasicHostSQL.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.BasicHostTableLineDataModel;
4 |
5 | import java.sql.Connection;
6 | import java.sql.PreparedStatement;
7 | import java.sql.ResultSet;
8 | import java.util.ArrayList;
9 |
10 | import static utils.BurpPrintUtils.LOG_ERROR;
11 | import static utils.BurpPrintUtils.stderr_println;
12 |
13 | /**
14 | * 存储基于主机相关的SQL查询函数
15 | */
16 | public class TableLineDataModelBasicHostSQL {
17 |
18 |
19 | private static String genHostTableSqlByWhereCondition(String WhereCondition){
20 | String selectSQL = ("SELECT id,root_url,find_info_num,has_important,find_url_num," +
21 | "find_path_num,find_api_num,path_to_url_num,unvisited_url_num,basic_path_num,all_url_num,run_status " +
22 | "FROM $tableName$ $WHERE$;")
23 | .replace("$tableName$", AnalyseHostResultTable.tableName);
24 | if (WhereCondition == null) WhereCondition= "";
25 | return selectSQL.replace("$WHERE$", WhereCondition);
26 | }
27 |
28 | //联合 获取所有行数据
29 | public static synchronized ArrayList fetchHostTableLineBySQl(String selectSQL){
30 | ArrayList apiDataModels = new ArrayList<>();
31 |
32 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
33 | try (ResultSet rs = stmt.executeQuery()) {
34 | while (rs.next()) {
35 | BasicHostTableLineDataModel apiDataModel = new BasicHostTableLineDataModel(
36 | rs.getInt("id"),
37 | rs.getString("root_url"),
38 | rs.getInt("find_info_num"),
39 | rs.getBoolean("has_important"),
40 | rs.getInt("find_url_num"),
41 | rs.getInt("find_path_num"),
42 | rs.getInt("find_api_num"),
43 | rs.getInt("path_to_url_num"),
44 | rs.getInt("unvisited_url_num"),
45 | rs.getInt("all_url_num"),
46 | rs.getInt("basic_path_num"),
47 | rs.getString("run_status")
48 | );
49 | apiDataModels.add(apiDataModel);
50 | }
51 | }
52 | } catch (Exception e) {
53 | stderr_println(LOG_ERROR, String.format("[-] Error Fetch All ReqData Left Join InfoAnalyse On SQL: %s", e.getMessage()));
54 | }
55 | return apiDataModels;
56 | }
57 |
58 | // 获取当前所有记录
59 | public static synchronized ArrayList fetchHostTableLineAll() {
60 | String selectSQL = genHostTableSqlByWhereCondition(null);
61 | return fetchHostTableLineBySQl(selectSQL);
62 | }
63 |
64 | //获取有效数据的行
65 | public static synchronized ArrayList fetchHostTableLineHasInfoOrUri() {
66 | // 获取当前所有记录的数据
67 | String WhereCondition = "Where find_url_num>0 or find_path_num>0 or find_info_num>0";
68 | String selectSQL = genHostTableSqlByWhereCondition(WhereCondition);
69 | return fetchHostTableLineBySQl(selectSQL);
70 | }
71 |
72 | //获取有效数据的行 并且忽略已经处理的项
73 | public static synchronized ArrayList fetchHostTableLineHasInfoOrUriNotHandle() {
74 | // 获取当前所有记录的数据
75 | String WhereCondition = ("Where (find_url_num>0 or find_path_num>0 or find_info_num>0) and run_status != 'RUN_STATUS'")
76 | .replace("RUN_STATUS", Constants.HANDLE_END);
77 | String selectSQL = genHostTableSqlByWhereCondition(WhereCondition);
78 | return fetchHostTableLineBySQl(selectSQL);
79 | }
80 |
81 | //获取敏感数据的行
82 | public static synchronized ArrayList fetchHostTableLineHasInfo() {
83 | // 获取当前所有记录的数据
84 | String WhereCondition = "where find_info_num>0";
85 | String selectSQL = genHostTableSqlByWhereCondition(WhereCondition);
86 | return fetchHostTableLineBySQl(selectSQL);
87 | }
88 |
89 | //获取敏感数据的行 并且忽略已经处理的项
90 | public static synchronized ArrayList fetchHostTableLineHasInfoNotHandle() {
91 | // 获取当前所有记录的数据
92 | String WhereCondition = ("where find_info_num>0 and run_status != 'RUN_STATUS'")
93 | .replace("RUN_STATUS", Constants.HANDLE_END);
94 | String selectSQL = genHostTableSqlByWhereCondition(WhereCondition);
95 | return fetchHostTableLineBySQl(selectSQL);
96 | }
97 |
98 | public static synchronized ArrayList fetchHostTableLineHasUnVisitedUrls() {
99 | // 获取当前所有记录的数据
100 | String WhereCondition = "where unvisited_url_num>0";
101 | String selectSQL = genHostTableSqlByWhereCondition(WhereCondition);
102 | return fetchHostTableLineBySQl(selectSQL);
103 | }
104 |
105 | public static synchronized ArrayList fetchHostTableLineAnyIsNull() {
106 | // 获取当前所有记录的数据
107 | String WhereCondition = "where (find_url_num is null and find_path_num is null and find_info_num is null) or (find_url_num <1 and find_path_num <1 and find_info_num <1) ";
108 | String selectSQL = genHostTableSqlByWhereCondition(WhereCondition);
109 | return fetchHostTableLineBySQl(selectSQL);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/database/TableLineDataModelBasicUrlSQL.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.BasicUrlTableLineDataModel;
4 |
5 | import java.sql.Connection;
6 | import java.sql.PreparedStatement;
7 | import java.sql.ResultSet;
8 | import java.util.ArrayList;
9 |
10 | import static utils.BurpPrintUtils.*;
11 |
12 | public class TableLineDataModelBasicUrlSQL {
13 | private static String genUrlTableSqlByWhereCondition(String WhereCondition){
14 | String selectSQL = ("SELECT A.id,A.msg_hash,A.req_url,A.req_method,A.resp_status_code,A.req_source,A.run_status,A.resp_length," +
15 | "B.find_url_num,B.find_path_num,B.find_info_num,B.has_important,B.find_api_num " +
16 | "from $tableName1$ A LEFT JOIN $tableName2$ B ON A.msg_hash = B.msg_hash $WHERE$ order by A.id;")
17 | .replace("$tableName1$", ReqDataTable.tableName)
18 | .replace("$tableName2$", AnalyseUrlResultTable.tableName);
19 |
20 | if (WhereCondition == null) WhereCondition="";
21 |
22 | return selectSQL.replace("$WHERE$", WhereCondition);
23 | }
24 |
25 | //联合 获取所有行数据
26 | public static synchronized ArrayList fetchUrlTableLineBySQl(String selectSQL){
27 | ArrayList apiDataModels = new ArrayList<>();
28 |
29 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
30 | try (ResultSet rs = stmt.executeQuery()) {
31 | while (rs.next()) {
32 | BasicUrlTableLineDataModel apiDataModel = new BasicUrlTableLineDataModel(
33 | rs.getInt("id"),
34 | rs.getString("msg_hash"),
35 | rs.getString("req_url"),
36 | rs.getString("req_method"),
37 | rs.getInt("resp_status_code"),
38 | rs.getString("req_source"),
39 | rs.getInt("find_url_num"),
40 | rs.getInt("find_path_num"),
41 | rs.getInt("find_info_num"),
42 | rs.getBoolean("has_important"),
43 | rs.getInt("find_api_num"),
44 | rs.getString("run_status"),
45 | rs.getInt("resp_length")
46 | );
47 | apiDataModels.add(apiDataModel);
48 | }
49 | }
50 | } catch (Exception e) {
51 | stderr_println(LOG_ERROR, String.format("[-] Error Fetch All ReqData Left Join InfoAnalyse On MsgHash: %s", e.getMessage()));
52 | }
53 | return apiDataModels;
54 | }
55 |
56 | // 获取当前所有记录
57 | public static synchronized ArrayList fetchUrlTableLineAll() {
58 | String selectSQL = genUrlTableSqlByWhereCondition(null);
59 | return fetchUrlTableLineBySQl(selectSQL);
60 | }
61 |
62 | //获取有效数据的行
63 | public static synchronized ArrayList fetchUrlTableLineHasInfoOrUri() {
64 | // 获取当前所有记录的数据
65 | String WhereCondition = "Where find_url_num>0 or find_path_num>0 or find_info_num>0";
66 | String selectSQL = genUrlTableSqlByWhereCondition(WhereCondition);
67 | return fetchUrlTableLineBySQl(selectSQL);
68 | }
69 |
70 | //获取有效数据的行 并且忽略已处理的项
71 | public static synchronized ArrayList fetchUrlTableLineHasInfoOrUriNotHandle() {
72 | // 获取当前所有记录的数据
73 | String WhereCondition = ("Where (find_url_num>0 or find_path_num>0 or find_info_num>0) and A.run_status != 'RUN_STATUS'")
74 | .replace("RUN_STATUS", Constants.HANDLE_END);
75 | String selectSQL = genUrlTableSqlByWhereCondition(WhereCondition);
76 | return fetchUrlTableLineBySQl(selectSQL);
77 | }
78 |
79 | //获取存在敏感信息的行
80 | public static synchronized ArrayList fetchUrlTableLineHasInfo() {
81 | // 获取当前所有记录的数据
82 | String WhereCondition = "where find_info_num>0";
83 | String selectSQL = genUrlTableSqlByWhereCondition(WhereCondition);
84 | return fetchUrlTableLineBySQl(selectSQL);
85 | }
86 |
87 | //获取存在敏感信息的行 并且忽略已处理的项
88 | public static synchronized ArrayList fetchUrlTableLineHasInfoNotHandle() {
89 | // 获取当前所有记录的数据
90 | String WhereCondition = ("where find_info_num>0 and A.run_status != 'RUN_STATUS'")
91 | .replace("RUN_STATUS", Constants.HANDLE_END);
92 | String selectSQL = genUrlTableSqlByWhereCondition(WhereCondition);
93 | return fetchUrlTableLineBySQl(selectSQL);
94 | }
95 |
96 | //获取没有数据的行,备用,用于后续删除数据
97 | public static synchronized ArrayList fetchUrlTableLineAnyIsNull() {
98 | // 获取当前所有记录的数据
99 | String WhereCondition = "where (find_url_num is null and find_path_num is null and find_info_num is null) or (find_url_num <1 and find_path_num <1 and find_info_num <1) ";
100 | String selectSQL = genUrlTableSqlByWhereCondition(WhereCondition);
101 | return fetchUrlTableLineBySQl(selectSQL);
102 | }
103 |
104 | //获取没有数据的行,备用,用于后续删除数据
105 | public static synchronized int clearUrlTableLineAnyIsNull() {
106 | int rowsAffected = -1;
107 |
108 | // 获取当前所有记录的数据
109 | String deleteSQL = ("DELETE FROM $tableName1$ WHERE id IN (" +
110 | "SELECT A.id FROM $tableName1$ A LEFT JOIN $tableName2$ B ON A.msg_hash=B.msg_hash " +
111 | "WHERE (find_url_num IS NULL AND find_path_num IS NULL AND find_info_num IS NULL) " +
112 | "OR (find_url_num < 1 AND find_path_num < 1 AND find_info_num < 1));")
113 | .replace("$tableName1$", ReqDataTable.tableName)
114 | .replace("$tableName2$", AnalyseUrlResultTable.tableName);
115 |
116 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(deleteSQL)) {
117 | rowsAffected = stmt.executeUpdate();
118 | stdout_println(LOG_DEBUG, String.format(String.format("[-] table [%s] cleared Useless Data [%s] line.", ReqDataTable.tableName, rowsAffected)));
119 | } catch (Exception e) {
120 | stderr_println(LOG_ERROR, String.format("[-] Error clear Useless Data On Table [%s] -> Error:[%s]", ReqDataTable.tableName, e.getMessage()));
121 | e.printStackTrace();
122 | }
123 |
124 | return rowsAffected;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/database/UnionTableSql.java:
--------------------------------------------------------------------------------
1 | package database;
2 |
3 | import model.FindPathModel;
4 |
5 | import java.sql.Connection;
6 | import java.sql.PreparedStatement;
7 | import java.sql.ResultSet;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import static utils.BurpPrintUtils.*;
12 |
13 | public class UnionTableSql {
14 | //联合 获取一条需要更新的Path数据
15 | public static synchronized List fetchHostTableNeedUpdatePathDataList(int limit){
16 | List findPathModels = new ArrayList<>();
17 |
18 | // 首先选取一条记录的ID 状态是已经分析完毕,并且 当前 PathTree 的 基本路径数量 大于 生成分析数据时的 基本路径数量
19 | String selectSQL = ("SELECT A.id, A.root_url, A.find_path " +
20 | "From $tableName1$ A LEFT JOIN $tableName2$ B ON A.root_url = B.root_url " +
21 | "WHERE B.basic_path_num > A.basic_path_num Limit ?;")
22 | .replace("$tableName1$", AnalyseHostResultTable.tableName)
23 | .replace("$tableName2$", PathTreeTable.tableName);
24 |
25 | try (Connection conn = DBService.getInstance().getNewConn(); PreparedStatement stmt = conn.prepareStatement(selectSQL)) {
26 | stmt.setInt(1, limit);
27 |
28 | try (ResultSet rs = stmt.executeQuery()) {
29 | while (rs.next()) {
30 | FindPathModel findPathModel = new FindPathModel(
31 | rs.getInt("id"),
32 | rs.getString("root_url"),
33 | rs.getString("find_path")
34 | );
35 | findPathModels.add(findPathModel);
36 | }
37 | }
38 | } catch (Exception e) {
39 | stderr_println(LOG_ERROR, String.format("[-] Error fetch Need Update Path Data List: %s", e.getMessage()));
40 | }
41 | return findPathModels;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/model/AccessedUrlInfo.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import static utils.RespHashUtils.calcCRC32;
4 |
5 | public class AccessedUrlInfo {
6 | public String rootUrl;
7 | public String reqUrl;
8 | public String urlHash;
9 | public int respStatusCode;
10 |
11 | public AccessedUrlInfo(String reqUrl, String rootUrl, int respStatusCode) {
12 | this.reqUrl = reqUrl;
13 | this.rootUrl = rootUrl;
14 | this.respStatusCode = respStatusCode;
15 | this.urlHash = calcCRC32(reqUrl);
16 | }
17 |
18 | public String getUrlHash() {
19 | return urlHash;
20 | }
21 |
22 | public String getReqUrl() {
23 | return reqUrl;
24 | }
25 |
26 | public String getRootUrl() {
27 | return rootUrl;
28 | }
29 |
30 | public int getRespStatusCode() {
31 | return respStatusCode;
32 | }
33 |
34 |
35 | }
--------------------------------------------------------------------------------
/src/main/java/model/AnalyseHostResultModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import com.alibaba.fastjson2.JSONArray;
4 | import utils.CastUtils;
5 |
6 | import java.util.HashMap;
7 | import java.util.List;
8 |
9 | /**
10 | * 用来存储基于主机的结果的模型
11 | */
12 | public class AnalyseHostResultModel {
13 | private String rootUrl;
14 | private HashMap urlInfoArrayMap;
15 | private List urlList;
16 | private List pathList;
17 | private List apiList;
18 | private Boolean hasImportant;
19 |
20 | // 中转构造函数
21 | public AnalyseHostResultModel(AnalyseUrlResultModel analyseUrlResultModel) {
22 | this.rootUrl = new HttpUrlInfo(analyseUrlResultModel.getReqUrl()).getRootUrlUsual();
23 | this.urlInfoArrayMap = analyseUrlResultModel.getUrlInfoArrayMap();
24 | this.urlList = analyseUrlResultModel.getUrlList();
25 | this.pathList = analyseUrlResultModel.getPathList();
26 | this.apiList = analyseUrlResultModel.getApiList();
27 | this.hasImportant = analyseUrlResultModel.getHasImportant();
28 | }
29 |
30 | public String getRootUrl() {
31 | return rootUrl;
32 | }
33 |
34 | public HashMap getUrlInfoArrayMap() {
35 | return urlInfoArrayMap;
36 | }
37 |
38 | public List getUrlList() {
39 | return urlList;
40 | }
41 |
42 | public List getPathList() {
43 | return pathList;
44 | }
45 |
46 | public List getApiList() {
47 | return apiList;
48 | }
49 |
50 | public Boolean getHasImportant() {
51 | return hasImportant;
52 | }
53 |
54 | public List getUnvisitedUrlList() {
55 | return CastUtils.listAddList(this.urlList, this.apiList);
56 | }
57 |
58 | public List getUnvisitedUrlList(boolean addApiList) {
59 | if (addApiList)
60 | return CastUtils.listAddList(this.urlList, this.apiList);
61 | else
62 | return this.urlList;
63 | }
64 | }
--------------------------------------------------------------------------------
/src/main/java/model/AnalyseUrlResultModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import com.alibaba.fastjson2.JSONArray;
4 | import utils.CastUtils;
5 |
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | public class AnalyseUrlResultModel {
11 | private String reqUrl;
12 | private JSONArray infoArray;
13 | private List urlList;
14 | private List pathList;
15 | private List apiList;
16 | private Boolean hasImportant;
17 |
18 | //新增一个URL类型的
19 | public AnalyseUrlResultModel(String reqUrl, JSONArray infoArray, List urlList, List pathList, List apiList, Boolean hasImportant) {
20 | this.reqUrl = reqUrl;
21 | this.infoArray = infoArray;
22 | this.urlList = urlList;
23 | this.pathList = pathList;
24 | this.apiList = apiList;
25 | this.hasImportant = hasImportant;
26 | }
27 |
28 | public AnalyseUrlResultModel(String reqUrl, String infoJsonArrayStr, String urlListStr, String pathListStr, String apiListStr, Boolean hasImportant) {
29 | this.reqUrl = reqUrl;
30 | this.infoArray = CastUtils.toJsonArray(infoJsonArrayStr);
31 | this.urlList = CastUtils.toStringList(urlListStr);
32 | this.pathList = CastUtils.toStringList(pathListStr);
33 | this.apiList = CastUtils.toStringList(apiListStr);
34 | this.hasImportant = hasImportant;
35 | }
36 |
37 | public String getReqUrl() {
38 | return reqUrl;
39 | }
40 |
41 | public JSONArray getInfoArray() {
42 | return infoArray;
43 | }
44 |
45 | public List getUrlList() {
46 | return urlList;
47 | }
48 |
49 | public List getPathList() {
50 | return pathList;
51 | }
52 |
53 | public List getApiList() {
54 | return apiList;
55 | }
56 |
57 | public Boolean getHasImportant() {
58 | return hasImportant;
59 | }
60 |
61 | public HashMap getUrlInfoArrayMap() {
62 | HashMap urlInfoArrayMap= new HashMap<>();
63 | if (!infoArray.isEmpty()){
64 | urlInfoArrayMap.put(reqUrl,infoArray);
65 | }
66 | return urlInfoArrayMap;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/model/BasicHostTableLineDataModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 |
4 | public class BasicHostTableLineDataModel {
5 | private Integer id;
6 | private String rootUrl;
7 | private String host;
8 | private String domain;
9 |
10 | private Integer findInfoNum;
11 | private Boolean hasImportant;
12 |
13 | private Integer findUrlNum;
14 | private Integer findPathNum;
15 | private Integer findApiNum;
16 |
17 | private Integer pathToUrlNum;
18 | private Integer unvisitedUrlNum;
19 | private Integer allUrlNum;
20 | private Integer basicPathNum;
21 |
22 | private String runStatus;
23 |
24 | // 构造函数
25 | public BasicHostTableLineDataModel(int id, String rootUrl,
26 | int findInfoNum, boolean hasImportant,
27 | int findUrlNum, int findPathNum, int findApiNum,
28 | int pathToUrlNum, int unvisitedUrlNum,
29 | int allUrlNum, int basicPathNum, String runStatus) {
30 | this.id = id;
31 | this.rootUrl = rootUrl;
32 | this.host = parseHostFromUrl(rootUrl);
33 | this.domain = parseDomainFromUrl(rootUrl);
34 |
35 | this.findInfoNum = findInfoNum;
36 | this.hasImportant = hasImportant;
37 |
38 |
39 | this.findUrlNum = findUrlNum;
40 | this.findPathNum = findPathNum;
41 | this.findApiNum = findApiNum;
42 |
43 | this.pathToUrlNum = pathToUrlNum;
44 | this.unvisitedUrlNum = unvisitedUrlNum;
45 |
46 | this.allUrlNum = allUrlNum;
47 | this.basicPathNum = basicPathNum;
48 |
49 | this.runStatus = runStatus;
50 | }
51 |
52 | private String parseHostFromUrl(String rootUrl) {
53 | //从URL中解析出host
54 | return new HttpUrlInfo(rootUrl).getHostPort();
55 | }
56 |
57 | private String parseDomainFromUrl(String rootUrl) {
58 | //从URL中解析出domain
59 | return new HttpUrlInfo(rootUrl).getRootDomain();
60 | }
61 |
62 |
63 | public Object[] toRowDataArray() {
64 | return new Object[]{
65 | this.getId(),
66 | this.getRootUrl(),
67 | this.getHost(),
68 | this.getDomain(),
69 | this.getHasImportant(),
70 | this.getFindInfoNum(),
71 | this.getFindUrlNum(),
72 | this.getFindPathNum(),
73 | this.getFindApiNum(),
74 | this.getPathToUrlNum(),
75 | this.getUnvisitedUrlNum(),
76 | this.getAllUrlNum(),
77 | this.getBasicPathNum(),
78 | this.getRunStatus()
79 | };
80 | }
81 |
82 | public Integer getId() {
83 | return id;
84 | }
85 |
86 | public String getRootUrl() {
87 | return rootUrl;
88 | }
89 |
90 | public Integer getFindInfoNum() {
91 | return findInfoNum;
92 | }
93 |
94 | public Boolean getHasImportant() {
95 | return hasImportant;
96 | }
97 |
98 | public Integer getFindUrlNum() {
99 | return findUrlNum;
100 | }
101 |
102 | public Integer getFindPathNum() {
103 | return findPathNum;
104 | }
105 |
106 | public Integer getFindApiNum() {
107 | return findApiNum;
108 | }
109 |
110 | public Integer getPathToUrlNum() {
111 | return pathToUrlNum;
112 | }
113 |
114 | public Integer getUnvisitedUrlNum() {
115 | return unvisitedUrlNum;
116 | }
117 |
118 | public Integer getBasicPathNum() {
119 | return basicPathNum;
120 | }
121 |
122 | public String getRunStatus() {
123 | return runStatus;
124 | }
125 |
126 | public Integer getAllUrlNum() {
127 | return allUrlNum;
128 | }
129 |
130 | public String getHost() {
131 | return host;
132 | }
133 | public String getDomain() {
134 | return domain;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/main/java/model/BasicHostTableTabDataModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | public class BasicHostTableTabDataModel {
4 | private String rootUrl;
5 |
6 | private String findInfo;
7 | private String findUrl;
8 | private String findPath;
9 | private String findApi;
10 |
11 | private String pathToUrl;
12 | private String unvisitedUrl;
13 |
14 | private String allUrlStatus;
15 |
16 |
17 | public BasicHostTableTabDataModel(String rootUrl, String findInfo, String findUrl, String findPath,
18 | String findApi, String pathToUrl, String unvisitedUrl, String allUrlStatus) {
19 | this.rootUrl = rootUrl;
20 | this.findUrl = findUrl;
21 | this.findPath = findPath;
22 | this.findInfo = findInfo;
23 | this.findApi = findApi;
24 | this.pathToUrl = pathToUrl;
25 | this.unvisitedUrl = unvisitedUrl;
26 | this.allUrlStatus = allUrlStatus;
27 | }
28 |
29 | public String getRootUrl() {
30 | return rootUrl;
31 | }
32 |
33 | public String getFindUrl() {
34 | return findUrl;
35 | }
36 |
37 | public String getFindPath() {
38 | return findPath;
39 | }
40 |
41 | public String getFindInfo() {
42 | return findInfo;
43 | }
44 |
45 | public String getFindApi() {
46 | return findApi;
47 | }
48 |
49 | public String getPathToUrl() {
50 | return pathToUrl;
51 | }
52 |
53 | public String getUnvisitedUrl() {
54 | return unvisitedUrl;
55 | }
56 |
57 | public String getAllUrlStatus() {
58 | return allUrlStatus;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/model/BasicUrlTableLineDataModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 |
4 | public class BasicUrlTableLineDataModel {
5 | private Integer id;
6 | private String msgHash;
7 | private String reqUrl;
8 | private String reqMethod;
9 | private Integer respStatusCode;
10 | private String reqSource;
11 | private Integer findUrlNum;
12 | private Integer findPathNum;
13 | private Integer findInfoNum;
14 | private Integer findApiNum;
15 | private String runStatus;
16 | private Integer respLength;
17 | private Boolean hasImportant;
18 | // 构造函数
19 | public BasicUrlTableLineDataModel(int id, String msgHash, String reqUrl, String reqMethod, int respStatusCode,
20 | String reqSource, int findUrlNum, int findPathNum, int findInfoNum,
21 | boolean hasImportant, int findApiNum, String runStatus, int respLength) {
22 | this.id = id;
23 | this.msgHash = msgHash;
24 | this.reqUrl = reqUrl;
25 | this.reqMethod = reqMethod;
26 | this.respStatusCode = respStatusCode;
27 | this.reqSource = reqSource;
28 | this.findUrlNum = findUrlNum;
29 | this.findPathNum = findPathNum;
30 | this.findInfoNum = findInfoNum;
31 | this.findApiNum = findApiNum;
32 | this.runStatus = runStatus;
33 | this.respLength = respLength;
34 | this.hasImportant = hasImportant;
35 | }
36 |
37 | public Object[] toRowDataArray() {
38 | return new Object[]{
39 | this.getId(),
40 | this.getReqSource(),
41 | this.getMsgHash(),
42 | this.getReqUrl(),
43 | this.getReqMethod(),
44 | this.getRespStatusCode(),
45 | this.getRespLength(),
46 | this.getHasImportant(),
47 | this.getFindInfoNum(),
48 | this.getFindUrlNum(),
49 | this.getFindPathNum(),
50 | this.getFindApiNum(),
51 | this.getRunStatus()
52 | };
53 | }
54 |
55 | public Integer getId() {
56 | return id;
57 | }
58 |
59 | public String getMsgHash() {
60 | return msgHash;
61 | }
62 |
63 | public String getReqUrl() {
64 | return reqUrl;
65 | }
66 |
67 | public String getReqMethod() {
68 | return reqMethod;
69 | }
70 |
71 | public Integer getRespStatusCode() {
72 | return respStatusCode;
73 | }
74 |
75 | public String getReqSource() {
76 | return reqSource;
77 | }
78 |
79 | public Integer getFindUrlNum() {
80 | return findUrlNum;
81 | }
82 |
83 | public Integer getFindPathNum() {
84 | return findPathNum;
85 | }
86 |
87 | public Integer getFindInfoNum() {
88 | return findInfoNum;
89 | }
90 |
91 | public Integer getFindApiNum() {
92 | return findApiNum;
93 | }
94 |
95 | public String getRunStatus() {
96 | return runStatus;
97 | }
98 |
99 | public Integer getRespLength() {
100 | return respLength;
101 | }
102 |
103 | public Boolean getHasImportant() {
104 | return hasImportant;
105 | }
106 |
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/model/BasicUrlTableTabDataModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | public class BasicUrlTableTabDataModel {
4 | private String msgHash;
5 | private String findUrl;
6 | private String findPath;
7 | private String findInfo;
8 | private String findApi;
9 |
10 | public BasicUrlTableTabDataModel(String msgHash, String findUrl, String findPath, String findInfo,
11 | String findApi) {
12 | this.msgHash = msgHash;
13 | this.findUrl = findUrl;
14 | this.findPath = findPath;
15 | this.findInfo = findInfo;
16 | this.findApi = findApi;
17 | }
18 |
19 | public String getMsgHash() {
20 | return msgHash;
21 | }
22 |
23 | public String getFindUrl() {
24 | return findUrl;
25 | }
26 |
27 | public String getFindPath() {
28 | return findPath;
29 | }
30 |
31 | public String getFindInfo() {
32 | return findInfo;
33 | }
34 |
35 | public String getFindApi() {
36 | return findApi;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/model/FindPathModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import com.alibaba.fastjson2.JSONArray;
4 | import utils.PathTreeUtils;
5 |
6 | import java.util.LinkedHashSet;
7 | import java.util.List;
8 | import java.util.Set;
9 |
10 | public class FindPathModel {
11 | private int id;
12 | private String rootUrl;
13 | private JSONArray findPath;
14 |
15 |
16 | public FindPathModel(int id, String rootUrl, String findPath) {
17 | this.id = id;
18 | this.rootUrl = rootUrl;
19 | this.findPath = JSONArray.parse(findPath);
20 | }
21 |
22 | public int getId() {
23 | return id;
24 | }
25 |
26 |
27 | public String getRootUrl() {
28 | return rootUrl;
29 | }
30 |
31 |
32 | public JSONArray getFindPath() {
33 | return findPath;
34 | }
35 |
36 |
37 | /**
38 | * 从路径模型中获取单层路径
39 | * @param findPathModelList
40 | * @return
41 | */
42 | public static Set getSingleLayerPathSet(List findPathModelList) {
43 | Set pathSet = new LinkedHashSet<>();
44 | //查询msgHash列表对应的所有数据find path 数据
45 | for (FindPathModel findPathModel: findPathModelList){
46 | //逐个提取PATH 并 加入 pathSet
47 | JSONArray findPaths = findPathModel.getFindPath();
48 | if (!findPaths.isEmpty()){
49 | // 提取 path中的单层路径
50 | for (Object uriPath : findPaths){
51 | List uriPart = PathTreeUtils.getUrlPart((String) uriPath);
52 | if (uriPart.size() == 1){
53 | pathSet.add(PathTreeUtils.formatUriPath((String) uriPath));
54 | }
55 | }
56 | }
57 | }
58 | return pathSet;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/model/FingerPrintRule.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import utils.CastUtils;
4 |
5 | import java.text.SimpleDateFormat;
6 | import java.util.Date;
7 | import java.util.List;
8 |
9 | public class FingerPrintRule {
10 | private String matchType;
11 | private String location;
12 | private String describe;
13 | private List matchKeys;
14 | private boolean isImportant;
15 | private String type;
16 | private boolean isOpen;
17 | private String accuracy;
18 |
19 | // 新添加的构造函数
20 | public FingerPrintRule(String type, String describe, boolean isImportant, String matchType, String location, List matchKeys, boolean isOpen, String accuracy) {
21 | this.matchType = matchType;
22 | this.describe = describe;
23 | this.location = location;
24 | this.matchKeys = matchKeys;
25 | this.type = type;
26 | this.isImportant = isImportant;
27 | this.isOpen = isOpen;
28 | this.accuracy = accuracy;
29 | }
30 |
31 | public boolean getIsOpen(){
32 | return isOpen;
33 | }
34 |
35 | public void setOpen(boolean isOpen){
36 | this.isOpen = isOpen;
37 | }
38 |
39 | public String getAccuracy(){
40 | return accuracy;
41 | }
42 |
43 | public void setAccuracy(String accuracy){
44 | this.accuracy = accuracy;
45 | }
46 |
47 | public String getDescribe(){return describe;}
48 |
49 | public void setDescribe(String describe){
50 | this.describe = describe;
51 | }
52 |
53 | public String getType(){return type;}
54 |
55 | public void setType(String type){this.type = type;}
56 |
57 | public boolean getIsImportant(){return isImportant;}
58 |
59 | public void setIsImportant(boolean isImportant){this.isImportant = isImportant;}
60 |
61 | public String getMatchType() {
62 | return matchType;
63 | }
64 |
65 | public void setMatchType(String matchType) {
66 | this.matchType = matchType;
67 | }
68 |
69 | public String getLocation() {
70 | return location;
71 | }
72 |
73 | public void setLocation(String location) {
74 | this.location = location;
75 | }
76 |
77 | public List getMatchKeys() {
78 | return matchKeys;
79 | }
80 |
81 | public void setMatchKeys(List matchKeys) {
82 | this.matchKeys = matchKeys;
83 | }
84 |
85 | public String getInfo(String color){
86 | return "Time: " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + "
matchType: " + matchType + "
Type: " + type + "
accuracy: " + accuracy + "
describe: " + describe + "
location: " + location + "
matchKeys: " + CastUtils.listToString(matchKeys) + "
";
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/model/FingerPrintRulesWrapper.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import java.util.List;
4 |
5 |
6 | public class FingerPrintRulesWrapper {
7 | private List fingerprint;
8 |
9 | public List getFingerprint() {
10 | return fingerprint;
11 | }
12 |
13 | public void setFingerprint(List fingerprint) {
14 | this.fingerprint = fingerprint;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/model/HttpMsgInfo.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import burp.*;
4 |
5 | import java.nio.charset.StandardCharsets;
6 | import java.util.zip.CRC32;
7 |
8 | import static utils.CastUtils.isNotEmptyObj;
9 |
10 | //创建一个类用于存储 代理 流量的解析结果
11 | public class HttpMsgInfo {
12 | private static final IExtensionHelpers helpers = BurpExtender.getHelpers();
13 | private byte[] reqBytes;
14 | private byte[] respBytes;
15 | private String reqMethod;
16 | private HttpUrlInfo urlInfo;
17 | private HttpRespInfo respInfo;
18 | private int respStatusCode;
19 | private String respTitle;
20 | private String msgHash;
21 |
22 | // 构造函数
23 | public HttpMsgInfo(IInterceptedProxyMessage iInterceptedProxyMessage) {
24 | IHttpRequestResponse messageInfo = iInterceptedProxyMessage.getMessageInfo();
25 | reqBytes = messageInfo.getRequest();
26 |
27 | //请求方法
28 | IRequestInfo requestInfoBetter = helpers.analyzeRequest(messageInfo);
29 | reqMethod = requestInfoBetter.getMethod();
30 |
31 | //从请求URL解析部分信息 //直接从请求体是没有办法获取到请求URL信息的, URL此时只能从外部传入
32 | String reqUrl = requestInfoBetter.getUrl().toString();
33 | urlInfo = new HttpUrlInfo(reqUrl);
34 |
35 | //从响应结果解析部分信息
36 | respBytes = messageInfo.getResponse();
37 | respInfo = new HttpRespInfo(respBytes);
38 |
39 | //响应码是常用的
40 | respStatusCode = respInfo.getStatusCode();
41 | respTitle = respInfo.getRespTitle();
42 |
43 | //请求响应信息的简单hash值
44 | msgHash = calcMsgHash(urlInfo.getUrlToFileUsual(),reqMethod,respStatusCode,respInfo.getBodyLenVague());
45 | }
46 |
47 | // 构造函数
48 | public HttpMsgInfo(IHttpRequestResponse iHttpRequestResponse) {
49 | //请求信息
50 | reqBytes = iHttpRequestResponse.getRequest();
51 |
52 | //请求方法
53 | IHttpService httpService = iHttpRequestResponse.getHttpService();
54 | IRequestInfo requestInfoBetter = helpers.analyzeRequest(httpService,reqBytes);
55 | reqMethod = requestInfoBetter.getMethod();
56 |
57 | //从请求URL解析部分信息
58 | String reqUrl = requestInfoBetter.getUrl().toString();
59 | urlInfo = new HttpUrlInfo(reqUrl);
60 |
61 | //从响应结果解析部分信息
62 | respBytes = iHttpRequestResponse.getResponse();
63 | respInfo = new HttpRespInfo(respBytes);
64 |
65 | //响应码是常用的
66 | respStatusCode = respInfo.getStatusCode();
67 | respTitle = respInfo.getRespTitle();
68 |
69 | //请求响应信息的简单hash值
70 | msgHash = calcMsgHash(urlInfo.getUrlToFileUsual(),reqMethod,respStatusCode,respInfo.getBodyLenVague());
71 | }
72 |
73 |
74 | // 构造函数
75 | public HttpMsgInfo(String requestUrl, byte[] requestBytes, byte[] responseBytes, String msgInfoHash) {
76 | //请求信息
77 | reqBytes = requestBytes;
78 |
79 | //请求方法
80 | IRequestInfo requestInfoSimple = helpers.analyzeRequest(reqBytes);
81 | reqMethod = requestInfoSimple.getMethod();
82 |
83 | //从请求URL解析部分信息
84 | String reqUrl = requestUrl;
85 | urlInfo = new HttpUrlInfo(reqUrl);
86 |
87 | //从响应结果解析部分信息
88 | respBytes = responseBytes;
89 | respInfo = new HttpRespInfo(respBytes);
90 |
91 | //响应码是常用的
92 | respStatusCode = respInfo.getStatusCode();
93 | respTitle = respInfo.getRespTitle();
94 |
95 | //请求响应信息的简单hash值 因为中间可能截断了超大的响应体 , 因此最好手动传入 msgHash
96 | msgHash = msgInfoHash;
97 | }
98 |
99 | /**
100 | * 计算消息Hash
101 | */
102 | private String calcMsgHash(String urlToFileUsual, String reqMethod, int respStatusCode, int respBodyLenVague) {
103 | return calcCRC32(String.format("%s|%s|%s|%s", urlToFileUsual, reqMethod, respStatusCode, respBodyLenVague));
104 | }
105 |
106 | /**
107 | * 计算给定字符串的CRC32校验和,并以十六进制字符串形式返回。
108 | * @param string 要计算CRC32的字符串
109 | * @return 字符串的CRC32校验和的十六进制表示
110 | */
111 | private static String calcCRC32(String string) {
112 | // 使用 UTF-8 编码将字符串转换为字节数组
113 | byte[] inputBytes = string.getBytes(StandardCharsets.UTF_8);
114 | // 初始化CRC32对象
115 | CRC32 crc32 = new CRC32();
116 | // 更新CRC值
117 | crc32.update(inputBytes, 0, inputBytes.length);
118 | // 将计算后的CRC32值转换为十六进制字符串并返回
119 | return Long.toHexString(crc32.getValue()).toLowerCase();
120 | }
121 |
122 | public String getReqMethod() {
123 | return reqMethod;
124 | }
125 |
126 | public byte[] getRespBytes() {
127 | return respBytes;
128 | }
129 |
130 | public byte[] getReqBytes() {
131 | return reqBytes;
132 | }
133 |
134 | public String getMsgHash() {
135 | return msgHash;
136 | }
137 |
138 | public void setRespBytes(byte[] respBytes) {
139 | this.respBytes = respBytes;
140 | }
141 |
142 | public HttpUrlInfo getUrlInfo() {
143 | return urlInfo;
144 | }
145 |
146 | public HttpRespInfo getRespInfo() {
147 | return respInfo;
148 | }
149 |
150 | public int getRespStatusCode() {
151 | return respStatusCode;
152 | }
153 |
154 | public String getRespTitle() {
155 | return respTitle;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/model/HttpRespInfo.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import burp.BurpExtender;
4 | import burp.IExtensionHelpers;
5 | import burp.IResponseInfo;
6 | import utils.RespHashUtils;
7 | import utils.RespTitleUtils;
8 |
9 | import java.util.Arrays;
10 |
11 | public class HttpRespInfo {
12 | private static final IExtensionHelpers helpers = BurpExtender.getHelpers();
13 | private byte[] respBytes = "".getBytes();
14 | private int statusCode = -1;
15 | private int respLength = -1;
16 | private int bodyLength = -1;
17 | private int bodyLenVague = -1;
18 | private String inferredMimeType = "";
19 | private String statedMimeType = "";
20 | private int bodyOffset = -1;
21 | private String respTitle = "";
22 |
23 | private String iconHash = ""; //记录响应体的hash值
24 |
25 | public String getIconHash() {
26 | return iconHash;
27 | }
28 |
29 | HttpRespInfo(byte[] responseBytes) {
30 | if (responseBytes == null || responseBytes.length <= 0){
31 | // Warning: That response body is empty !!!
32 | return;
33 | }
34 |
35 | respBytes = responseBytes;
36 | //响应长度
37 | respLength = respBytes.length;
38 | //响应信息
39 | IResponseInfo responseInfo = helpers.analyzeResponse(respBytes);
40 | //响应状态码
41 | statusCode = responseInfo.getStatusCode();
42 | //获取响应类型
43 | inferredMimeType = responseInfo.getInferredMimeType(); //根据响应的内容自动推断出的 MIME 类型
44 | statedMimeType = responseInfo.getStatedMimeType(); //由服务器明确声明的内容类型
45 | //响应体分割标记
46 | bodyOffset = responseInfo.getBodyOffset();
47 | bodyLength = getBodyBytes().length;
48 | //大致的响应长度
49 | bodyLenVague = bodyLength / 200;
50 | //响应文本标题
51 | respTitle = RespTitleUtils.parseTextTitle(respBytes);
52 | //当响应类型是 ico 类型时计算一下hash值
53 | if (getStatedMimeType() != null && getStatedMimeType().contains("ico")){
54 | iconHash = RespHashUtils.getFaviconHash(getBodyBytes());
55 | }
56 | }
57 |
58 |
59 | /**
60 | * 获取 请求体或响应体的body部分
61 | */
62 | public byte[] getBodyBytes() {
63 | // 确保 bodyOffset 不会导致数组越界
64 | int bodyLength = Math.max(0, respBytes.length - bodyOffset);
65 |
66 | // 从 bytes 数组中复制 body 的部分
67 | return Arrays.copyOfRange(respBytes, bodyOffset, bodyOffset + bodyLength);
68 | }
69 |
70 | /**
71 | * 获取 请求或响应的头部信息部分
72 | */
73 | public byte[] getHeaderBytes() {
74 | // 确保 headerOffset 不会导致数组越界,并且至少是从0开始
75 | int headerLength = Math.max(0, bodyOffset);
76 | // 从 bytes 数组中复制 header 的部分
77 | return Arrays.copyOfRange(respBytes, 0, headerLength);
78 | }
79 |
80 | public int getStatusCode() {
81 | return statusCode;
82 | }
83 |
84 | public int getRespLength() {
85 | return respLength;
86 | }
87 |
88 | public int getBodyLength() {
89 | return bodyLength;
90 | }
91 |
92 | public int getBodyLenVague() {
93 | return bodyLenVague;
94 | }
95 |
96 | public String getInferredMimeType() {
97 | return inferredMimeType;
98 | }
99 |
100 | public String getStatedMimeType() {
101 | return statedMimeType;
102 | }
103 |
104 | public int getBodyOffset() {
105 | return bodyOffset;
106 | }
107 |
108 | public byte[] getRespBytes() {
109 | return respBytes;
110 | }
111 |
112 | public String getRespTitle() {
113 | return respTitle;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/model/PathToUrlsModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import com.alibaba.fastjson2.JSONArray;
4 | import utils.CastUtils;
5 |
6 | import java.util.List;
7 |
8 | public class PathToUrlsModel {
9 | private int id;
10 | private int basicPathNum;
11 | private List pathToUrls;
12 | private List unvisitedUrls;
13 |
14 | public PathToUrlsModel(int id, int basic_path_num, JSONArray pathToUrls, JSONArray unvisitedUrls) {
15 | this.id = id;
16 | this.basicPathNum = basic_path_num;
17 | this.pathToUrls = CastUtils.toStringList(pathToUrls);
18 | this.unvisitedUrls = CastUtils.toStringList(unvisitedUrls);
19 | }
20 |
21 | public PathToUrlsModel(int id, int basic_path_num, String pathToUrls, String unvisitedUrls) {
22 | this.id = id;
23 | this.basicPathNum = basic_path_num;
24 | this.pathToUrls = CastUtils.toStringList(pathToUrls);
25 | this.unvisitedUrls = CastUtils.toStringList(unvisitedUrls);
26 | }
27 |
28 | public int getId() {
29 | return id;
30 | }
31 |
32 | public void setId(int id) {
33 | this.id = id;
34 | }
35 |
36 | public int getBasicPathNum() {
37 | return basicPathNum;
38 | }
39 |
40 | public void setBasicPathNum(int basicPathNum) {
41 | this.basicPathNum = basicPathNum;
42 | }
43 |
44 | public List getPathToUrls() {
45 | return pathToUrls;
46 | }
47 |
48 | public void setPathToUrls(List pathToUrls) {
49 | this.pathToUrls = pathToUrls;
50 | }
51 |
52 | public List getUnvisitedUrls() {
53 | return unvisitedUrls;
54 | }
55 |
56 | public void setUnvisitedUrls(List unvisitedUrls) {
57 | this.unvisitedUrls = unvisitedUrls;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/model/PathTreeModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import com.alibaba.fastjson2.JSONObject;
4 | import utils.CastUtils;
5 |
6 | public class PathTreeModel {
7 | private String rootUrl;
8 | private Integer basicPathNum;
9 | private JSONObject pathTree;
10 |
11 | public PathTreeModel(String rootUrl, Integer basicPathNum, JSONObject pathTree) {
12 | this.rootUrl = rootUrl;
13 | this.basicPathNum = basicPathNum;
14 | this.pathTree = pathTree;
15 | }
16 |
17 | public PathTreeModel(String rootUrl, int basicPathNum, String pathTree) {
18 | this.rootUrl = rootUrl;
19 | this.basicPathNum = basicPathNum;
20 | this.pathTree = CastUtils.toJsonObject(pathTree);
21 | }
22 |
23 |
24 |
25 | public Integer getBasicPathNum() {
26 | return basicPathNum;
27 | }
28 |
29 | public JSONObject getPathTree() {
30 | return pathTree;
31 | }
32 |
33 | public String getRootUrl() {
34 | return rootUrl;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/model/RecordHashMap.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 |
6 | public class RecordHashMap {
7 |
8 | private final ConcurrentHashMap countMap;
9 |
10 | public RecordHashMap() {
11 | this.countMap = new ConcurrentHashMap<>();
12 | }
13 |
14 | public Map getStringMap() {
15 | return this.countMap;
16 | }
17 |
18 | public Integer get(String key) {
19 | Integer ret = this.countMap.get(key);
20 | if (ret == null) {
21 | return 0;
22 | } else {
23 | return ret;
24 | }
25 | }
26 |
27 | public void add(String key) {
28 | if (key == null || key.length() <= 0) {
29 | throw new IllegalArgumentException("Key 不能为空");
30 | }
31 |
32 | synchronized (this.getStringMap()) {
33 | this.countMap.put(key, (this.get(key) + 1));
34 | }
35 | }
36 |
37 | public void del(String key) {
38 | if (this.countMap.get(key) != null) {
39 | this.countMap.remove(key);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/model/RecordPathDirsModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | public class RecordPathDirsModel {
4 | private String rootUrl;
5 | private String reqPathDirs;
6 |
7 | // 构造函数
8 | public RecordPathDirsModel(String rootUrl, String reqPathDirs) {
9 | this.rootUrl = rootUrl;
10 | this.reqPathDirs = reqPathDirs;
11 | }
12 |
13 | public String getRootUrl() {
14 | return rootUrl;
15 | }
16 |
17 | public String getReqPathDirs() {
18 | return reqPathDirs;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/model/RecordPathModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.util.zip.CRC32;
5 |
6 | public class RecordPathModel {
7 | private String reqHash;
8 | private String rootUrl;
9 | private String reqPathDir;
10 | private int respStatusCode;
11 |
12 | public RecordPathModel(String rootUrl, String reqPathDir, int respStatusCode) {
13 | this.rootUrl = rootUrl;
14 | this.reqPathDir = reqPathDir;
15 | this.respStatusCode = respStatusCode;
16 | this.reqHash = getCalcCRC32();
17 | }
18 |
19 | public RecordPathModel(HttpUrlInfo urlInfo, int respStatusCode) {
20 | this.rootUrl = urlInfo.getRootUrlUsual();
21 | this.reqPathDir = urlInfo.getPathToDir();
22 | this.respStatusCode = respStatusCode;
23 | this.reqHash = getCalcCRC32();
24 | }
25 |
26 | private String getCalcCRC32() {
27 | return calcCRC32(String.format("%s|%s|%s", this.rootUrl, this.reqPathDir, this.respStatusCode));
28 | }
29 |
30 | /**
31 | * 计算给定字符串的CRC32校验和,并以十六进制字符串形式返回。
32 | * @param string 要计算CRC32的字符串
33 | * @return 字符串的CRC32校验和的十六进制表示
34 | */
35 | private String calcCRC32(String string) {
36 | // 使用 UTF-8 编码将字符串转换为字节数组
37 | byte[] inputBytes = string.getBytes(StandardCharsets.UTF_8);
38 | // 初始化CRC32对象
39 | CRC32 crc32 = new CRC32();
40 | // 更新CRC值
41 | crc32.update(inputBytes, 0, inputBytes.length);
42 | // 将计算后的CRC32值转换为十六进制字符串并返回
43 | return Long.toHexString(crc32.getValue()).toLowerCase();
44 | }
45 |
46 |
47 | public String getReqPathDir() {
48 | return reqPathDir;
49 | }
50 |
51 | public int getRespStatusCode() {
52 | return respStatusCode;
53 | }
54 |
55 | public String getReqHash() {
56 | return reqHash;
57 | }
58 |
59 | public String getRootUrl() {
60 | return rootUrl;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/model/ReqMsgDataModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | public class ReqMsgDataModel {
4 | private String msgHash;
5 | private String reqUrl;
6 | private byte[] reqBytes;
7 | private byte[] respBytes;
8 |
9 | public ReqMsgDataModel(String msgHash, String reqUrl, byte[] reqBytes, byte[] respBytes) {
10 | this.msgHash = msgHash;
11 | this.reqUrl = reqUrl;
12 | this.reqBytes = reqBytes;
13 | this.respBytes = respBytes;
14 | }
15 |
16 | public String getMsgHash() {
17 | return msgHash;
18 | }
19 |
20 | public String getReqUrl() {
21 | return reqUrl;
22 | }
23 |
24 | public byte[] getReqBytes() {
25 | return reqBytes;
26 | }
27 |
28 | public byte[] getRespBytes() {
29 | return respBytes;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/model/ReqUrlRespStatusModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | public class ReqUrlRespStatusModel {
4 | private Integer id;
5 | private String reqUrl;
6 | private String reqMethod;
7 | private Integer respStatusCode;
8 | private Integer respLength;
9 |
10 | // 有参构造函数
11 | public ReqUrlRespStatusModel(Integer id, String reqUrl, String reqMethod, Integer respStatusCode, Integer respLength) {
12 | this.id = id;
13 | this.reqUrl = reqUrl;
14 | this.reqMethod = reqMethod;
15 | this.respStatusCode = respStatusCode;
16 | this.respLength = respLength;
17 | }
18 |
19 | public Integer getId() {
20 | return id;
21 | }
22 |
23 | public String getReqUrl() {
24 | return reqUrl;
25 | }
26 |
27 | public String getReqMethod() {
28 | return reqMethod;
29 | }
30 |
31 | public Integer getRespStatusCode() {
32 | return respStatusCode;
33 | }
34 |
35 | public Integer getRespLength() {
36 | return respLength;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/main/java/model/RespFieldsModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import com.alibaba.fastjson2.JSON;
4 | import utils.CastUtils;
5 | import utils.RespHashUtils;
6 |
7 | import java.util.*;
8 |
9 | /**
10 | * 用于响应信息对比的数据模型
11 | */
12 | public class RespFieldsModel {
13 | private Integer statusCode; // 响应状态码,需要忽略 200 的情况
14 | private Integer respLength; // 响应头中的长度 需要忽略小于0的情况
15 | private Integer respBodyLength; // 响应内容大小
16 | private String respTextTitle; // 响应文本标题
17 | private String respHashContent; // 响应内容HASH
18 | private String respRedirectUrl; // 响应重定向URL
19 |
20 | public RespFieldsModel(HttpRespInfo respInfo) {
21 | this.statusCode = respInfo.getStatusCode();
22 | this.respLength = respInfo.getRespLength();
23 | this.respBodyLength = respInfo.getBodyLength();
24 | this.respTextTitle = respInfo.getRespTitle();
25 | this.respRedirectUrl = CastUtils.parseRespRedirectUrl(respInfo.getHeaderBytes());
26 | this.respHashContent = RespHashUtils.calcCRC32(respInfo.getBodyBytes());
27 | }
28 |
29 | public String toJSONString(){
30 | return JSON.toJSONString(getAllFieldsAsMap());
31 | }
32 |
33 | // 新增方法:获取所有属性的名称和值
34 | public Map getAllFieldsAsMap() {
35 | Map fieldMap = new HashMap<>();
36 | fieldMap.put("StatusCode", statusCode);
37 | fieldMap.put("RespLength", respLength);
38 | fieldMap.put("BodyLength", respBodyLength);
39 | fieldMap.put("RespTitle", respTextTitle);
40 | fieldMap.put("RespHash", respHashContent);
41 | fieldMap.put("RedirectUrl", respRedirectUrl);
42 | return fieldMap;
43 | }
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/model/UnVisitedUrlsModel.java:
--------------------------------------------------------------------------------
1 | package model;
2 |
3 | import utils.CastUtils;
4 |
5 | import java.util.List;
6 |
7 | public class UnVisitedUrlsModel {
8 | private int id;
9 | private String rootUrl;
10 | private List unvisitedUrls;
11 |
12 | public UnVisitedUrlsModel(int id, String rootUrl, String unvisitedUrl) {
13 | this.id = id;
14 | this.rootUrl = rootUrl;
15 | this.unvisitedUrls = CastUtils.toStringList(unvisitedUrl);
16 | }
17 |
18 | public int getId() {
19 | return id;
20 | }
21 |
22 | public String getRootUrl() {
23 | return rootUrl;
24 | }
25 |
26 | public List getUnvisitedUrls() {
27 | return unvisitedUrls;
28 | }
29 |
30 | public void setUnvisitedUrls(List unvisitedUrls) {
31 | this.unvisitedUrls = unvisitedUrls;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/ui/FingerTabRender/ButtonEditor.java:
--------------------------------------------------------------------------------
1 | package ui.FingerTabRender;
2 |
3 | import burp.BurpExtender;
4 | import model.FingerPrintRule;
5 | import ui.RuleConfigPanel;
6 | import utils.ConfigUtils;
7 | import utils.UiUtils;
8 |
9 | import javax.swing.*;
10 | import javax.swing.table.DefaultTableModel;
11 | import javax.swing.table.TableCellEditor;
12 | import java.awt.*;
13 | import java.awt.event.ActionEvent;
14 | import java.awt.event.ActionListener;
15 |
16 | public class ButtonEditor extends AbstractCellEditor implements TableCellEditor {
17 | private final JPanel buttonsPanel;
18 | private final JButton editButton;
19 | private final JButton deleteButton;
20 | private final JButton toggleButton;
21 | private final Icon EDIT_ICON = UiUtils.getImageIcon("/icon/editButton.png");
22 | private final Icon DELETE_ICON = UiUtils.getImageIcon("/icon/deleteButton.png");
23 | private final Icon openIcon = UiUtils.getImageIcon("/icon/openButtonIcon.png");
24 | private final Icon closeIcon = UiUtils.getImageIcon("/icon/shutdownButtonIcon.png");
25 |
26 | public ButtonEditor(JTable sourceTable) {
27 | toggleButton = new JButton(); //开关按钮
28 | toggleButton.setIcon(openIcon);
29 |
30 | editButton = new JButton(); //编辑按钮
31 | editButton.setIcon(EDIT_ICON);
32 |
33 | deleteButton = new JButton(); //删除按钮
34 | deleteButton.setIcon(DELETE_ICON);
35 |
36 | editButton.setPreferredSize(new Dimension(17, 17));
37 | deleteButton.setPreferredSize(new Dimension(17, 17));
38 | toggleButton.setPreferredSize(new Dimension(17, 17));
39 |
40 | toggleButton.addActionListener(new ActionListener() {
41 | @Override
42 | public void actionPerformed(ActionEvent e) {
43 | int viewRow = sourceTable.getSelectedRow(); // 获取视图中选中的行
44 | if (viewRow < 0) {
45 | return; // 如果没有选中任何行,就不执行编辑操作
46 | }
47 | int modelRow = sourceTable.convertRowIndexToModel(viewRow); // 转换为模型索引
48 | int dataIndex = RuleConfigPanel.tableToModelIndexMap.get(modelRow); // 使用模型索引查找原始数据列表中的索引
49 |
50 | RuleConfigPanel.editingRow = dataIndex; // 更新编辑行索引为原始数据列表中的索引
51 | FingerPrintRule rule = BurpExtender.fingerprintRules.get(dataIndex);
52 | if (rule.getIsOpen()) {
53 | toggleButton.setIcon(closeIcon);
54 | rule.setOpen(false);
55 | } else {
56 | toggleButton.setIcon(openIcon);
57 | rule.setOpen(true);
58 | }
59 | fireEditingStopped();
60 | sourceTable.repaint();
61 | }
62 | });
63 |
64 | // 在编辑按钮的 ActionListener 中添加以下代码来设置 matchKeyField 的值
65 | editButton.addActionListener(new ActionListener() {
66 | public void actionPerformed(ActionEvent e) {
67 | int viewRow = sourceTable.getSelectedRow(); // 获取视图中选中的行
68 | if (viewRow < 0) {
69 | return; // 如果没有选中任何行,就不执行编辑操作
70 | }
71 | int modelRow = sourceTable.convertRowIndexToModel(viewRow); // 转换为模型索引
72 |
73 | //加载规则编辑面板
74 | RuleConfigPanel.showRuleEditorPanel(modelRow);
75 |
76 | /*
77 | //跟随标签显示 优化版本
78 | Point btnLocation = ((JButton) e.getSource()).getLocationOnScreen();
79 | // 计算面板的左上角新位置
80 | int newX = btnLocation.x - fingerConfigTab.editRulePanel.getWidth() - 20; //水平方向,从左向右增加。
81 | int newY = btnLocation.y + ((JButton) e.getSource()).getHeight(); //垂直方向,从上向下增加。
82 | // 获取容器的大小
83 | Dimension containerSize = sourceTable.getSize();
84 | // 获取面板的大小
85 | Dimension panelSize = fingerConfigTab.editRulePanel.getPreferredSize();
86 | // 检查面板是否会超出容器的底部边界
87 | if (newY + panelSize.height > containerSize.height){
88 | // 如果会超出底部边界,则将面板移到按钮上方
89 | newY = btnLocation.y - panelSize.height - 50;
90 | }
91 | */
92 |
93 | fireEditingStopped(); // 停止表格的编辑状态
94 | }
95 | });
96 |
97 | deleteButton.addActionListener(new ActionListener() {
98 | public void actionPerformed(ActionEvent e) {
99 | fireEditingStopped(); // 确保停止编辑状态
100 | int viewRow = sourceTable.getSelectedRow(); // 获取视图中选中的行
101 | if (viewRow < 0) {
102 | return; // 如果没有选中任何行,就不执行删除操作
103 | }
104 | int modelRow = sourceTable.convertRowIndexToModel(viewRow); // 转换为模型索引
105 | int dataIndex = RuleConfigPanel.tableToModelIndexMap.get(modelRow); // 获取实际数据索引
106 |
107 | // 删除数据源中的数据
108 | BurpExtender.fingerprintRules.remove(dataIndex);
109 |
110 | // 更新映射
111 | RuleConfigPanel.tableToModelIndexMap.remove(modelRow);
112 |
113 | // 由于删除了一个元素,需要更新所有后续元素的索引
114 | for (int i = modelRow; i < RuleConfigPanel.tableToModelIndexMap.size(); i++) {
115 | RuleConfigPanel.tableToModelIndexMap.set(i, RuleConfigPanel.tableToModelIndexMap.get(i) - 1);
116 | }
117 |
118 | // 删除表格模型中的数据
119 | ((DefaultTableModel) sourceTable.getModel()).removeRow(viewRow);
120 |
121 | // 在删除行之后,重新验证和重绘表格
122 | sourceTable.revalidate();
123 | sourceTable.repaint();
124 |
125 | //重新加载系统CONF_配置
126 | ConfigUtils.reloadConfigArrayListFromRules(BurpExtender.fingerprintRules);
127 | }
128 | });
129 |
130 | //把三个按钮放在一个小面板中
131 | buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
132 | buttonsPanel.add(toggleButton);
133 | buttonsPanel.add(editButton);
134 | buttonsPanel.add(deleteButton);
135 | buttonsPanel.setBorder(BorderFactory.createEmptyBorder());
136 | }
137 |
138 | @Override
139 | public Object getCellEditorValue() {
140 | return null;
141 | }
142 |
143 | @Override
144 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
145 | return buttonsPanel;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/ui/FingerTabRender/ButtonRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.FingerTabRender;
2 |
3 | import burp.BurpExtender;
4 | import model.FingerPrintRule;
5 | import ui.RuleConfigPanel;
6 | import utils.UiUtils;
7 |
8 | import javax.swing.*;
9 | import javax.swing.table.TableCellRenderer;
10 | import java.awt.*;
11 |
12 |
13 | public class ButtonRenderer extends JPanel implements TableCellRenderer {
14 | private static final Icon EDIT_ICON = UiUtils.getImageIcon("/icon/editButton.png");
15 | private static final Icon DELETE_ICON = UiUtils.getImageIcon("/icon/deleteButton.png");
16 | private static final Icon OPEN_ICON = UiUtils.getImageIcon("/icon/openButtonIcon.png");
17 | private static final Icon CLOSE_ICON = UiUtils.getImageIcon("/icon/shutdownButtonIcon.png");
18 | private final JButton editButton;
19 | private final JButton deleteButton;
20 | private final JButton toggleButton;
21 |
22 | public ButtonRenderer() {
23 | setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
24 | editButton = createButton(EDIT_ICON);
25 | deleteButton = createButton(DELETE_ICON);
26 | toggleButton = createButton(OPEN_ICON);
27 | add(toggleButton);
28 | add(editButton);
29 | add(deleteButton);
30 | setOpaque(true); // 设置为不透明,这样背景颜色变更才会生效
31 | }
32 |
33 | private JButton createButton(Icon icon) {
34 | JButton button = new JButton(icon);
35 | button.setPreferredSize(new Dimension(17, 17));
36 | button.setMargin(new Insets(0, 0, 0, 0)); // 设置按钮边距为0
37 | // 设置按钮边界为透明,以免在不同的LookAndFeel下显示不一致
38 | button.setBorder(BorderFactory.createEmptyBorder());
39 | button.setContentAreaFilled(false);
40 | return button;
41 | }
42 |
43 | @Override
44 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
45 | // 注意:这里使用传入的 `row` 参数,而不是 `table.getSelectedRow()`
46 | int modelRow = table.convertRowIndexToModel(row); // 转换为模型索引
47 | int dataIndex = RuleConfigPanel.tableToModelIndexMap.get(modelRow); // 使用模型索引查找原始数据列表中的索引
48 |
49 | FingerPrintRule rule = BurpExtender.fingerprintRules.get(dataIndex);
50 | if (rule.getIsOpen()) {
51 | toggleButton.setIcon(OPEN_ICON); // 如果规则是打开状态,设置为打开图标
52 | } else {
53 | toggleButton.setIcon(CLOSE_ICON); // 如果规则是关闭状态,设置为关闭图标
54 | }
55 |
56 | // 设置背景色,根据是否选中来决定
57 | if (isSelected) {
58 | setBackground(table.getSelectionBackground());
59 | } else {
60 | setBackground(table.getBackground());
61 | }
62 | // 重要:这里要返回包含正确图标的 toggleButton
63 | return this;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/ui/FingerTabRender/CenterRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.FingerTabRender;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 |
6 | public class CenterRenderer extends DefaultTableCellRenderer {
7 | public CenterRenderer() {
8 | setHorizontalAlignment(JLabel.CENTER);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/ui/FingerTabRender/HeaderIconTypeRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.FingerTabRender;
2 |
3 |
4 | import utils.UiUtils;
5 |
6 | import javax.swing.*;
7 | import javax.swing.table.DefaultTableCellRenderer;
8 | import java.awt.*;
9 |
10 | public class HeaderIconTypeRenderer extends DefaultTableCellRenderer {
11 |
12 | // 预加载图标
13 | private static final Icon FILTER_ICON = UiUtils.getImageIcon("/icon/filterIcon.png");
14 |
15 | public HeaderIconTypeRenderer() {
16 | super();
17 | setHorizontalAlignment(JLabel.CENTER); //仅需设置一次 设置水平对齐方式
18 | setHorizontalTextPosition(JLabel.LEFT); //设置文本相对于图标的水平位置
19 | setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); //更改鼠标光标形状 设置为手形
20 | }
21 |
22 | @Override
23 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
24 | // 调用super方法来保留原始行为
25 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
26 |
27 | // 根据列设置图标
28 | if (column == 1) {
29 | setIcon(FILTER_ICON);
30 | } else {
31 | setIcon(null);
32 | setHorizontalAlignment(JLabel.LEADING); // 文本对齐方式恢复默认
33 | }
34 |
35 | // Since we're modifying the renderer itself, return 'this'
36 | return this;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/ui/FingerTabRender/LeftRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.FingerTabRender;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 |
6 | public class LeftRenderer extends DefaultTableCellRenderer {
7 | public LeftRenderer() {
8 | setHorizontalAlignment(JLabel.LEFT);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/ui/MainTabRender/ColorInfoCellRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.MainTabRender;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 | import java.awt.*;
6 |
7 | // 自定义渲染器类
8 | public class ColorInfoCellRenderer extends DefaultTableCellRenderer {
9 |
10 | private static final long serialVersionUID = 1L;
11 |
12 | public ColorInfoCellRenderer() {
13 | setHorizontalAlignment(CENTER); // 设置居中
14 | }
15 |
16 | @Override
17 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
18 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
19 |
20 | Integer infoNum = (Integer) value;
21 |
22 | if (infoNum != null) {
23 | if (infoNum > 0) {
24 | setBackground(Color.GREEN); // 状态为 true 时的背景颜色
25 | } else {
26 | setBackground(Color.RED); // 状态为 false 时的背景颜色
27 | }
28 | } else {
29 | setBackground(table.getBackground()); // 如果值为空或不是布尔值,则使用默认背景色
30 | }
31 |
32 | return this;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/main/java/ui/MainTabRender/HasImportantCellRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.MainTabRender;
2 |
3 | import utils.UiUtils;
4 |
5 | import javax.swing.*;
6 | import javax.swing.table.DefaultTableCellRenderer;
7 | import java.awt.*;
8 |
9 | public class HasImportantCellRenderer extends DefaultTableCellRenderer {
10 |
11 | // 预加载并缓存图标
12 | private final Icon importantIcon = UiUtils.getImageIcon("/icon/importantButtonIcon.png", 15, 15);
13 | //private final Icon notImportantIcon = UiUtils.getImageIcon("/icon/normalIcon.png", 15, 15);
14 | public HasImportantCellRenderer() {
15 | setHorizontalAlignment(CENTER); // 设置居中
16 | }
17 |
18 | @Override
19 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
20 | // 调用父类以保留默认行为
21 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
22 |
23 |
24 | // 根据单元格值设置相应图标
25 | if (value instanceof Boolean) {
26 | if ((Boolean) value){
27 | setIcon(importantIcon);
28 | // 设置文本为空,因为我们只显示图标
29 | setText("");
30 | }else{
31 | setIcon(null);
32 | setText("NO");
33 | }
34 | } else {
35 | setIcon(null);
36 | setText((String)value); // 如果值不是布尔类型,则不显示图标
37 | }
38 |
39 | return this;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/ui/MainTabRender/RunStatusCellRenderer.java:
--------------------------------------------------------------------------------
1 | package ui.MainTabRender;
2 |
3 | import database.Constants;
4 | import utils.UiUtils;
5 |
6 | import javax.swing.*;
7 | import javax.swing.table.DefaultTableCellRenderer;
8 | import java.awt.*;
9 |
10 | public class RunStatusCellRenderer extends DefaultTableCellRenderer {
11 | // 预加载并缓存图标
12 | private final Icon pendingIcon = UiUtils.getImageIcon("/icon/convenientOperationIcon.png", 15, 15);
13 | private final Icon handlingIcon = UiUtils.getImageIcon("/icon/searchButton.png", 15, 15);
14 | private final Icon handledIcon = UiUtils.getImageIcon("/icon/findUrlFromJS.png", 15, 15);
15 |
16 | public RunStatusCellRenderer() {
17 | setHorizontalAlignment(CENTER); // 设置居中
18 | }
19 |
20 | @Override
21 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
22 | // 调用父类以保留默认行为
23 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
24 |
25 | // 根据单元格值设置相应图标
26 | if (value instanceof String) {
27 | String stringValue = (String) value;
28 | if (Constants.HANDLE_WAIT.equals(stringValue)|| Constants.ANALYSE_END.equals(stringValue)) {
29 | setIcon(pendingIcon);
30 | setText(""); // 设置文本为空,因为我们只显示图标
31 | } else if (Constants.HANDLE_ING.equals(stringValue)) {
32 | setIcon(handlingIcon);
33 | setText(""); // 设置文本为空,因为我们只显示图标
34 | } else if (Constants.HANDLE_END.equals(stringValue)) {
35 | setIcon(handledIcon);
36 | setText(""); // 设置文本为空,因为我们只显示图标
37 | } else {
38 | setIcon(null);
39 | setText(stringValue); // 显示字符串值
40 | }
41 | } else {
42 | // 其他类型的值保持不变
43 | setIcon(null);
44 | setText((String)value);
45 | }
46 |
47 | return this;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/ui/MainTabRender/TableHeaderWithTips.java:
--------------------------------------------------------------------------------
1 | package ui.MainTabRender;
2 |
3 | import javax.swing.table.JTableHeader;
4 | import javax.swing.table.TableColumnModel;
5 | import java.awt.event.MouseEvent;
6 |
7 | public class TableHeaderWithTips extends JTableHeader {
8 |
9 | private final String[] tooltips;
10 |
11 | public TableHeaderWithTips(TableColumnModel columnModel, String[] tooltips) {
12 | super(columnModel); // do everything a normal JTableHeader does
13 | this.tooltips = tooltips; // plus extra data
14 | }
15 |
16 | @Override
17 | public String getToolTipText(MouseEvent e) {
18 | int index = columnModel.getColumnIndexAtX(e.getPoint().x);
19 | int realIndex = columnModel.getColumn(index).getModelIndex();
20 |
21 | // 检查索引是否在 tooltips 数组的有效范围内
22 | if (realIndex >= 0 && realIndex < tooltips.length) {
23 | return tooltips[realIndex];
24 | } else {
25 | return null;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/ui/Tabs.java:
--------------------------------------------------------------------------------
1 | package ui;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.ITab;
5 |
6 | import javax.swing.*;
7 | import java.awt.*;
8 |
9 |
10 | public class Tabs implements ITab {
11 | private final JTabbedPane tabs;
12 | private final String name;
13 | private final RuleConfigPanel ruleConfigPanel;
14 | private final BasicUrlInfoPanel msgInfoPanel;
15 | private final BasicHostInfoPanel hostInfoPanel;
16 |
17 | public Tabs(IBurpExtenderCallbacks callbacks, String name){
18 | this.name = name;
19 |
20 | // 定义tab标签页
21 | this.tabs = new JTabbedPane();
22 |
23 | this.hostInfoPanel = BasicHostInfoPanel.getInstance();
24 | this.tabs.add("聚合面板", this.hostInfoPanel);
25 |
26 | this.msgInfoPanel = BasicUrlInfoPanel.getInstance();
27 | this.tabs.add("请求详情", this.msgInfoPanel);
28 |
29 | this.ruleConfigPanel = RuleConfigPanel.getInstance();
30 | this.tabs.add("规则配置", this.ruleConfigPanel);
31 |
32 | // 将整个tab加载到平台即可
33 | callbacks.customizeUiComponent(tabs);
34 | // 将自定义选项卡添加到Burp的UI
35 | callbacks.addSuiteTab(this);
36 | }
37 |
38 |
39 | @Override
40 | public String getTabCaption() {
41 | return this.name;
42 | }
43 |
44 | @Override
45 | public Component getUiComponent() {
46 | return this.tabs;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/java/utilbox/ByteArrayUtils.java:
--------------------------------------------------------------------------------
1 | package utilbox;
2 |
3 | import java.nio.charset.Charset;
4 |
5 | public class ByteArrayUtils {
6 |
7 |
8 | /**
9 | * byte[]数组截取
10 | * srcPoC 是原数组的起始位置,length是要截取的长度
11 | */
12 | public static byte[] subByte(byte[] b, int srcPos, int length) {
13 | byte[] b1 = new byte[length];
14 | System.arraycopy(b, srcPos, b1, 0, length);
15 | return b1;
16 | }
17 |
18 |
19 | public static String getSystemCharSet() {
20 | return Charset.defaultCharset().toString();
21 |
22 | //System.out.println(System.getProperty("file.encoding"));
23 | }
24 |
25 |
26 | /**
27 | * 将10进制转换为16进制
28 | *
29 | * @param decimal 10进制
30 | * @return 16进制
31 | */
32 | public static String decimalToHex(int decimal) {
33 | String hex = Integer.toHexString(decimal);
34 | return hex.toUpperCase();
35 | }
36 |
37 |
38 | /**
39 | * 拼接多个byte[]数组的方法
40 | *
41 | * @param arrays
42 | * @return
43 | */
44 | public static byte[] join(byte[]... arrays) {
45 | int len = 0;
46 | for (byte[] arr : arrays) {
47 | len += arr.length;//计算多个数组的长度总和
48 | }
49 |
50 | byte[] result = new byte[len];
51 | int idx = 0;
52 |
53 | for (byte[] arr : arrays) {
54 | for (byte b : arr) {
55 | result[idx++] = b;
56 | }
57 | }
58 |
59 | return result;
60 | }
61 |
62 |
63 | /**
64 | * https://stackoverflow.com/questions/21341027/find-indexof-a-byte-array-within-another-byte-array
65 | * Search the data byte array for the first occurrence
66 | * of the byte array pattern.
67 | */
68 | public static int BytesIndexOf(byte[] data, byte[] pattern) {
69 | int[] failure = computeFailure(pattern);
70 |
71 | int j = 0;
72 |
73 | for (int i = 0; i < data.length; i++) {
74 | while (j > 0 && pattern[j] != data[i]) {
75 | j = failure[j - 1];
76 | }
77 | if (pattern[j] == data[i]) {
78 | j++;
79 | }
80 | if (j == pattern.length) {
81 | return i - pattern.length + 1;
82 | }
83 | }
84 | return -1;
85 | }
86 |
87 | /**
88 | * Computes the failure function using a boot-strapping process,
89 | * where the pattern is matched against itself.
90 | */
91 | private static int[] computeFailure(byte[] pattern) {
92 | int[] failure = new int[pattern.length];
93 |
94 | int j = 0;
95 | for (int i = 1; i < pattern.length; i++) {
96 | while (j > 0 && pattern[j] != pattern[i]) {
97 | j = failure[j - 1];
98 | }
99 | if (pattern[j] == pattern[i]) {
100 | j++;
101 | }
102 | failure[i] = j;
103 | }
104 |
105 | return failure;
106 | }
107 |
108 | public static boolean equals(byte[] a, byte[] b) {
109 | if (a == null || b == null) {
110 | return false;
111 | }
112 |
113 | if (a.length != b.length) {
114 | return false;
115 | }
116 |
117 | for (int i = 0; i < a.length; i++) {
118 | if (a[i] != b[i]) {
119 | return false;
120 | }
121 | }
122 | return true;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/utilbox/CharsetUtils.java:
--------------------------------------------------------------------------------
1 | package utilbox;
2 |
3 | import org.apache.commons.io.input.BOMInputStream;
4 |
5 | import java.io.ByteArrayInputStream;
6 | import java.io.IOException;
7 | import java.io.UnsupportedEncodingException;
8 | import java.nio.charset.Charset;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public class CharsetUtils {
14 |
15 | public static String getSystemCharSet() {
16 | return Charset.defaultCharset().toString();
17 | }
18 |
19 | public static boolean isValidCharset(String charsetName) {
20 | return getCharsetNameList().contains(charsetName);
21 | }
22 |
23 | public static List getCharsetNameList() {
24 | Map charsets = Charset.availableCharsets();
25 | return new ArrayList(charsets.keySet());
26 | }
27 |
28 | /**
29 | * 消除大小写差异
30 | * @param charsetName
31 | * @return
32 | */
33 | public static String getCharsetName(String charsetName) {
34 | List charsetNameList = getCharsetNameList(); // 调用一次并保存结果
35 | for (String name : charsetNameList) {
36 | if (name.equalsIgnoreCase(charsetName)) {
37 | return name;
38 | }
39 | }
40 | return null;
41 | }
42 |
43 | /**
44 | * 进行响应包的编码转换。
45 | * @param content
46 | * @return 转换后的格式的byte[]
47 | */
48 | public static byte[] covertCharSet(byte[] content,String originalCharset,String newCharset){
49 | if (originalCharset == null) {
50 | originalCharset = detectCharset(content);
51 | }
52 | try {
53 | return new String(content,originalCharset).getBytes(newCharset);
54 | } catch (UnsupportedEncodingException e) {
55 | e.printStackTrace();
56 | return content;
57 | }
58 | }
59 |
60 |
61 | public static byte[] covertCharSet(byte[] content,String newCharset) throws UnsupportedEncodingException {
62 | return covertCharSet(content,null,newCharset);
63 | }
64 |
65 |
66 | public static String detectCharset(byte[] bytes){
67 | try {
68 | ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
69 | BOMInputStream bomInputStream = BOMInputStream.builder().setInputStream(bis).get();
70 | String encoding = bomInputStream.getBOMCharsetName();
71 | bomInputStream.close();
72 | return getCharsetName(encoding);
73 | } catch (IOException e) {
74 | e.printStackTrace();
75 | return null;
76 | }
77 | }
78 |
79 |
80 | public static void main(String[] args) throws Exception {
81 | // Map charsets = Charset.availableCharsets();
82 | //
83 | // // 打印所有字符编码集的规范名称
84 | // System.out.println("Available Charsets:");
85 | // for (String name : charsets.keySet()) {
86 | // System.out.println(name);
87 | // }
88 |
89 | System.out.println(detectCharset("中国中文11111".getBytes("UTF-8")));
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/utils/AnalyseUriFilter.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import model.HttpUrlInfo;
4 |
5 | import java.net.MalformedURLException;
6 | import java.net.URL;
7 | import java.util.*;
8 | import java.util.regex.Pattern;
9 |
10 | import static utils.BurpPrintUtils.*;
11 | import static utils.CastUtils.isEmptyObj;
12 | import static utils.ElementUtils.isEqualsOneKey;
13 |
14 | public class AnalyseUriFilter {
15 | private static final Pattern CHINESE_PATTERN = Pattern.compile("[\u4E00-\u9FA5]");
16 |
17 | /**
18 | * 过滤无用的提取路径 通过判断和指定的路径相等
19 | * @param matchList
20 | * @return
21 | */
22 | public static List filterPathByEqualUselessPath(List matchList, List blackPathEquals) {
23 | List newList = new ArrayList<>();
24 | for (String path : matchList){
25 | if(!isEqualsOneKey(path, blackPathEquals, false)){
26 | newList.add(path);
27 | }
28 | }
29 | return newList;
30 | }
31 |
32 | /**
33 | * 过滤无用的提取路径 通过判断是否包含无用关键字
34 | * @param matchList
35 | * @return
36 | */
37 | public static List filterPathByContainUselessKey(List matchList, List blackPathKeys) {
38 | if (isEmptyObj(matchList)) return matchList;
39 |
40 | List newList = new ArrayList<>();
41 | for (String path : matchList){
42 | if(!ElementUtils.isContainOneKey(path, blackPathKeys, false)){
43 | newList.add(path);
44 | }
45 | }
46 | return newList;
47 | }
48 |
49 | /**
50 | * 过滤无用的提取路径 通过判断是否包含中文路径
51 | * @param matchList
52 | * @return
53 | */
54 | public static List filterPathByContainChinese(List matchList) {
55 | if (isEmptyObj(matchList)) return matchList;
56 |
57 | List newList = new ArrayList<>();
58 | for (String s : matchList){
59 | if(!CHINESE_PATTERN.matcher(s).find()){
60 | newList.add(s);
61 | }
62 | }
63 | return newList;
64 | }
65 |
66 | /**
67 | * 过滤黑名单HOST域名
68 | * @param urls
69 | * @param blackHosts
70 | * @return
71 | */
72 | public static List filterBlackHosts(List urls, List blackHosts) {
73 | if (isEmptyObj(blackHosts) || isEmptyObj(urls)) return urls;
74 |
75 | List list = new ArrayList<>();
76 | for (String url : urls) {
77 | HttpUrlInfo urlInfo = new HttpUrlInfo(url);
78 | if (!ElementUtils.isContainOneKey(urlInfo.getRootUrlUsual(), blackHosts, false)) {
79 | list.add(url);
80 | }
81 | }
82 | return list;
83 | }
84 |
85 | /**
86 | * 过滤黑名单后缀名 图片后缀之类的不需要提取请求信息
87 | * @param uris
88 | * @param blackSuffixes
89 | * @return
90 | */
91 | public static List filterBlackSuffixes(List uris, List blackSuffixes) {
92 | if (isEmptyObj(blackSuffixes)||isEmptyObj(uris)) return uris;
93 |
94 | List list = new ArrayList<>();
95 | for (String urlStr : uris) {
96 | String suffix = parseUrlExt(urlStr);
97 | if (!isEqualsOneKey(suffix, blackSuffixes, false))
98 | list.add(urlStr);
99 | }
100 | return list;
101 | }
102 |
103 | /**
104 | * 过滤黑名单路径 /jquery.js 之类的不需要提取信息
105 | * @param urls
106 | * @param blackPaths
107 | * @return
108 | */
109 | public static List filterBlackPaths(List urls, List blackPaths) {
110 | if (isEmptyObj(urls)) return urls;
111 |
112 | List list = new ArrayList<>();
113 | for (String urlStr : urls) {
114 | try {
115 | URL url = new URL(urlStr);
116 | String path = url.getPath();
117 | if (!ElementUtils.isContainOneKey(path, blackPaths, false)) {
118 | list.add(urlStr);
119 | }else {
120 | stdout_println(LOG_DEBUG, String.format("[*] Black Paths Filter %s", urlStr));
121 | }
122 | } catch (MalformedURLException e) {
123 | stderr_println(LOG_DEBUG, String.format("[!] new URL(%s) -> Error: %s", urlStr, e.getMessage()));
124 | }
125 | }
126 | return list;
127 | }
128 |
129 | /**
130 | * 过滤提取的值 在请求字符串内的项
131 | * @param baseUri
132 | * @param matchUriList
133 | * @return
134 | */
135 | public static List filterUriBySelfContain(String baseUri, List matchUriList) {
136 | if (isEmptyObj(baseUri) || baseUri == "/" ) return matchUriList;
137 | if (isEmptyObj(matchUriList)) return matchUriList;
138 |
139 | List list = new ArrayList<>();
140 | for (String uri : matchUriList){
141 | if (!baseUri.contains(uri)) {
142 | // system_println(String.format("%s 不包含 %s", baseUri, uri));
143 | list.add(uri);}
144 | }
145 | return list;
146 | }
147 |
148 | /**
149 | * 过滤提取出的URL列表 仅保留自身域名的
150 | * @param basicHost
151 | * @param matchUrlList
152 | * @return
153 | */
154 | public static List filterUrlByMainHost(String basicHost, List matchUrlList){
155 | if (isEmptyObj(basicHost) || isEmptyObj(matchUrlList)) return matchUrlList;
156 |
157 | List newUrlList = new ArrayList<>();
158 | for (String matchUrl : matchUrlList){
159 | //对比提取出来的URL和请求URL的域名部分是否相同,不相同的一般不是
160 | try {
161 | String newHost = (new URL(matchUrl)).getHost();
162 | if (!newHost.contains(basicHost))
163 | continue;
164 | } catch (Exception e) {
165 | stderr_println(LOG_DEBUG, String.format("[!] new URL(%s) -> Error: %s", matchUrl, e.getMessage()));
166 | continue;
167 | }
168 | newUrlList.add(matchUrl);
169 | }
170 | return newUrlList;
171 | }
172 |
173 | /**
174 | * 粗略获取一个URI的后缀 支持PATH 忽略 # 号
175 | * @param uri
176 | * @return
177 | */
178 | private static String parseUrlExt(String uri) {
179 | String pureUrl = uri.substring(0, uri.contains("?") ? uri.indexOf("?") : uri.length());
180 | return (pureUrl.lastIndexOf(".") > -1 ? pureUrl.substring(pureUrl.lastIndexOf(".") + 1) : "").toLowerCase();
181 | }
182 |
183 |
184 | /**
185 | * 格式化所有URL 以HttpUrlInfo 内部为基准 统一带端口或不带默认端口
186 | * @param urls
187 | * @return
188 | */
189 | public static List formatUrls(List urls) {
190 | if (isEmptyObj(urls)) return urls;
191 |
192 | List list = new ArrayList<>();
193 | for (String urlStr : urls) {
194 | String url = new HttpUrlInfo(urlStr).getRawUrlUsual();
195 | list.add(url);
196 | }
197 | return list;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/main/java/utils/BurpFileUtils.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import burp.BurpExtender;
4 | import burp.IBurpExtenderCallbacks;
5 | import com.alibaba.fastjson2.JSON;
6 | import com.alibaba.fastjson2.TypeReference;
7 |
8 | import java.io.*;
9 | import java.nio.charset.Charset;
10 | import java.nio.charset.StandardCharsets;
11 | import java.nio.file.Files;
12 | import java.nio.file.Path;
13 | import java.nio.file.Paths;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | import static utils.BurpPrintUtils.*;
18 | import static utils.CastUtils.isNotEmptyObj;
19 |
20 | public class BurpFileUtils {
21 | /**
22 | * 检查指定路径的文件是否存在
23 | * @param filePath 文件的路径
24 | * @return 如果文件存在返回true,否则返回false
25 | */
26 | public static boolean isFileExists(String filePath) {
27 | Path path = Paths.get(filePath);
28 | return Files.exists(path);
29 | }
30 |
31 | /**
32 | * 拼接目录和文件名
33 | * @param directory 目录路径
34 | * @param fileName 文件名
35 | * @return 拼接后的完整路径
36 | */
37 | public static String concatPath(String directory, String fileName) {
38 | Path path = Paths.get(directory, fileName);
39 | return path.toString();
40 | }
41 |
42 | /**
43 | * 读取文本文件内容并返回一个字符串
44 | * @param filePath 文本文件的路径
45 | * @return 文件内容字符串,如果发生错误则返回null
46 | */
47 | public static String readFileToString(String filePath, Charset charsetName) {
48 | StringBuilder content = new StringBuilder();
49 | try (BufferedReader reader = new BufferedReader(
50 | new InputStreamReader(new FileInputStream(filePath), charsetName))) {
51 | String line;
52 | while ((line = reader.readLine()) != null) {
53 | content.append(line).append("\n");
54 | }
55 | } catch (IOException e) {
56 | stderr_println(e.getMessage());
57 | e.printStackTrace();
58 | return null;
59 | }
60 | return content.toString();
61 | }
62 |
63 | public static String readFileToString(String filePath) {
64 | return readFileToString(filePath, StandardCharsets.UTF_8);
65 | }
66 |
67 | /**
68 | * 从jar包中读取资源文件内容到字符串
69 | * @param resourceName 资源的路径(例如:"com/example/myfile.txt")
70 | * @param charset
71 | * @return 文件内容字符串,如果发生错误则返回null
72 | */
73 | public static String readResourceToString(String resourceName, Charset charset) {
74 | StringBuilder content = new StringBuilder();
75 | try (InputStream inputStream = BurpFileUtils.class.getClassLoader().getResourceAsStream(resourceName);
76 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))) {
77 |
78 | if (inputStream == null) {
79 | stderr_println("无法找到资源: " + resourceName);
80 | return null;
81 | }
82 |
83 | String line;
84 | while ((line = reader.readLine()) != null) {
85 | content.append(line).append("\n");
86 | }
87 | } catch (IOException e) {
88 | stderr_println(e.getMessage());
89 | e.printStackTrace();
90 | return null;
91 | }
92 | return content.toString();
93 | }
94 |
95 | /**
96 | * 获取-插件运行路径
97 | *
98 | * @return
99 | */
100 | public static String getPluginPath(IBurpExtenderCallbacks callbacks) {
101 | String path = "";
102 | Integer lastIndex = callbacks.getExtensionFilename().lastIndexOf(File.separator);
103 | path = callbacks.getExtensionFilename().substring(0, lastIndex) + File.separator;
104 | return path;
105 | }
106 |
107 | /**
108 | * 从 jar文件所在路径或jar文件内部读取配置文件
109 | * @param callbacks
110 | * @param configName
111 | * @param charset
112 | * @return
113 | */
114 | public static String ReadPluginConfFile(IBurpExtenderCallbacks callbacks, String configName, Charset charset) {
115 | String configJson;
116 |
117 | String extensionPath = getPluginPath(callbacks);
118 | String configPath = concatPath(extensionPath, configName);
119 |
120 | if(isFileExists(configPath)){
121 | stdout_println(LOG_INFO, String.format("[+] Custom Config File Path: %s", configPath));
122 | configJson = readFileToString(configPath, charset);
123 | }else {
124 | configName = String.format("conf/%s", configName);
125 | stdout_println(LOG_INFO, String.format("[+] User Jar File Inner Config: %s -> %s", extensionPath, configName));
126 | configJson = readResourceToString(configName, charset);
127 | }
128 | return configJson;
129 | }
130 |
131 | /**
132 | * 获取插件同级目录下的指定文件
133 | * @param callbacks
134 | * @param fileName
135 | * @return
136 | */
137 | public static Path getPluginDirFilePath(IBurpExtenderCallbacks callbacks, String fileName) {
138 | Path path = Paths.get(getPluginPath(callbacks), fileName);
139 | return path.toAbsolutePath();
140 | }
141 |
142 | /**
143 | * 获取插件同级目录下的指定文件
144 | * @param fileName
145 | * @return
146 | */
147 | public static String getPluginDirFilePath(String fileName) {
148 | Path path = Paths.get(getPluginPath(BurpExtender.getCallbacks()), fileName);
149 | return path.toString();
150 | }
151 |
152 | /**
153 | * 简单的保存字符串到文件,不处理报错信息
154 | * @param file
155 | * @param content
156 | * @throws IOException
157 | */
158 | public static void writeToFile(File file, String content) throws IOException {
159 | // 使用UTF-8编码写入文件
160 | OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
161 | writer.write(content);
162 | writer.close();
163 | }
164 |
165 | public static void writeToPluginPathFile(String configName, String content) throws IOException {
166 | // 使用UTF-8编码写入文件
167 | String pluginDirFilePath = getPluginDirFilePath(configName);
168 | File fileToSave = new File(pluginDirFilePath);
169 | writeToFile(fileToSave, content);
170 | }
171 |
172 | public static void writeToPluginPathFileNotEx(String configName, String content) {
173 | try { writeToPluginPathFile(configName, content); } catch (IOException e) { e.printStackTrace(); }
174 | }
175 |
176 | /**
177 | * 从本地缓存文件读取过滤器
178 | */
179 | public static Map> LoadJsonFromFile(String configPath) {
180 | configPath = getPluginDirFilePath(configPath);
181 | if (isFileExists(configPath)){
182 | String configJson = readFileToString(configPath);
183 | if (isNotEmptyObj(configJson)){
184 | TypeReference