Compare commits

...

156 Commits
2.0.6 ... 4.0

Author SHA1 Message Date
gh0stkey
daacb2e146 Version: 4.0 Update 2024-12-21 15:34:45 +08:00
gh0stkey
1f1ca99f10 Update 2024-12-21 15:19:19 +08:00
gh0stkey
fa35b0a625 Version: 3.4 Update 2024-11-16 19:48:50 +08:00
gh0stkey
8ef98d20a9 Version: 3.4 Update 2024-11-16 18:42:15 +08:00
gh0stkey
e556abb6f7 Version: 3.4 Update 2024-11-16 18:06:49 +08:00
EvilChen
471aab5ea1 Merge pull request #221 from AabyssZG/master
Update Rules.yml
2024-10-24 11:48:57 +08:00
曾哥
76b475bd91 Update Rules.yml 2024-10-24 11:40:29 +08:00
gh0stkey
6014089594 Version: 3.3.4 Update 2024-10-14 16:35:20 +08:00
EvilChen
910658f2e0 Update 问题反馈.md 2024-09-23 00:05:40 +08:00
gh0stkey
8692b0a494 Version: 3.3.3 Update 2024-09-19 17:45:47 +08:00
gh0stkey
5419d4a679 Version: 3.3.3 Update 2024-09-19 17:11:55 +08:00
gh0stkey
ae8cb2fd25 Version: 3.3.3 Update 2024-09-19 17:08:46 +08:00
EvilChen
5b6bdbe5b6 Update README.md 2024-08-28 16:19:24 +08:00
EvilChen
ddb08e9a6e Update README.md 2024-08-28 16:18:25 +08:00
EvilChen
6a2f289d57 Update build.gradle 2024-08-26 10:04:57 +08:00
gh0stkey
84746a7089 Version: 3.3.2 Update 2024-08-23 22:03:31 +08:00
gh0stkey
68f0bce619 Version: 3.3.1 Update 2024-08-12 10:41:24 +08:00
gh0stkey
4f0401347c Version: 3.3.1 Update 2024-08-12 10:34:26 +08:00
gh0stkey
a7e0a2a6ce Update 2024-07-31 08:57:17 +08:00
gh0stkey
b7c5a8363d Update 2024-07-31 08:49:53 +08:00
gh0stkey
d7b4419d51 Version: 3.3 Update 2024-07-23 09:22:43 +08:00
gh0stkey
5f54d1f461 Version: 3.3 Update 2024-07-23 09:21:30 +08:00
EvilChen
e4b7f86a0c Update README.md 2024-07-19 00:24:04 +08:00
EvilChen
cc30f41bfa Add files via upload 2024-07-18 23:38:29 +08:00
EvilChen
386c562311 Delete images/config.png 2024-07-18 23:37:12 +08:00
EvilChen
a867039284 Update README.md 2024-07-18 23:36:52 +08:00
gh0stkey
3a8d9eae11 Version: 3.2.2 Update 2024-06-19 22:20:46 +08:00
gh0stkey
e5f55b6c4c Version: 3.2.2 Update 2024-06-19 22:16:57 +08:00
gh0stkey
54973d9f4f Version: 3.2.1 Update 2024-05-30 16:01:25 +08:00
gh0stkey
fb347a8dc6 Version: 3.2.1 Update 2024-05-30 15:56:49 +08:00
gh0stkey
04b6652b03 Version: 3.2.1 Update 2024-05-30 14:42:26 +08:00
gh0stkey
6d4abae898 Version: 3.2.1 Update 2024-05-30 14:37:01 +08:00
gh0stkey
97172fab45 Update 2024-05-26 15:09:12 +08:00
EvilChen
ba3b206acf Update build.gradle 2024-05-24 17:13:31 +08:00
gh0stkey
99ed2cb2fd Version: 3.2 Update 2024-05-24 15:31:07 +08:00
gh0stkey
8a47f61caa Version: 3.2 Update 2024-05-24 15:00:49 +08:00
gh0stkey
ad323ba7a5 Version: 3.1 Update 2024-05-23 12:12:33 +08:00
gh0stkey
332b119064 Version: 3.1 Update 2024-05-23 12:00:13 +08:00
gh0stkey
ead03d42b9 Version: 3.0.2 Update 2024-05-12 19:25:33 +08:00
gh0stkey
4da3d3f42d Version: 3.0.2 Update 2024-05-12 19:02:38 +08:00
EvilChen
3363ca25ed Update issue templates 2024-05-11 09:56:23 +08:00
gh0stkey
496d0d2174 Version: 3.0.1 Update 2024-05-11 09:44:19 +08:00
gh0stkey
f387834c4d Version: 3.0.1 Update 2024-05-09 13:34:38 +08:00
gh0stkey
ca773f368b Version: 3.0.1 Update 2024-05-09 13:32:22 +08:00
gh0stkey
a6cd01300b Version: 3.0 Update 2024-05-07 16:08:46 +08:00
gh0stkey
ba079ab1d8 Version: 3.0 Update 2024-05-06 12:56:56 +08:00
EvilChen
a96dab6615 Update issue templates 2024-05-06 11:36:06 +08:00
gh0stkey
ad1a14b27e Version: 2.6.1 Update 2024-03-22 15:34:53 +08:00
gh0stkey
3a536a52de Version: 2.6 Update 2024-02-02 19:07:03 +08:00
ᴋᴇʏ
ea87c53958 Update issue templates 2024-01-26 20:05:43 +08:00
gh0stkey
e08b930fb5 Version: 2.5.11 Update 2024-01-18 12:07:20 +08:00
gh0stkey
49647d68d0 Version: 2.5.10 Update 2023-12-12 14:54:16 +08:00
gh0stkey
1c63841140 Version: 2.5.10 Update 2023-12-12 14:19:50 +08:00
gh0stkey
105c506039 Version: 2.5.10 Update 2023-12-12 14:19:28 +08:00
gh0stkey
f1941bccd7 Version: 2.5.9 Update 2023-11-28 15:26:25 +08:00
gh0stkey
d38e70523a Version: 2.5.9 Update 2023-11-28 09:11:56 +08:00
gh0stkey
1f7651c114 Version: 2.5.9 Update 2023-11-27 15:09:31 +08:00
gh0stkey
fc9a253d2b Version: 2.5.9 Update 2023-11-27 14:55:28 +08:00
ᴋᴇʏ
4cbcc1bcc4 Update issue templates 2023-11-27 09:11:52 +08:00
gh0stkey
765807de6e Version: 2.5.8 Update 2023-11-16 19:44:27 +08:00
gh0stkey
548315e163 Version: 2.5.8 Update 2023-11-16 19:33:38 +08:00
ᴋᴇʏ
d3ab207825 Update issue templates 2023-11-16 14:31:15 +08:00
ᴋᴇʏ
44260dd4ff Update issue templates 2023-11-16 14:27:15 +08:00
gh0stkey
cf3ac4978f Update README.md 2023-11-15 13:18:50 +08:00
gh0stkey
9c8dad8ac0 Version: 2.5.7 Update 2023-11-13 08:59:53 +08:00
gh0stkey
5cd216e45d Version: 2.5.7 Update 2023-11-13 08:28:44 +08:00
gh0stkey
87c5f713fa Version: 2.5.6 Update 2023-11-07 12:05:55 +08:00
gh0stkey
a0946bb723 Version: 2.5.6 Update 2023-11-07 11:32:44 +08:00
gh0stkey
bcb5177b54 Version: 2.5.6 Update 2023-11-07 11:15:20 +08:00
gh0stkey
0225c00f69 Version: 2.5.5 Update 2023-10-26 14:17:56 +08:00
gh0stkey
eafae602b8 Version: 2.5.4.1 2023-10-25 16:02:07 +08:00
gh0stkey
e56d8eb5d5 Version: 2.5.4 Update 2023-10-24 17:54:44 +08:00
gh0stkey
681cce0644 Version: 2.5.4 Update 2023-10-24 17:51:21 +08:00
gh0stkey
d43809e25f Version: 2.5.3 Update 2023-10-23 21:59:08 +08:00
gh0stkey
567dea6c60 Version: 2.5.3 Update 2023-10-23 21:51:12 +08:00
ᴋᴇʏ
8c388510c5 Version: 2.5.2 Update 2023-10-19 22:57:40 +08:00
ᴋᴇʏ
e22596819b Version: 2.5.2 Update 2023-10-19 22:46:11 +08:00
ᴋᴇʏ
d2cd7a0d03 Version: 2.5.2 Update 2023-10-19 22:45:44 +08:00
ᴋᴇʏ
67afe1f650 Version: 2.5.2 Update 2023-10-19 22:44:34 +08:00
ᴋᴇʏ
0602346249 Version: 2.5.2 Update 2023-10-19 22:43:29 +08:00
ᴋᴇʏ
953b966961 Version: 2.5.2 Update 2023-10-19 22:42:54 +08:00
ᴋᴇʏ
4c23d62576 Version: 2.5.2 Update 2023-10-19 22:41:50 +08:00
ᴋᴇʏ
6e9b8c8f37 Version: 2.5.2 Update 2023-10-19 22:41:14 +08:00
ᴋᴇʏ
ed58d891d5 Version: 2.5.2 Update 2023-10-19 22:40:06 +08:00
ᴋᴇʏ
33f5cab037 Version: 2.5.2 Update 2023-10-19 22:38:50 +08:00
ᴋᴇʏ
8b79c71df9 Version: 2.5.1 Update 2023-10-18 17:28:07 +08:00
ᴋᴇʏ
9ea0e4be9c Version: 2.5.1 Update 2023-10-18 17:08:09 +08:00
ᴋᴇʏ
41f197bcb2 Version: 2.5.1 Update 2023-10-18 15:17:45 +08:00
ᴋᴇʏ
31e419aed2 Version: 2.5.1 Update 2023-10-18 15:14:48 +08:00
ᴋᴇʏ
cf90a9366a Version: 2.5.1 Update 2023-10-18 15:14:33 +08:00
ᴋᴇʏ
6546446e4f Add files via upload 2023-10-18 00:51:20 +08:00
ᴋᴇʏ
6c4073c8ee Version: 2.5.1 Update 2023-10-18 00:51:01 +08:00
ᴋᴇʏ
1e1d51921d Version: 2.5.1 Update 2023-10-18 00:50:05 +08:00
ᴋᴇʏ
9135b8cbd2 Add files via upload 2023-10-18 00:49:02 +08:00
ᴋᴇʏ
cc7956d8dc Version: 2.5.1 Update 2023-10-18 00:48:12 +08:00
ᴋᴇʏ
405efdd5da Version: 2.5.1 Update 2023-10-18 00:47:41 +08:00
ᴋᴇʏ
0bb425f00b Version: 2.5.1 Update 2023-10-18 00:47:34 +08:00
ᴋᴇʏ
0bdff6fe28 Version: 2.5.1 Update 2023-10-18 00:46:21 +08:00
ᴋᴇʏ
6bd153d16a Version: 2.5.1 Update 2023-10-18 00:45:22 +08:00
ᴋᴇʏ
b12f9355fa Version: 2.5.1 Update 2023-10-18 00:44:50 +08:00
ᴋᴇʏ
fa9dcfc3d2 Version: 2.5.1 Update 2023-10-18 00:43:39 +08:00
ᴋᴇʏ
2e23388925 Version: 2.5.1 Update 2023-10-18 00:42:46 +08:00
ᴋᴇʏ
06fd54c9ce Delete BurpExtender.java 2023-10-18 00:42:22 +08:00
ᴋᴇʏ
0707a773c8 Version: 2.5.1 Update 2023-10-18 00:41:51 +08:00
ᴋᴇʏ
d0f49f8e6c Version: 2.5.1 Update 2023-10-18 00:40:37 +08:00
gh0stkey
5404c90c00 Update 2023-10-12 21:51:49 +08:00
gh0stkey
e68619d1c2 Version: 2.5 Update 2023-10-12 21:38:27 +08:00
gh0stkey
dd08ffaaa2 Update 2023-10-09 14:26:03 +08:00
gh0stkey
d9aeda4cc3 Update README.md 2023-10-09 14:25:14 +08:00
gh0stkey
e1c05ba10d Version: 2.4.7 Update 2023-09-28 01:48:51 +08:00
gh0stkey
6a17064b3a Version: 2.4.7 Update 2023-09-28 01:42:09 +08:00
gh0stkey
e698bb1caa Version: 2.4.7 Update 2023-09-28 01:23:36 +08:00
gh0stkey
a69503ca3d Version: 2.4.7 Update 2023-09-27 23:55:02 +08:00
AnonymousUser
d590d4a70e Version: 2.4.6 Update 2023-02-22 17:36:50 +08:00
AnonymousUser
548339fa58 Version: 2.4.6 Update 2023-02-22 17:25:55 +08:00
AnonymousUser
df4496d4fd Version: 2.4.5 Update 2022-12-18 16:12:16 +08:00
AnonymousUser
48e355ac54 Update 2022-12-18 15:09:28 +08:00
AnonymousUser
b784aa1425 Version: 2.4.4 Update 2022-09-26 18:49:35 +08:00
AnonymousUser
440b3b1504 Version: 2.4.3 Update 2022-09-20 10:33:00 +08:00
ᴋᴇʏ
a8f1798c7b Update README.md 2022-07-25 10:56:33 +08:00
AnonymousUser
225ee471ec Version: 2.4.2 Update 2022-07-15 10:12:34 +08:00
AnonymousUser
5097124867 Version: 2.4.1 Update 2022-06-29 16:05:51 +08:00
AnonymousUser
7e0e3054be Version: 2.4.1 Update 2022-06-29 15:17:42 +08:00
ᴋᴇʏ
17a84fc19e Update README.md 2022-06-24 11:11:27 +08:00
ᴋᴇʏ
1573d563eb Update README.md 2022-06-23 22:31:39 +08:00
ᴋᴇʏ
515f7b33f0 Update README.md 2022-06-23 22:26:51 +08:00
AnonymousUser
1dc510d576 Version: 2.4 Update 2022-06-23 15:48:22 +08:00
AnonymousUser
f401214524 Version: 2.4 Update 2022-06-23 15:34:22 +08:00
ᴋᴇʏ
7ebba02200 Update README.md 2022-06-22 10:26:36 +08:00
AnonymousUser
93f5c73aac Update 2022-06-22 10:20:44 +08:00
AnonymousUser
60b261d6ef Update 2022-06-21 17:06:45 +08:00
AnonymousUser
c84ebf3a9d Version: 2.3 Update 2022-05-27 13:29:32 +08:00
AnonymousUser
15f84028bb Version: 2.2.3 Update 2022-05-12 11:00:55 +08:00
AnonymousUser
1238e536d1 Version: 2.2.2 Update 2022-05-04 23:17:24 +08:00
AnonymousUser
5d23a68c0e Version: 2.2.2 Update 2022-05-04 22:51:58 +08:00
AnonymousUser
d7f04526b4 Version: 2.2.2 Update 2022-05-04 22:48:44 +08:00
AnonymousUser
acff96ed7b Version: 2.2.2 Update 2022-05-04 22:47:28 +08:00
AnonymousUser
350c093162 Update 2022-05-04 20:50:53 +08:00
AnonymousUser
0d3d4f88e9 Version: 2.2.1 Update 2022-04-21 10:50:10 +08:00
AnonymousUser
37ca315aba Version: 2.2 Update 2022-04-08 17:25:35 +08:00
AnonymousUser
241247a4a0 Version: 2.2 Update 2022-04-08 17:21:40 +08:00
AnonymousUser
08bfb69fce Version: 2.1.6 Update 2022-03-31 13:54:12 +08:00
AnonymousUser
a6d5f3a204 Version: 2.1.5 Update 2022-02-25 13:22:14 +08:00
AnonymousUser
c4d8743fe3 Version: 2.1.4 Update 2022-02-21 09:31:03 +08:00
AnonymousUser
903077c830 Version: 2.1.3 Update 2022-01-11 14:46:25 +08:00
AnonymousUser
daddf15af2 Version: 2.1.2 Update 2021-10-23 00:06:48 +08:00
AnonymousUser
e747011ec0 Version: 2.1.2 Update 2021-10-23 00:00:20 +08:00
AnonymousUser
f7b2e99eb2 Version: 2.1.2 Update 2021-10-22 22:46:42 +08:00
AnonymousUser
59cd0a88b9 Update 2021-10-22 21:58:45 +08:00
AnonymousUser
b16cbf5b60 Update 2021-10-22 21:57:11 +08:00
AnonymousUser
4ef766dd82 Version: 2.1.1 Update 2021-10-21 23:42:15 +08:00
AnonymousUser
5d9f590977 Version: 2.1 Update 2021-09-12 15:23:54 +08:00
AnonymousUser
5c326d3ca6 Version: 2.0.7 Update 2021-09-07 22:09:42 +08:00
ᴋᴇʏ
62edae0ab4 Merge pull request #28 from gh0stkey/add-license-1
Create LICENSE
2021-08-16 11:01:05 +08:00
ᴋᴇʏ
562378873c Create LICENSE 2021-08-16 11:00:53 +08:00
AnonymousUser
5f62e9653f update 2021-08-01 15:00:12 +08:00
66 changed files with 4950 additions and 1794 deletions

30
.github/ISSUE_TEMPLATE/问题反馈.md vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: 问题反馈
about: 尽可能详细的描述问题并反馈
title: "[BUG] 问题标题"
labels: bug
assignees: ''
---
## 使用环境
```
HaE 版本:
有无自定义规则:
BurpSuite 版本:
操作系统版本:
是否阅读README
是否知晓注意事项:
是否查阅历史ISSUE
```
## 问题详情
问题描述:
出现的场景:
## 解决建议
无。

138
.gitignore vendored
View File

@@ -1,3 +1,137 @@
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains+all Patch ###
# Ignore everything but code style settings and run configurations
# that are supposed to be shared within teams.
.idea/*
!.idea/codeStyles
!.idea/runConfigurations
### macOS ###
# General
.DS_Store
.idea
.gradle
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Gradle ###
.gradle
**/build/
!src/**/build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties
# Cache of project
.gradletasknamecache
# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# End of https://www.toptal.com/developers/gitignore/api/macos,gradle,jetbrains+all

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

115
README.md
View File

@@ -1,90 +1,83 @@
# HaE - Highlighter and Extractor
<div align="center">
<img src="images/logo.png" style="width: 20%" />
<h4><a href="https://gh0st.cn/HaE/">赋能白帽,高效作战!</a></h4>
<h5>第一作者: <a href="https://github.com/gh0stkey">EvilChen</a>(中孚信息元亨实验室)<br>第二作者: <a href="https://github.com/0chencc">0chencc</a>(米斯特安全团队)<br>第三作者: <a href="https://github.com/vaycore">vaycore</a>(独立安全研究员)</h5>
</div>
HaE相关作者 (来自米斯特安全团队 www.acmesec.cn)
## 项目介绍
架构作者: [@0chencc](https://github.com/0Chencc)
核心功能作者: [@EvilChen](https://github.com/gh0stkey)
**HaE**是一款**网络安全(数据安全)领域**下的框架式项目,采用了**乐高积木式**模块化设计理念实现对HTTP消息包含WebSocket精细化的标记和提取。
## 公共规则网站
通过运用**多引擎**的自定义正则表达式HaE能够准确匹配并处理HTTP请求与响应报文包含WebSocket对匹配成功的内容进行有效的标记和信息抽取从而提升网络安全数据安全领域下的**漏洞和数据分析效率**。
https://gh0st.cn/HaE/
> 随着现代化Web应用采用前后端分离的开发模式日常漏洞挖掘的过程中捕获的HTTP请求流量也相应增加。若想全面评估一个Web应用会花费大量时间在无用的报文上。**HaE的出现旨在解决这类情况**借助HaE您能够**有效减少**测试时间,将更多精力集中在**有价值且有意义**的报文上,从而**提高漏洞挖掘效率**。
## 介绍
GitHub项目地址https://github.com/gh0stkey/HaE
**HaE**是基于 `BurpSuite` 插件 `JavaAPI` 开发的请求高亮标记与信息提取的辅助型插件。
GitCode项目地址https://gitcode.com/gh0stkey/HaE
![-w1070](images/16000706401522.jpg)
**所获荣誉**:
该插件可以通过自定义正则的方式匹配**响应报文或请求报文**,可以自行决定符合该自定义正则匹配的相应请求是否需要高亮标记、信息提取。
1. [入选2022年KCon兵器谱](https://mp.weixin.qq.com/s/JohMsl1WD29LHCHuLf8mVQ)
2. [入选GitCode G-Star项目](https://gitcode.com/gh0stkey/HaE)
**注**: `HaE`的使用,对测试人员来说需要基本的正则表达式基础,由于`Java`正则表达式的库并没有`Python`的优雅或方便在使用正则的HaE要求使用者必须使用`()`将所需提取的表达式内容包含;例如你要匹配一个**Shiro应用**的响应报文,正常匹配规则为`rememberMe=delete`,如果你要提取这段内容的话就需要变成`(rememberMe=delete)`
**注意事项**:
1. HaE 3.0版本开始采用`Montoya API`进行开发使用新版HaE需要升级你的BurpSuite版本>=2023.12.1)。
2. HaE 2.6版本后对规则字段进行了更新,因此无法适配<=2.6版本的规则,请用户自行前往[规则转换页面](https://gh0st.cn/HaE/ConversionRule.html)进行转换。
3. 自定义HaE规则必须用左右括号`()`将所需提取的表达式内容包含,例如你要匹配一个**Shiro应用**的响应报文,正常匹配规则为`rememberMe=delete`在HaE的规则中就需要变成`(rememberMe=delete)`
## 使用方法
插件装载: `Extender - Extensions - Add - Select File - Next`
初次装载`HaE`初始化配置文件,默认配置文件内置一个正则: `Email`,初始化的配置文件会放在与`BurpSuite Jar`包同级目录下
初次装载`HaE`从Jar包中加载离线的规则库如果更新可以点击`Reinit`进行重新初始化。内置规则库地址可以在Github上找到`https://github.com/gh0stkey/HaE/blob/master/src/main/resources/rules/Rules.yml`
除了初始化的配置文件外,还有`Setting.yml`,该文件用于存储配置文件路径;`HaE`支持自定义配置文件路径,你可以通过点击`Select File`按钮进行选择自定义配置文件。
配置文件(`Config.yml`)和规则文件(`Rules.yml`)会放在固定目录下:
![-w477](images/16000710069404.jpg)
1. Linux/Mac用户的配置文件目录`~/.config/HaE/`
2. Windows用户的配置文件目录`%USERPROFILE%/.config/HaE/`
## 插件优点
除此之外,您也可以选择将配置文件存放在`HaE Jar包`的同级目录下的`/.config/HaE/`中,**以便于离线携带**。
1. 多选项自定义控制适配需求
2. 多颜色高亮分类将BurpSuite的所有高亮颜色集成: `red, orange, yellow, green, cyan, blue, pink, magenta, gray`
3. **颜色升级算法**: 利用下标的方式进行优先级排序当满足2个同颜色条件则以优先级顺序上升颜色例如: **两个正则,颜色为橘黄色,该请求两个正则都匹配到了,那么将升级为红色**
4. 配置文件采用YAML格式存储更加便于阅读和修改
5. 内置简单缓存,在“多正则、大数据”的场景下减少卡顿现象
6. **支持标签分页**,点击`...`即可添加新的标签页,对着标签页右键即可删除
### 规则释义
![-w477](images/16000720732851.jpg)
HaE目前的规则一共有8个字段详细的含义如下所示
## 实际使用
| 字段 | 含义 |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name | 规则名称,主要用于简短概括当前规则的作用。 |
| F-Regex | 规则正则主要用于填写正则表达式。在HaE中所需提取匹配的内容需要用`(``)`将正则表达式进行包裹。|
| S-Regex | 规则正则作用及使用同F-Regex。S-Regex为二次正则可以用于对F-Regex匹配的数据结果进行二次的匹配提取如不需要的情况下可以留空。|
| Format | 格式化输出在NFA引擎的正则表达式中我们可以通过`{0}``{1}``{2}`…的方式进行取分组格式化输出。默认情况下使用`{0}`即可。 |
| Scope | 规则作用域主要用于表示当前规则作用于HTTP报文的哪个部分。支持请求、响应的行、头、体以及完整的报文。 |
| Engine | 正则引擎,主要用于表示当前规则的正则表达式所使用的引擎。**DFA引擎**:对于文本串里的每一个字符只需扫描一次,速度快、特性少;**NFA引擎**:要翻来覆去标注字符、取消标注字符,速度慢,但是特性(如:分组、替换、分割)丰富。 |
| Color | 规则匹配颜色主要用于表示当前规则匹配到对应HTTP报文时所需标记的高亮颜色。在HaE中具备颜色升级算法当出现相同颜色时会自动向上升级一个颜色进行标记。 |
| Sensitive | 规则敏感性,主要用于表示当前规则对于大小写字母是否敏感,敏感(`True`)则严格按照大小写要求匹配,不敏感(`False`)则反之。 |
使用 RGPerson 生成测试数据,放入网站根目录文件中:
## 优势特点
![-w467](images/16000719723284.jpg)
1. **功能**通过对HTTP报文的颜色高亮、注释和提取帮助使用者获取有意义的信息**聚焦高价值报文**。
2. **界面**:清晰可视的界面设计,以及**简洁的界面交互**,帮助使用者更轻松的了解和配置项目,**避免`多按钮`式的复杂体验**。
3. **查询**将HTTP报文的高亮、注释和提取到的相关信息**集中在一个数据面板**,可以一键查询、提取信息,从而提高测试和梳理效率。
4. **算法**:内置高亮颜色的升级算法,当出现相同颜色时**会自动向上升级一个颜色**进行标记,**避免`屠龙者终成恶龙`场景**。
5. **管理****融入BurpSuite的项目数据管理**当使用BurpSuite进行项目存储时HaE数据也会一并存储。
6. **实战**:官方规则库和规则字段作用功能,都是**基于实战化场景总结输出**的,**以此提高数据的有效性、精准性发现**。
访问该地址,在`Proxy - HTTP History`中可以看见高亮请求,响应标签页中含有`MarkINFO`标签,其中将匹配到的信息提取了出来。
| 界面名称 | 界面展示 |
| ------------------------ | ---------------------------------------------------- |
| Rules规则管理 | <img src="images/rules.png" style="width: 80%" /> |
| Config配置管理 | <img src="images/config.png" style="width: 80%" /> |
| Databoard数据集合 | <img src="images/databoard.png" style="width: 80%" /> |
| MarkInfo数据展示 | <img src="images/markinfo.png" style="width: 80%" /> |
![-w1047](images/16000720732854.jpg)
## 支持项目
如果你觉得HaE好用可以打赏一下作者给作者持续更新下去的动力
## 正则优化
有些正则在实战应用场景中并不理想
在正则匹配手机号、身份证号码的时候(纯数字类)会存在一些误报(这里匹配身份证号码无法进行校验,误报率很高),但手机号处理这一块可以解决:
原正则:
```
1[3-9]\d{9}
```
误报场景: `12315188888888123`,这时候会匹配到`15188888888`,而实际上这一段并不是手机号,所以修改正则为:
```
[^0-9]+(1[3-9]\d{9})[^0-9]+
```
也就是要求匹配的手机号前后不能为0-9的数字。
## 实战用法
1. CMS指纹识别Discuz正则: `(Powered by Discuz!)`
2. OSS对象存储信息泄露正则: `([A|a]ccess[K|k]ey[I|i]d|[A|a]ccess[K|k]ey[S|s]ecret)`
3. 内网地址信息提取,正则: `(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:172\.(?:(?:1[6-9])|(?:2\d)|(?:3[01]))\.\d{1,3}\.\d{1,3})|(?:192\.168\.\d{1,3}\.\d{1,3})`
4. 实战插件关联搭配,漏洞挖掘案例: https://mp.weixin.qq.com/s/5vNn7dMRZBtv0ojPBAHV7Q
...还有诸多使用方法等待大家去发掘。
## 文末
随笔: 正义感是一个不可丢失的东西。
Github项目地址BUG、需求、正则欢迎提交: https://github.com/gh0stkey/HaE
<div align=center>
<img src="images/reward.jpeg" style="width: 30%" />
</div>
## 404StarLink 2.0 - Galaxy

View File

@@ -1,33 +1,37 @@
plugins {
id 'java'
}
repositories {
mavenCentral()
}
compileJava {
options.encoding = "UTF-8"
}
sourceSets {
main {
java {
srcDir './src/main/java'
}
}
}
task fatJar(type: Jar) {
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
dependencies {
compile 'net.portswigger.burp.extender:burp-extender-api:1.7.13'
compile 'org.jetbrains:annotations:16.0.2'
compile group: 'org.yaml', name: 'snakeyaml', version: '1.28'
compile 'net.sourceforge.jregex:jregex:1.2_01'
compile 'dk.brics.automaton:automaton:1.11-8'
plugins {
id 'java'
}
sourceCompatibility = 17
targetCompatibility = 17
repositories {
mavenCentral()
}
sourceSets {
main {
java {
srcDir './src/main/java'
}
}
}
dependencies {
implementation 'net.portswigger.burp.extensions:montoya-api:2023.12.1'
implementation 'org.yaml:snakeyaml:2.0'
implementation 'dk.brics.automaton:automaton:1.11-8'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
}
test {
useJUnitPlatform()
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

BIN
images/config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
images/databoard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
images/markinfo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
images/reward.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
images/rules.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View File

@@ -1,233 +0,0 @@
package burp;
import burp.action.*;
import burp.ui.MainUI;
import javax.swing.*;
import java.awt.*;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.io.PrintWriter;
import java.util.Map;
/*
* @author EvilChen
*/
public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEditorTabFactory, ITab {
private MainUI main = new MainUI();
private static PrintWriter stdout;
private IBurpExtenderCallbacks callbacks;
private static IExtensionHelpers helpers;
private static IMessageEditorTab HaETab;
MatchHTTP mh = new MatchHTTP();
ExtractContent ec = new ExtractContent();
DoAction da = new DoAction();
GetColorKey gck = new GetColorKey();
UpgradeColor uc = new UpgradeColor();
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks)
{
this.callbacks = callbacks;
BurpExtender.helpers = callbacks.getHelpers();
String version = "2.0.6";
callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 定义输出
stdout = new PrintWriter(callbacks.getStdout(), true);
stdout.println("@UI Author: 0chencc");
stdout.println("@Core Author: EvilChen");
stdout.println("@Github: https://github.com/gh0stkey/HaE");
// UI
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
initialize();
}
});
callbacks.registerHttpListener(BurpExtender.this);
callbacks.registerMessageEditorTabFactory(BurpExtender.this);
}
private void initialize(){
callbacks.customizeUiComponent(main);
callbacks.addSuiteTab(BurpExtender.this);
}
@Override
public String getTabCaption(){
return "HaE";
}
@Override
public Component getUiComponent() {
return main;
}
/*
* 使用processHttpMessage用来做Highlighter
*/
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
// 判断是否是响应且该代码作用域为REPEATER、INTRUDER、PROXY分别对应toolFlag 64、32、4
if (toolFlag == 64 || toolFlag == 32 || toolFlag == 4) {
Map<String, Map<String, Object>> obj;
// 流量清洗
String urlString = helpers.analyzeRequest(messageInfo.getHttpService(), messageInfo.getRequest()).getUrl().toString();
urlString = urlString.indexOf("?") > 0 ? urlString.substring(0, urlString.indexOf("?")) : urlString;
// 正则判断
if (mh.matchSuffix(urlString)) {
return;
}
if (messageIsRequest) {
byte[] byteRequest = messageInfo.getRequest();
// 获取报文头
List<String> requestTmpHeaders = helpers.analyzeRequest(messageInfo.getHttpService(), byteRequest).getHeaders();
String requestHeaders = String.join("\n", requestTmpHeaders);
// 获取报文主体
int requestBodyOffset = helpers.analyzeRequest(messageInfo.getHttpService(), byteRequest).getBodyOffset();
byte[] requestBody = Arrays.copyOfRange(byteRequest, requestBodyOffset, byteRequest.length);
obj = ec.matchRegex(byteRequest, requestHeaders, requestBody, "request");
} else {
byte[] byteResponse = messageInfo.getResponse();
// 获取报文头
List<String> responseTmpHeaders = helpers.analyzeRequest(messageInfo.getHttpService(), byteResponse).getHeaders();
String responseHeaders = String.join("\n", responseTmpHeaders);
// 获取报文主体
int responseBodyOffset = helpers.analyzeResponse(byteResponse).getBodyOffset();
byte[] responseBody = Arrays.copyOfRange(byteResponse, responseBodyOffset, byteResponse.length);
obj = ec.matchRegex(byteResponse, responseHeaders, responseBody, "response");
}
List<String> colorList = da.highlightList(obj);
if (colorList.size() != 0) {
String color = uc.getEndColor(gck.getColorKeys(colorList, Config.colorArray), Config.colorArray);
messageInfo.setHighlight(color);
}
}
}
class MarkInfoTab implements IMessageEditorTab {
private ITextEditor markInfoText;
private byte[] currentMessage;
private final IMessageEditorController controller;
private byte[] extractRequestContent;
private byte[] extractResponseContent;
public MarkInfoTab(IMessageEditorController controller, boolean editable) {
this.controller = controller;
markInfoText = callbacks.createTextEditor();
markInfoText.setEditable(editable);
}
@Override
public String getTabCaption() {
return "MarkInfo";
}
@Override
public Component getUiComponent() {
return markInfoText.getComponent();
}
@Override
public boolean isEnabled(byte[] content, boolean isRequest) {
Map<String, Map<String, Object>> obj;
if (isRequest) {
try {
// 流量清洗
String urlString = helpers.analyzeRequest(controller.getHttpService(), controller.getRequest()).getUrl().toString();
urlString = urlString.indexOf("?") > 0 ? urlString.substring(0, urlString.indexOf("?")) : urlString;
// 正则判断
if (mh.matchSuffix(urlString)) {
return false;
}
} catch (Exception e) {
return false;
}
IRequestInfo iRequestInfo = helpers.analyzeRequest(controller.getHttpService(), content);
// 获取报文头
List<String> requestTmpHeaders = iRequestInfo.getHeaders();
String requestHeaders = String.join("\n", requestTmpHeaders);
// 获取报文主体
int requestBodyOffset = iRequestInfo.getBodyOffset();
byte[] requestBody = Arrays.copyOfRange(content, requestBodyOffset, content.length);
obj = ec.matchRegex(content, requestHeaders, requestBody, "request");
if (obj.size() > 0) {
String result = da.extractString(obj);
extractRequestContent = result.getBytes();
return true;
}
} else {
IResponseInfo iResponseInfo = helpers.analyzeResponse(content);
// 获取报文头
List<String> responseTmpHeaders = iResponseInfo.getHeaders();
String responseHeaders = String.join("\n", responseTmpHeaders);
// 获取报文主体
int responseBodyOffset = iResponseInfo.getBodyOffset();
byte[] responseBody = Arrays.copyOfRange(content, responseBodyOffset, content.length);
obj = ec.matchRegex(content, responseHeaders, responseBody, "response");
if (obj.size() > 0) {
String result = da.extractString(obj);
extractResponseContent = result.getBytes();
return true;
}
}
return false;
}
@Override
public byte[] getMessage() {
return currentMessage;
}
@Override
public boolean isModified() {
return markInfoText.isTextModified();
}
@Override
public byte[] getSelectedData() {
return markInfoText.getSelectedText();
}
/*
* 使用setMessage用来做Extractor
*/
@Override
public void setMessage(byte[] content, boolean isRequest) {
try {
String c = new String(content, "UTF-8").intern();
} catch (UnsupportedEncodingException e) {
stdout.println(e);
}
if (content.length > 0) {
if (isRequest) {
markInfoText.setText(extractRequestContent);
} else {
markInfoText.setText(extractResponseContent);
}
}
currentMessage = content;
}
}
@Override
public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) {
HaETab = new MarkInfoTab(controller, editable);
return HaETab;
}
}

View File

@@ -1,38 +0,0 @@
package burp;
/*
* @author EvilChen
*/
public class Config {
public static String excludeSuffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|arc|au|avi|azw|bin|bmp|bz|bz2|cmx|cod|csh|css|csv|doc|docx|eot|epub|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|m3u|mid|midi|mjs|mp2|mp3|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|snd|svg|swf|tar|tif|tiff|ttf|vsd|wav|weba|webm|webp|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip|zip";
public static String[] scopeArray = new String[] {
"any",
"response",
"response header",
"response body",
"request",
"request header",
"request body"
};
public static String[] engineArray = new String[] {
"nfa",
"dfa"
};
public static String outputTplString = "[%s]\n%s\n\n";
public static String[] colorArray = new String[] {
"red",
"orange",
"yellow",
"green",
"cyan",
"blue",
"pink",
"magenta",
"gray"
};
}

View File

@@ -1,33 +0,0 @@
package burp.action;
import java.util.Map;
import burp.Config;
import java.util.ArrayList;
import java.util.List;
/*
* @author EvilChen
*/
public class DoAction {
public String extractString(Map<String, Map<String, Object>> obj) {
String[] result = {""};
obj.keySet().forEach(i->{
Map<String, Object> tmpMap = obj.get(i);
String data = tmpMap.get("data").toString();
String tmpStr = String.format(Config.outputTplString, i, data).intern();
result[0] += tmpStr;
});
return result[0];
}
public List<String> highlightList(Map<String, Map<String, Object>> obj) {
List<String> colorList = new ArrayList<String>();
obj.keySet().forEach(i->{
Map<String, Object> tmpMap = obj.get(i);
String color = tmpMap.get("color").toString();
colorList.add(color);
});
return colorList;
}
}

View File

@@ -1,103 +0,0 @@
package burp.action;
import java.io.UnsupportedEncodingException;
import java.util.*;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.AutomatonMatcher;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
import jregex.Matcher;
import jregex.Pattern;
import burp.yaml.LoadRule;
import burp.yaml.LoadConfigFile;
/*
* @author EvilChen
*/
public class ExtractContent {
private LoadConfigFile lcf = new LoadConfigFile();
private LoadRule lr = new LoadRule(lcf.getConfigPath());
public Map<String, Map<String, Object>> matchRegex(byte[] content, String headers, byte[] body, String scopeString) {
Map<String, Map<String, Object>> map = new HashMap<>(); // 最终返回的结果
Map<String,Object[][]> rules = lr.getConfig();
rules.keySet().forEach(i -> {
String matchContent = "";
for (Object[] objects : rules.get(i)) {
// 遍历获取规则
List<String> result = new ArrayList<>();
Map<String, Object> tmpMap = new HashMap<>();
String name = objects[1].toString();
boolean loaded = (Boolean) objects[0];
String regex = objects[2].toString();
String color = objects[3].toString();
String scope = objects[4].toString();
String engine = objects[5].toString();
// 判断规则是否开启与作用域
if (loaded && (scope.contains(scopeString) || scope.equals("any"))) {
switch (scope) {
case "any":
case "request":
case "response":
try {
matchContent = new String(content, "UTF-8").intern();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
break;
case "request header":
case "response header":
matchContent = headers;
break;
case "request body":
case "response body":
try {
matchContent = new String(body, "UTF-8").intern();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
break;
}
if (engine.equals("nfa")) {
Pattern pattern = new Pattern(regex);
Matcher matcher = pattern.matcher(matchContent);
while (matcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(matcher.group(1));
}
} else {
RegExp regexpr = new RegExp(regex);
Automaton auto = regexpr.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
AutomatonMatcher autoMatcher = runAuto.newMatcher(matchContent);
while (autoMatcher.find()) {
// 添加匹配数据至list
// 强制用户使用()包裹正则
result.add(autoMatcher.group());
}
}
// 去除重复内容
HashSet tmpList = new HashSet(result);
result.clear();
result.addAll(tmpList);
if (!result.isEmpty()) {
tmpMap.put("color", color);
tmpMap.put("data", String.join("\n", result));
// 初始化格式
map.put(name, tmpMap);
}
}
}
});
return map;
}
}

View File

@@ -1,27 +0,0 @@
package burp.action;
import java.util.ArrayList;
import java.util.List;
/*
* @author EvilChen
*/
public class GetColorKey {
/*
* 颜色下标获取
*/
public List<Integer> getColorKeys(List<String> keys, String[] colorArray){
List<Integer> result = new ArrayList<Integer>();
int size = colorArray.length;
// 根据颜色获取下标
for (int x = 0; x < keys.size(); x++) {
for (int v = 0; v < size; v++) {
if (colorArray[v].equals(keys.get(x))) {
result.add(v);
}
}
}
return result;
}
}

View File

@@ -1,24 +0,0 @@
package burp.action;
import jregex.Matcher;
import jregex.Pattern;
import jregex.REFlags;
import burp.yaml.LoadConfigFile;
/*
* @author EvilChen
*/
public class MatchHTTP {
// 匹配后缀
LoadConfigFile lc = new LoadConfigFile();
public boolean matchSuffix(String str) {
Pattern pattern = new Pattern(String.format("[\\w]+[\\.](%s)", lc.getExcludeSuffix()), REFlags.IGNORE_CASE);
Matcher matcher = pattern.matcher(str);
if(matcher.find()){
return true;
}else{
return false;
}
}
}

View File

@@ -1,56 +0,0 @@
package burp.action;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
/*
* @author EvilChen
*/
public class UpgradeColor {
private String endColor = "";
/*
* 颜色升级递归算法
*/
private String colorUpgrade(List<Integer> colorList, String[] colorArray) {
int colorSize = colorList.size();
colorList.sort(Comparator.comparingInt(Integer::intValue));
int i = 0;
List<Integer> stack = new ArrayList<Integer>();
while (i < colorSize) {
if (stack.isEmpty()) {
stack.add(colorList.get(i));
i++;
} else {
if (colorList.get(i) != stack.stream().reduce((first, second) -> second).orElse(99999999)) {
stack.add(colorList.get(i));
i++;
} else {
stack.set(stack.size() - 1, stack.get(stack.size() - 1) - 1);
i++;
}
}
}
// 利用HashSet删除重复元素
HashSet tmpList = new HashSet(stack);
if (stack.size() == tmpList.size()) {
stack.sort(Comparator.comparingInt(Integer::intValue));
if(stack.get(0).equals(-1)) {
this.endColor = colorArray[0];
} else {
this.endColor = colorArray[stack.get(0)];
}
} else {
this.colorUpgrade(stack, colorArray);
}
return "";
}
public String getEndColor(List<Integer> colorList, String[] colorArray) {
colorUpgrade(colorList, colorArray);
return endColor;
}
}

View File

@@ -1,117 +0,0 @@
package burp.ui;
import javax.swing.*;
import javax.swing.plaf.metal.MetalIconFactory;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
/*
* @author 6dc
*
* A class which creates a JTabbedPane and auto sets a close button when you add a tab
*/
public class JTabbedPaneCloseButton extends JTabbedPane {
public JTabbedPaneCloseButton() {
super();
}
/* Override Addtab in order to add the close Button everytime */
@Override
public void addTab(String title, Icon icon, Component component, String tip) {
super.addTab(title, icon, component, tip);
int count = this.getTabCount() - 1;
setTabComponentAt(count, new CloseButtonTab(component, title, icon));
}
@Override
public void addTab(String title, Icon icon, Component component) {
addTab(title, icon, component, null);
}
@Override
public void addTab(String title, Component component) {
addTab(title, null, component);
}
public void addTab(String title,Component component,Boolean closewith){
if (closewith){
addTab(title,component);
}else{
super.addTab(title,null,component,null);
}
}
/* addTabNoExit */
public void addTabNoExit(String title, Icon icon, Component component, String tip) {
super.addTab(title, icon, component, tip);
}
public void addTabNoExit(String title, Icon icon, Component component) {
addTabNoExit(title, icon, component, null);
}
public void addTabNoExit(String title, Component component) {
addTabNoExit(title, null, component);
}
/* Button */
public class CloseButtonTab extends JPanel {
private Component tab;
public CloseButtonTab(final Component tab, String title, Icon icon) {
this.tab = tab;
setOpaque(false);
FlowLayout flowLayout = new FlowLayout(FlowLayout.CENTER, 3, 3);
setLayout(flowLayout);
JLabel jLabel = new JLabel(title);
jLabel.setIcon(icon);
add(jLabel);
JButton button = new JButton(MetalIconFactory.getInternalFrameCloseIcon(2));
button.setMargin(new Insets(0, 0, 0, 0));
button.addMouseListener(new CloseListener(tab));
add(button);
}
}
/* ClickListener */
public class CloseListener implements MouseListener
{
private Component tab;
public CloseListener(Component tab){
this.tab=tab;
}
@Override
public void mouseClicked(MouseEvent e) {
if(e.getSource() instanceof JButton){
JButton clickedButton = (JButton) e.getSource();
JTabbedPane tabbedPane = (JTabbedPane) clickedButton.getParent().getParent().getParent();
tabbedPane.remove(tab);
}
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {
if(e.getSource() instanceof JButton){
JButton clickedButton = (JButton) e.getSource();
}
}
@Override
public void mouseExited(MouseEvent e) {
if(e.getSource() instanceof JButton){
JButton clickedButton = (JButton) e.getSource();
}
}
}
}

View File

@@ -1,347 +0,0 @@
package burp.ui;
import org.jetbrains.annotations.NotNull;
import burp.yaml.LoadConfigFile;
import burp.yaml.LoadRule;
import burp.yaml.SetRuleConfig;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.*;
import java.util.Map;
/*
* @author LinChen
*/
public class MainUI extends JPanel{
public MainUI() {
initComponents();
}
public void closeTabActionPerformed(ActionEvent e){
if (tabbedPane1.getTabCount()>2){
if (tabbedPane1.getSelectedIndex()!=0){
SetRuleConfig setruleconfig = new SetRuleConfig();
setruleconfig.deleteRules(tabbedPane1.getTitleAt(tabbedPane1.getSelectedIndex()));
tabbedPane1.remove(tabbedPane1.getSelectedIndex());
tabbedPane1.setSelectedIndex(tabbedPane1.getSelectedIndex()-1);
}else{
SetRuleConfig setruleconfig = new SetRuleConfig();
setruleconfig.deleteRules(tabbedPane1.getTitleAt(tabbedPane1.getSelectedIndex()));
tabbedPane1.remove(tabbedPane1.getSelectedIndex());
tabbedPane1.setSelectedIndex(tabbedPane1.getSelectedIndex());
}
}
}
private void SelectFileMouseClicked(MouseEvent e) {
JFileChooser chooseconfig = new JFileChooser();
chooseconfig.setFileSelectionMode(JFileChooser.FILES_ONLY);
FileNameExtensionFilter filter = new FileNameExtensionFilter("Yaml File (.yml/.yaml)","yaml", "yml");
chooseconfig.setFileFilter(filter);
int selectframe = chooseconfig.showDialog(new JLabel(),"Select");
if (selectframe == JFileChooser.APPROVE_OPTION){
String configpath = chooseconfig.getSelectedFile().toString();
reloadRule(configpath);
loadfile.setConfigPath(configpath);
}
configfilepathtext.setText(loadfile.getConfigPath());
}
private void reloadRule(String configfile){
tabbedPane1.removeAll();
LoadRule loadrule = new LoadRule(configfile);
Map<String,Object[][]> config = loadrule.getConfig();
ruleSwitch.setListen(false);
config.keySet().forEach(i->tabbedPane1.addTab(i,new RulePane(config.get(i),tabbedPane1)));
tabbedPane1.addTab("...",new JLabel());
ruleSwitch.setListen(true);
}
private void reloadRule(){
tabbedPane1.removeAll();
LoadRule loadrule = new LoadRule(loadfile.getConfigPath());
Map<String,Object[][]> config = loadrule.getConfig();
ruleSwitch.setListen(false);
config.keySet().forEach(i->tabbedPane1.addTab(i,new RulePane(config.get(i),tabbedPane1))
);
tabbedPane1.addTab("...",new JLabel());
ruleSwitch.setListen(true);
}
private void reloadMouseClicked(MouseEvent e) {
reloadRule();
}
private void ESSaveMouseClicked(MouseEvent e) {
// TODO add your code here
LoadConfigFile lcf = new LoadConfigFile();
lcf.setExcludeSuffix(EStext.getText());
}
private void initComponents() {
tabbedPane2 = new JTabbedPane();
tabbedPane1 = new JTabbedPane();
panel3 = new JPanel();
configfilepathtext = new JTextField();
label1 = new JLabel();
SelectFile = new JButton();
reload = new JButton();
label2 = new JLabel();
EStext = new JTextField();
ESSave = new JButton();
//======== this ========
setLayout(new GridBagLayout());
((GridBagLayout)getLayout()).columnWidths = new int[] {0, 0};
((GridBagLayout)getLayout()).rowHeights = new int[] {0, 0};
((GridBagLayout)getLayout()).columnWeights = new double[] {1.0, 1.0E-4};
((GridBagLayout)getLayout()).rowWeights = new double[] {1.0, 1.0E-4};
//======== tabbedPane2 ========
{
tabbedPane2.addTab("Rules", tabbedPane1);
//======== panel3 ========
{
panel3.setLayout(new GridBagLayout());
((GridBagLayout)panel3.getLayout()).columnWidths = new int[] {0, 0, 0, 0, 0};
((GridBagLayout)panel3.getLayout()).rowHeights = new int[] {0, 0, 0};
((GridBagLayout)panel3.getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)panel3.getLayout()).rowWeights = new double[] {0.0, 0.0, 1.0E-4};
//---- configfilepathtext ----
configfilepathtext.setEditable(false);
panel3.add(configfilepathtext, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 0, 5, 5), 0, 0));
//---- label1 ----
label1.setText("Config File Path:");
panel3.add(label1, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.VERTICAL,
new Insets(5, 5, 5, 5), 0, 0));
//---- SelectFile ----
SelectFile.setText("Select File ...");
SelectFile.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
SelectFileMouseClicked(e);
}
});
panel3.add(SelectFile, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 0, 5, 5), 0, 0));
//---- reload ----
reload.setText("Reload");
reload.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
reloadMouseClicked(e);
}
});
panel3.add(reload, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 0, 5, 5), 0, 0));
//---- label2 ----
label2.setText("ExcludeSuffix:");
panel3.add(label2, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,
new Insets(0, 5, 5, 5), 0, 0));
panel3.add(EStext, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 5), 0, 0));
//---- ESSave ----
ESSave.setText("Save");
ESSave.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
ESSaveMouseClicked(e);
}
});
panel3.add(ESSave, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 5), 0, 0));
}
tabbedPane2.addTab("Config", panel3);
}
add(tabbedPane2, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
// JFormDesigner - End of component initialization //GEN-END:initComponents
LoadRule loadRule = new LoadRule(loadfile.getConfigPath());
Map<String,Object[][]> config = loadRule.getConfig();
config.keySet().forEach(i->tabbedPane1.addTab(i,new RulePane(config.get(i),tabbedPane1)));
tabbedPane1.addTab("...",new JLabel());
//TabTitleEditListener ruleSwitch = new TabTitleEditListener(tabbedPane1);
configfilepathtext.setText(loadfile.getConfigPath());
LoadConfigFile lcf =new LoadConfigFile();
EStext.setText(lcf.getExcludeSuffix());
ruleSwitch = new TabTitleEditListener(tabbedPane1);
tabbedPane1.addChangeListener(ruleSwitch);
tabbedPane1.addMouseListener(ruleSwitch);
closeTab.addActionListener(e -> closeTabActionPerformed(e));
tabMenu.add(closeTab);
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JTabbedPane tabbedPane2;
private JTabbedPane tabbedPane1;
private JPanel panel3;
private JTextField configfilepathtext;
private JLabel label1;
private JButton SelectFile;
private JButton reload;
private JLabel label2;
private JTextField EStext;
private JButton ESSave;
// JFormDesigner - End of variables declaration //GEN-END:variables
protected static JPopupMenu tabMenu = new JPopupMenu();
private JMenuItem closeTab = new JMenuItem("Delete");
private TabTitleEditListener ruleSwitch;
private LoadConfigFile loadfile = new LoadConfigFile();
}
class TabTitleEditListener extends MouseAdapter implements ChangeListener, DocumentListener {
protected final JTextField editor = new JTextField();
protected final JTabbedPane tabbedPane;
protected int editingIdx = -1;
protected int len = -1;
protected Boolean listen = true;
protected Dimension dim;
protected Component tabComponent;
protected Boolean isRenamesucc = false;
protected LoadConfigFile loadfile = new LoadConfigFile();
protected LoadRule lr = new LoadRule(loadfile.getConfigPath());
protected SetRuleConfig setRuleConfig = new SetRuleConfig();
protected final Action startEditing = new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
editingIdx = tabbedPane.getSelectedIndex();
tabComponent = tabbedPane.getTabComponentAt(editingIdx);
tabbedPane.setTabComponentAt(editingIdx, editor);
isRenamesucc = true;
editor.setVisible(true);
editor.setText(tabbedPane.getTitleAt(editingIdx));
editor.selectAll();
editor.requestFocusInWindow();
len = editor.getText().length();
dim = editor.getPreferredSize();
editor.setMinimumSize(dim);
}
};
protected final Action renameTabTitle = new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
String title = editor.getText().trim();
if (editingIdx >= 0 && !title.isEmpty()) {
String oldname = tabbedPane.getTitleAt(editingIdx);
tabbedPane.setTitleAt(editingIdx, title);
setRuleConfig.rename(oldname,title);
}
cancelEditing.actionPerformed(null);
}
};
protected final Action cancelEditing = new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
if (editingIdx >= 0) {
tabbedPane.setTabComponentAt(editingIdx, tabComponent);
editor.setVisible(false);
editingIdx = -1;
len = -1;
tabComponent = null;
editor.setPreferredSize(null);
tabbedPane.requestFocusInWindow();
}
}
};
protected TabTitleEditListener(JTabbedPane tabbedPane) {
super();
this.tabbedPane = tabbedPane;
editor.setBorder(BorderFactory.createEmptyBorder());
editor.addFocusListener(new FocusAdapter() {
@Override public void focusLost(FocusEvent e) {
renameTabTitle.actionPerformed(null);
}
});
InputMap im = editor.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap am = editor.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel-editing");
am.put("cancel-editing", cancelEditing);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "rename-tab-title");
am.put("rename-tab-title", renameTabTitle);
editor.getDocument().addDocumentListener(this);
tabbedPane.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "start-editing");
tabbedPane.getActionMap().put("start-editing", startEditing);
}
@Override public void stateChanged(ChangeEvent e) {
if (e.getSource() instanceof JTabbedPane && listen) {
JTabbedPane pane = (JTabbedPane) e.getSource();
if (!isRenamesucc){
if (pane.getSelectedIndex() == pane.getComponentCount()-1){
newTab();
}
}else{
if (pane.getSelectedIndex() == pane.getComponentCount()-2){
newTab();
}
}
}
renameTabTitle.actionPerformed(null);
}
public void newTab(){
Object[][] data = new Object[][]{{false, "New Name", "(New Regex)", "gray", "any", "nfa"}};
insertTab(tabbedPane,setRuleConfig.newRules(),data);
}
public void insertTab(@NotNull JTabbedPane pane,String title,Object[][] data){
pane.addTab(title,new RulePane(data,pane));
pane.remove(pane.getSelectedIndex());
pane.addTab("...",new JLabel());
}
public void setListen(Boolean listen){
this.listen = listen;
}
@Override public void insertUpdate(DocumentEvent e) {
updateTabSize();
}
@Override public void removeUpdate(DocumentEvent e) {
updateTabSize();
}
@Override public void changedUpdate(DocumentEvent e) {}
@Override public void mouseClicked(MouseEvent e) {
switch (e.getButton()){
case 1:
{
Rectangle r = tabbedPane.getBoundsAt(tabbedPane.getSelectedIndex());
boolean isDoubleClick = e.getClickCount() >= 2;
if (isDoubleClick && r.contains(e.getPoint())) {
startEditing.actionPerformed(null);
} else {
renameTabTitle.actionPerformed(null);
}
break;
}
case 3:{
MainUI.tabMenu.show(e.getComponent(),e.getX(),e.getY());
break;
}
default:
break;
}
}
protected void updateTabSize() {
editor.setPreferredSize(editor.getText().length() > len ? null : dim);
tabbedPane.revalidate();
}
}

View File

@@ -1,197 +0,0 @@
package burp.ui;
import burp.yaml.SetRuleConfig;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;
/*
* @author LinChen
*/
public class RulePane extends JPanel {
public RulePane(Object[][] data,JTabbedPane pane) {
initComponents(data,pane);
}
private SetRuleConfig setruleconfig = new SetRuleConfig();
private Boolean isEdit = false;
private void RuleAddMouseClicked(MouseEvent e, JTabbedPane pane) {
// TODO add your code here
RuleSetting add = new RuleSetting();
int isOk = JOptionPane.showConfirmDialog(null,add,"RuleSetting - Add Rule",JOptionPane.OK_OPTION);
if(isOk == 0){
Vector data = new Vector();
data.add(false);
data.add(add.Name.getText());
data.add(add.Regex.getText());
data.add(add.ColorSelect.getSelectedItem().toString());
data.add(add.ScopeSelect.getSelectedItem().toString());
data.add(add.EngineSelect.getSelectedItem().toString());
model.insertRow(model.getRowCount(),data);
model = (DefaultTableModel) table.getModel();
setruleconfig.add(data,pane.getTitleAt(pane.getSelectedIndex()));
}
}
private void RuleEditMouseClicked(MouseEvent e,JTabbedPane pane){
if (table.getSelectedRowCount()>=1){
RuleSetting edit = new RuleSetting();
edit.Name.setText(table.getValueAt(table.getSelectedRow(),1).toString());
edit.Regex.setText(table.getValueAt(table.getSelectedRow(),2).toString());
edit.ColorSelect.setSelectedItem(table.getValueAt(table.getSelectedRow(),3).toString());
edit.ScopeSelect.setSelectedItem(table.getValueAt(table.getSelectedRow(),4).toString());
edit.EngineSelect.setSelectedItem(table.getValueAt(table.getSelectedRow(),5).toString());
int isOk = JOptionPane.showConfirmDialog(null,edit,"RuleSetting - Edit Rule",JOptionPane.OK_OPTION);
if (isOk ==0){
int select = table.convertRowIndexToModel(table.getSelectedRow());
model.setValueAt(edit.Name.getText(),select,1);
model.setValueAt(edit.Regex.getText(),select,2);
model.setValueAt(edit.ColorSelect.getSelectedItem().toString(),select,3);
model.setValueAt(edit.ScopeSelect.getSelectedItem().toString(),select,4);
model.setValueAt(edit.EngineSelect.getSelectedItem().toString(),select,5);
model = (DefaultTableModel) table.getModel();
setruleconfig.edit((Vector) model.getDataVector().get(select),select,pane.getTitleAt(pane.getSelectedIndex()));
}
}
}
private void RuleRemoveMouseClicked(MouseEvent e,JTabbedPane pane){
if (table.getSelectedRowCount()>=1){
int isOk = JOptionPane.showConfirmDialog(null,"Are your sure?","RuleSetting - Delete Rule",JOptionPane.OK_OPTION);
if (isOk==0){
int select = table.convertRowIndexToModel(table.getSelectedRow());
model.removeRow(select);
model = (DefaultTableModel) table.getModel();
setruleconfig.remove(select,pane.getTitleAt(pane.getSelectedIndex()));
}
}
}
private void RuleTableChange(TableModelEvent e,JTabbedPane pane) {
if (e.getColumn()==0&&table.getSelectedRow()!=-1&&!isEdit){
model = (DefaultTableModel) table.getModel();
int select = table.convertRowIndexToModel(table.getSelectedRow());
setruleconfig.edit((Vector) model.getDataVector().get(select),select,pane.getTitleAt(pane.getSelectedIndex()));
}
}
private void initComponents(Object[][] data,JTabbedPane pane) {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
RuleAdd = new JButton();
RuleEdit = new JButton();
scrollPane = new JScrollPane();
table = new JTable();
Remove = new JButton();
//======== this ========
setLayout(new GridBagLayout());
((GridBagLayout)getLayout()).columnWidths = new int[] {0, 0, 0};
((GridBagLayout)getLayout()).rowHeights = new int[] {0, 0, 0, 0, 0};
((GridBagLayout)getLayout()).columnWeights = new double[] {0.0, 1.0, 1.0E-4};
((GridBagLayout)getLayout()).rowWeights = new double[] {0.0, 0.0, 0.0, 1.0, 1.0E-4};
//---- RuleAdd ----
RuleAdd.setText("Add");
RuleAdd.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
isEdit = true;
RuleAddMouseClicked(e,pane);
model = (DefaultTableModel) table.getModel();
isEdit = false;
}
});
add(RuleAdd, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(15, 5, 3, 2), 0, 0));
//---- RuleEdit ----
RuleEdit.setText("Edit");
RuleEdit.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
isEdit = true;
RuleEditMouseClicked(e,pane);
model = (DefaultTableModel) table.getModel();
isEdit = false;
}
});
add(RuleEdit, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 5, 3, 2), 0, 0));
//======== scrollPane ========
{
//---- table ----
table.setShowVerticalLines(false);
table.setVerifyInputWhenFocusTarget(false);
table.setUpdateSelectionOnSort(false);
table.setShowHorizontalLines(false);
table.setModel(new DefaultTableModel());
table.setSurrendersFocusOnKeystroke(true);
scrollPane.setViewportView(table);
}
add(scrollPane, new GridBagConstraints(1, 0, 1, 4, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(15, 5, 5, 5), 0, 0));
//---- Remove ----
Remove.setText("Remove");
Remove.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
isEdit = true;
RuleRemoveMouseClicked(e,pane);
model = (DefaultTableModel) table.getModel();
isEdit = false;
}
});
add(Remove, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 5, 3, 2), 0, 0));
// JFormDesigner - End of component initialization //GEN-END:initComponents
table.setModel(model);
model.setDataVector(data,title);
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
RuleTableChange(e,pane);
}
});
table.setRowSorter(new TableRowSorter(model));
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
public JButton RuleAdd;
public JButton RuleEdit;
public JScrollPane scrollPane;
public JTable table;
public JButton Remove;
// JFormDesigner - End of variables declaration //GEN-END:variables
private final String[] title = new String[]{"Loaded", "Name", "Regex", "Color", "Scope", "Engine"};
private DefaultTableModel model = new DefaultTableModel() {
public Class<?> getColumnClass ( int column){
if (column == 0) {
return Boolean.class;
}else{
return String.class;
}
}
public boolean isCellEditable(int row,int column){
if (column ==0){
return true;
}else {
return false;
}
}
};
}

View File

@@ -1,105 +0,0 @@
package burp.ui;
import javax.swing.*;
import java.awt.*;
import burp.Config;
/*
* @author LinChen
*/
public class RuleSetting extends JPanel {
public RuleSetting() {
initComponents();
}
public void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
label5 = new JLabel();
label4 = new JLabel();
Regex = new JTextField();
label3 = new JLabel();
label2 = new JLabel();
Name = new JTextField();
ScopeSelect = new JComboBox<>();
EngineSelect = new JComboBox<>();
label6 = new JLabel();
ColorSelect = new JComboBox<>();
//======== this ========
setLayout(null);
//---- label5 ----
label5.setText("Engine:");
add(label5);
label5.setBounds(new Rectangle(new Point(10, 175), label5.getPreferredSize()));
//---- label4 ----
label4.setText("Scope:");
add(label4);
label4.setBounds(new Rectangle(new Point(10, 135), label4.getPreferredSize()));
add(Regex);
Regex.setBounds(70, 50, 265, 30);
//---- label3 ----
label3.setText("Regex:");
add(label3);
label3.setBounds(new Rectangle(new Point(10, 55), label3.getPreferredSize()));
//---- label2 ----
label2.setText("Name:");
add(label2);
label2.setBounds(new Rectangle(new Point(10, 15), label2.getPreferredSize()));
add(Name);
Name.setBounds(70, 10, 265, 30);
//---- ScopeSelect ----
ScopeSelect.setModel(new DefaultComboBoxModel<>(Config.scopeArray));
add(ScopeSelect);
ScopeSelect.setBounds(70, 130, 265, ScopeSelect.getPreferredSize().height);
//---- EngineSelect ----
EngineSelect.setModel(new DefaultComboBoxModel<>(Config.engineArray));
add(EngineSelect);
EngineSelect.setBounds(70, 170, 265, EngineSelect.getPreferredSize().height);
//---- label7 ----
label6.setText("Color:");
add(label6);
label6.setBounds(new Rectangle(new Point(10, 95), label6.getPreferredSize()));
//---- ColorSelect ----
ColorSelect.setModel(new DefaultComboBoxModel<>(Config.colorArray));
add(ColorSelect);
ColorSelect.setBounds(70, 90, 265, ColorSelect.getPreferredSize().height);
{
// compute preferred size
Dimension preferredSize = new Dimension();
for(int i = 0; i < getComponentCount(); i++) {
Rectangle bounds = getComponent(i).getBounds();
preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
}
Insets insets = getInsets();
preferredSize.width += insets.right;
preferredSize.height += insets.bottom;
setMinimumSize(preferredSize);
setPreferredSize(preferredSize);
}
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel label5;
private JLabel label4;
public JTextField Regex;
private JLabel label3;
private JLabel label2;
public JTextField Name;
public JComboBox<String> ScopeSelect;
public JComboBox<String> EngineSelect;
private JLabel label6;
public JComboBox<String> ColorSelect;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -1,19 +0,0 @@
package burp.yaml;
import java.util.List;
/*
* @author LinChen
*/
public class Config {
public List<Rules> rules;
public List<Rules> getRules() {
return rules;
}
public void setRules(List<Rules> rules) {
this.rules = rules;
}
}

View File

@@ -1,86 +0,0 @@
package burp.yaml;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/*
* @author LinChen
*/
public class LoadConfigFile {
private static Yaml yaml = new Yaml();
private static final String SettingPath = "Setting.yml";
private static final String ConfigPath = "Config.yml";
public LoadConfigFile(){
init();
}
// 初始化配置
public void init(){
File yamlSetting = new File(SettingPath);
if (!(yamlSetting.exists() && yamlSetting.isFile())) {
Map<String,Object> r = new HashMap<>();
r.put("configPath", ConfigPath);
r.put("excludeSuffix", getExcludeSuffix());
try{
Writer ws = new OutputStreamWriter(new FileOutputStream(SettingPath),"UTF-8");
yaml.dump(r, ws);
}catch (Exception ex){
ex.printStackTrace();
}
}
}
public String getExcludeSuffix(){
try {
InputStream inorder = new FileInputStream(SettingPath);
Map<String,Object> r;
r = yaml.load(inorder);
return r.get("excludeSuffix").toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
return "css|jpeg|gif|jpg|png|pdf|rar|zip|docx|doc|svg|jpeg|ico|woff|woff2|ttf|otf";
}
}
public String getConfigPath(){
try {
InputStream inorder = new FileInputStream(SettingPath);
Map<String,Object> r;
r = yaml.load(inorder);
return r.get("configPath").toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
return ConfigPath;
}
}
public void setExcludeSuffix(@NotNull String excludeSuffix){
Map<String,Object> r = new HashMap<>();
r.put("excludeSuffix", excludeSuffix);
r.put("configPath", getConfigPath());
try{
Writer ws = new OutputStreamWriter(new FileOutputStream(SettingPath),"UTF-8");
yaml.dump(r, ws);
}catch (Exception ex){
ex.printStackTrace();
}
}
public void setConfigPath(@NotNull String filePath){
Map<String,Object> r = new HashMap<>();
r.put("configPath", filePath);
r.put("excludeSuffix", getExcludeSuffix());
try{
Writer ws = new OutputStreamWriter(new FileOutputStream(SettingPath),"UTF-8");
yaml.dump(r, ws);
}catch (Exception ex){
ex.printStackTrace();
}
}
}

View File

@@ -1,89 +0,0 @@
package burp.yaml;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.nodes.Tag;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/*
* @author LinChen
*/
public class LoadRule {
private static String filePath = "Config.yml";
public LoadRule(String configfile){
filePath = configfile;
init();
}
// 初始化配置
public void init(){
File settingyaml = new File(filePath);
if (!(settingyaml.exists() && settingyaml.isFile())){
Map<String,Object[][]> r = new HashMap<>();
Rule rule = new Rule();
rule.setLoaded(true);
rule.setName("Email");
rule.setColor("yellow");
rule.setEngine("nfa");
rule.setScope("response");
rule.setRegex("(([a-zA-Z0-9][_|\\.])*[a-zA-Z0-9]+@([a-zA-Z0-9][-|_|\\.])*[a-zA-Z0-9]+\\.((?!js|css|jpg|jpeg|png|ico)[a-zA-Z]{2,}))");
Rules rules = new Rules();
rules.setType("Basic Information");
ArrayList<Rule> rl = new ArrayList<>();
rl.add(rule);
rules.setRule(rl);
ArrayList<Rules> rls = new ArrayList<>();
rls.add(rules);
Config config = new Config();
config.setRules(rls);
DumperOptions dop = new DumperOptions();
dop.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Representer representer = new Representer();
representer.addClassTag(Config.class, Tag.MAP);
Yaml yaml = new Yaml(new Constructor(),representer,dop);
LoadConfigFile loadfile = new LoadConfigFile();
File f = new File(loadfile.getConfigPath());
try{
Writer ws = new OutputStreamWriter(new FileOutputStream(f),"UTF-8");
yaml.dump(config,ws);
}catch (Exception ex){
ex.printStackTrace();
}
}
}
public static Map<String,Object[][]> getConfig(){
InputStream inorder = null;
{
try {
inorder = new FileInputStream(new File(filePath));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Yaml yaml = new Yaml(new Constructor(Config.class));
Config plugin = yaml.loadAs(inorder, Config.class);
Map<String,Object[][]> config = new HashMap<>();
plugin.rules.forEach(i->{
ArrayList<Object[]> data = new ArrayList<>();
i.rule.forEach(j->{
try {
data.add(j.getRuleObject());
}catch (Exception e){
e.printStackTrace();
}
});
config.put(i.getType(), data.toArray(new Object[data.size()][]));
});
return config;
}
}

View File

@@ -1,81 +0,0 @@
package burp.yaml;
import java.util.HashMap;
import java.util.Map;
/*
* @author LinChen
*/
public class Rule {
private String Name;
private Boolean Loaded;
private String Regex;
private String Color;
private String Engine;
private String Scope;
public Boolean getLoaded() {
return Loaded;
}
public String getColor() {
return Color;
}
public String getEngine() {
return Engine;
}
public String getName() {
return Name;
}
public String getRegex() {
return Regex;
}
public String getScope() {
return Scope;
}
public void setLoaded(Boolean loaded) {
this.Loaded = loaded;
}
public void setColor(String color) {
this.Color = color;
}
public void setEngine(String engine) {
this.Engine = engine;
}
public void setName(String name) {
this.Name = name;
}
public void setRegex(String regex) {
this.Regex = regex;
}
public void setScope(String scope) {
this.Scope = scope;
}
public Object[] getRuleObject(){
return new Object[]{Loaded, Name, Regex, Color, Scope, Engine};
}
public Map<String,Object> getRuleObjMap(){
Map<String,Object> r = new HashMap<>();
r.put("Loaded",Loaded);
r.put("Name",Name);
r.put("Regex",Regex);
r.put("Color",Color);
r.put("Scope",Scope);
r.put("Engine",Engine);
return r;
}
public String toString(){
return "{ \nLoaded: "+Loaded+"\nName: "+Name+"\nRegex: "+Regex+"\nColor: "+Color+"\nScope: "+Scope+"\nEngine: "+Engine+"\n}";
}
}

View File

@@ -1,35 +0,0 @@
package burp.yaml;
import java.util.List;
/*
* @author LinChen
*/
public class Rules {
private String type;
public List<Rule> rule;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Rule> getRule() {
return rule;
}
public void setRule(List<Rule> rule) {
this.rule = rule;
}
public void setRuleObj(){
}
public String toString(){
return "{ type: "+type+"\n config: "+ rule +"}\n";
}
}

View File

@@ -1,109 +0,0 @@
package burp.yaml;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.*;
public class SetRuleConfig {
private static Yaml yaml;
private static LoadConfigFile loadfile;
private static LoadRule lr;
private Map<String,Object[][]> config = lr.getConfig();
public void format(){
DumperOptions dop = new DumperOptions();
dop.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Representer representer = new Representer();
representer.addClassTag(Config.class, Tag.MAP);
yaml = new Yaml(new Constructor(),representer,dop);
Config con = new Config();
List<Rules> rls = new ArrayList<>();
config.keySet().forEach(i->
{
Rules rlstmp = new Rules();
rlstmp.setType(i);
List<Rule> rl = new ArrayList<>();
for (Object[] objects : config.get(i)) {
Rule rltmp = new Rule();
rltmp.setName((String) objects[1]);
rltmp.setLoaded((Boolean) objects[0]);
rltmp.setRegex((String) objects[2]);
rltmp.setColor((String) objects[3]);
rltmp.setScope((String) objects[4]);
rltmp.setEngine((String) objects[5]);
rl.add(rltmp);
}
rlstmp.setRule(rl);
rls.add(rlstmp);
});
con.setRules(rls);
File f = new File(loadfile.getConfigPath());
try{
Writer ws = new OutputStreamWriter(new FileOutputStream(f),"UTF-8");
yaml.dump(con,ws);
}catch (Exception ex){
ex.printStackTrace();
}
}
public void edit(Vector data,int select, String type){
loadfile = new LoadConfigFile();
lr = new LoadRule(loadfile.getConfigPath());
config = lr.getConfig();
config.get(type)[select] = data.toArray();
this.format();
}
public void add(Vector data,String type){
loadfile = new LoadConfigFile();
lr = new LoadRule(loadfile.getConfigPath());
config = lr.getConfig();
ArrayList<Object[]> x = new ArrayList<Object[]>(Arrays.asList(config.get(type)));
x.add(data.toArray());
config.put(type,x.toArray(new Object[x.size()][]));
this.format();
}
public void remove(int select,String type){
loadfile = new LoadConfigFile();
lr = new LoadRule(loadfile.getConfigPath());
config = lr.getConfig();
ArrayList<Object[]> x = new ArrayList<Object[]>(Arrays.asList(config.get(type)));
x.remove(select);
config.put(type,x.toArray(new Object[x.size()][]));
this.format();
}
public void rename(String oldname,String newname){
loadfile = new LoadConfigFile();
lr = new LoadRule(loadfile.getConfigPath());
config = lr.getConfig();
config.put(newname,config.remove(oldname));
this.format();
}
public void deleteRules(String Rules){
loadfile = new LoadConfigFile();
lr = new LoadRule(loadfile.getConfigPath());
config = lr.getConfig();
config.remove(Rules);
this.format();
}
public String newRules(){
int i = 0;
loadfile = new LoadConfigFile();
lr = new LoadRule(loadfile.getConfigPath());
config = lr.getConfig();
String name = "New ";
Object[][] data = new Object[][]{{false, "New Name", "(New Regex)", "gray", "any", "nfa"}};
while (config.containsKey(name+i)){
i++;
}
config.put(name+i,data);
this.format();
return name+i;
}
}

View File

@@ -0,0 +1,67 @@
package hae;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Config {
public static String suffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip";
public static String host = "gh0st.cn";
public static String status = "404";
public static String size = "0";
public static String boundary = "\n\t\n";
public static String[] scope = new String[]{
"any",
"any header",
"any body",
"response",
"response line",
"response header",
"response body",
"request",
"request line",
"request header",
"request body"
};
public static String scopeOptions = "Suite|Target|Proxy|Scanner|Intruder|Repeater|Logger|Sequencer|Decoder|Comparer|Extensions|Organizer|Recorded login replayer";
public static String modeStatus = "true";
public static String[] ruleFields = {
"Loaded", "Name", "F-Regex", "S-Regex", "Format", "Color", "Scope", "Engine", "Sensitive"
};
public static Object[][] ruleTemplate = new Object[][]{
{
false, "New Name", "(First Regex)", "(Second Regex)", "{0}", "gray", "any", "nfa", false
}
};
public static String[] engine = new String[]{
"nfa",
"dfa"
};
public static String[] color = new String[]{
"red",
"orange",
"yellow",
"green",
"cyan",
"blue",
"pink",
"magenta",
"gray"
};
public static Map<String, Object[][]> globalRules = new HashMap<>();
public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>();
}

View File

@@ -0,0 +1,60 @@
package hae;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.extension.ExtensionUnloadingHandler;
import burp.api.montoya.logging.Logging;
import hae.cache.CachePool;
import hae.component.Main;
import hae.component.board.message.MessageTableModel;
import hae.instances.editor.RequestEditor;
import hae.instances.editor.ResponseEditor;
import hae.instances.editor.WebSocketEditor;
import hae.instances.websocket.WebSocketMessageHandler;
import hae.utils.ConfigLoader;
import hae.utils.DataManager;
public class HaE implements BurpExtension {
@Override
public void initialize(MontoyaApi api) {
// 设置扩展名称
String version = "4.0";
api.extension().setName("HaE - Highlighter and Extractor");
// 加载扩展后输出的项目信息
Logging logging = api.logging();
logging.logToOutput("[ HACK THE WORLD - TO DO IT ]");
logging.logToOutput("[#] Author: EvilChen && 0chencc && vaycore");
logging.logToOutput("[#] Github: https://github.com/gh0stkey/HaE");
logging.logToOutput("[#] Version: " + version);
// 配置文件加载
ConfigLoader configLoader = new ConfigLoader(api);
MessageTableModel messageTableModel = new MessageTableModel(api, configLoader);
// 注册Tab页用于查询数据
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
// 注册WebSocket处理器
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));
// 注册消息编辑框(用于展示数据)
api.userInterface().registerHttpRequestEditorProvider(new RequestEditor(api, configLoader));
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader));
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api, configLoader));
// 从BurpSuite里加载数据
DataManager dataManager = new DataManager(api);
dataManager.loadData(messageTableModel);
api.extension().registerUnloadingHandler(new ExtensionUnloadingHandler() {
@Override
public void extensionUnloaded() {
// 卸载清空数据
Config.globalDataMap.clear();
CachePool.clear();
}
});
}
}

34
src/main/java/hae/cache/CachePool.java vendored Normal file
View File

@@ -0,0 +1,34 @@
package hae.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class CachePool {
private static final int MAX_SIZE = 100000;
private static final int EXPIRE_DURATION = 5;
private static final Cache<String, Map<String, Map<String, Object>>> cache =
Caffeine.newBuilder()
.maximumSize(MAX_SIZE)
.expireAfterWrite(EXPIRE_DURATION, TimeUnit.HOURS)
.build();
public static void put(String key, Map<String, Map<String, Object>> value) {
cache.put(key, value);
}
public static Map<String, Map<String, Object>> get(String key) {
return cache.getIfPresent(key);
}
public static void remove(String key) {
cache.invalidate(key);
}
public static void clear() {
cache.invalidateAll();
}
}

View File

@@ -0,0 +1,437 @@
package hae.component;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Registration;
import hae.component.board.message.MessageTableModel;
import hae.component.rule.Rules;
import hae.instances.http.HttpMessageActiveHandler;
import hae.instances.http.HttpMessagePassiveHandler;
import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.*;
import java.util.List;
import java.util.*;
public class Config extends JPanel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final MessageTableModel messageTableModel;
private final Rules rules;
private Registration activeHandler;
private Registration passiveHandler;
public Config(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel, Rules rules) {
this.api = api;
this.configLoader = configLoader;
this.messageTableModel = messageTableModel;
this.rules = rules;
this.activeHandler = api.http().registerHttpHandler(new HttpMessageActiveHandler(api, configLoader, messageTableModel));
this.passiveHandler = api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel));
initComponents();
}
private void initComponents() {
setLayout(new BorderLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
JPanel ruleInfoPanel = new JPanel(new GridBagLayout());
ruleInfoPanel.setBorder(new EmptyBorder(10, 15, 5, 15));
JLabel ruleLabel = new JLabel("Path:");
JTextField pathTextField = new JTextField();
pathTextField.setEditable(false);
pathTextField.setText(configLoader.getRulesFilePath());
JButton reloadButton = new JButton("Reload");
JButton reinitButton = new JButton("Reinit");
ruleInfoPanel.add(ruleLabel);
ruleInfoPanel.add(pathTextField, constraints);
ruleInfoPanel.add(Box.createHorizontalStrut(5));
ruleInfoPanel.add(reinitButton);
ruleInfoPanel.add(Box.createHorizontalStrut(5));
ruleInfoPanel.add(reloadButton);
reloadButton.addActionListener(this::reloadActionPerformed);
reinitButton.addActionListener(this::reinitActionPerformed);
constraints.gridx = 1;
JTabbedPane configTabbedPanel = new JTabbedPane();
String[] settingMode = new String[]{"Exclude suffix", "Block host", "Exclude status"};
JPanel settingPanel = createConfigTablePanel(settingMode);
JPanel northPanel = new JPanel(new BorderLayout());
JPanel modePanel = getModePanel();
JScrollPane modeScrollPane = new JScrollPane(modePanel);
modeScrollPane.setBorder(new TitledBorder("Mode"));
JTextField limitPanel = getLimitPanel();
JScrollPane limitScrollPane = new JScrollPane(limitPanel);
limitScrollPane.setBorder(new TitledBorder("Limit Size (MB)"));
JSplitPane northTopPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, modeScrollPane, limitScrollPane);
northTopPanel.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
northTopPanel.setDividerLocation(0.5);
}
});
JPanel scopePanel = getScopePanel();
JScrollPane scopeScrollPane = new JScrollPane(scopePanel);
scopeScrollPane.setBorder(new TitledBorder("Scope"));
northPanel.add(scopeScrollPane, BorderLayout.SOUTH);
northPanel.add(northTopPanel, BorderLayout.NORTH);
settingPanel.add(northPanel, BorderLayout.NORTH);
configTabbedPanel.add("Setting", settingPanel);
add(ruleInfoPanel, BorderLayout.NORTH);
add(configTabbedPanel, BorderLayout.CENTER);
}
private JPanel getScopePanel() {
JPanel scopePanel = new JPanel();
scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.X_AXIS));
scopePanel.setBorder(new EmptyBorder(3, 0, 6, 0));
String[] scopeInit = hae.Config.scopeOptions.split("\\|");
String[] scopeMode = configLoader.getScope().split("\\|");
for (String scope : scopeInit) {
JCheckBox checkBox = new JCheckBox(scope);
scopePanel.add(checkBox);
checkBox.addActionListener(e -> updateScope(checkBox));
for (String mode : scopeMode) {
if (scope.equals(mode)) {
checkBox.setSelected(true);
}
}
updateScope(checkBox);
}
return scopePanel;
}
private JPanel getModePanel() {
JPanel modePanel = new JPanel();
modePanel.setLayout(new BoxLayout(modePanel, BoxLayout.X_AXIS));
JCheckBox checkBox = new JCheckBox("Enable active http message handler");
modePanel.add(checkBox);
checkBox.addActionListener(e -> updateModeStatus(checkBox));
checkBox.setSelected(configLoader.getMode());
updateModeStatus(checkBox);
return modePanel;
}
private JTextField getLimitPanel() {
JTextField limitSizeTextField = new JTextField();
limitSizeTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
onTextChange();
}
@Override
public void removeUpdate(DocumentEvent e) {
onTextChange();
}
@Override
public void changedUpdate(DocumentEvent e) {
onTextChange();
}
private void onTextChange() {
String limitSizeText = limitSizeTextField.getText();
configLoader.setLimitSize(limitSizeText);
}
});
limitSizeTextField.setText(configLoader.getLimitSize());
return limitSizeTextField;
}
private TableModelListener craeteSettingTableModelListener(JComboBox<String> setTypeComboBox, DefaultTableModel model) {
return new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
String selected = (String) setTypeComboBox.getSelectedItem();
String values = getFirstColumnDataAsString(model);
if (selected.equals("Exclude suffix")) {
if (!values.equals(configLoader.getExcludeSuffix()) && !values.isEmpty()) {
configLoader.setExcludeSuffix(values);
}
}
if (selected.equals("Block host")) {
if (!values.equals(configLoader.getBlockHost()) && !values.isEmpty()) {
configLoader.setBlockHost(values);
}
}
if (selected.equals("Exclude status")) {
if (!values.equals(configLoader.getExcludeStatus()) && !values.isEmpty()) {
configLoader.setExcludeStatus(values);
}
}
}
};
}
private ActionListener createSettingActionListener(JComboBox<String> setTypeComboBox, DefaultTableModel model) {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String selected = (String) setTypeComboBox.getSelectedItem();
model.setRowCount(0);
if (selected.equals("Exclude suffix")) {
addDataToTable(configLoader.getExcludeSuffix().replaceAll("\\|", "\r\n"), model);
}
if (selected.equals("Block host")) {
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
}
if (selected.equals("Exclude status")) {
addDataToTable(configLoader.getExcludeStatus().replaceAll("\\|", "\r\n"), model);
}
}
};
}
private JPanel createConfigTablePanel(String[] mode) {
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
JPanel settingPanel = new JPanel(new BorderLayout());
DefaultTableModel model = new DefaultTableModel();
JTable table = new JTable(model);
model.addColumn("Value");
JScrollPane scrollPane = new JScrollPane(table);
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(new EmptyBorder(0, 3, 0, 0));
GridBagLayout layout = new GridBagLayout();
layout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0};
layout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
buttonPanel.setLayout(layout);
JPanel inputPanel = new JPanel(new BorderLayout());
JPanel inputPanelB = new JPanel(new BorderLayout());
inputPanelB.setBorder(new EmptyBorder(0, 0, 3, 0));
JButton addButton = new JButton("Add");
JButton removeButton = new JButton("Remove");
JButton pasteButton = new JButton("Paste");
JButton clearButton = new JButton("Clear");
JComboBox<String> setTypeComboBox = new JComboBox<>();
setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode));
model.addTableModelListener(craeteSettingTableModelListener(setTypeComboBox, model));
setTypeComboBox.addActionListener(createSettingActionListener(setTypeComboBox, model));
setTypeComboBox.setSelectedItem(mode[0]);
constraints.insets = new Insets(0, 0, 3, 0);
constraints.gridy = 0;
buttonPanel.add(setTypeComboBox, constraints);
constraints.gridy = 1;
buttonPanel.add(addButton, constraints);
constraints.gridy = 2;
buttonPanel.add(removeButton, constraints);
constraints.gridy = 3;
buttonPanel.add(pasteButton, constraints);
constraints.gridy = 4;
buttonPanel.add(clearButton, constraints);
JTextField addTextField = new JTextField();
String defaultText = "Enter a new item";
UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText);
inputPanelB.add(addTextField, BorderLayout.CENTER);
inputPanel.add(scrollPane, BorderLayout.CENTER);
inputPanel.add(inputPanelB, BorderLayout.NORTH);
settingPanel.add(buttonPanel, BorderLayout.EAST);
settingPanel.add(inputPanel, BorderLayout.CENTER);
addButton.addActionListener(e -> addActionPerformed(e, model, addTextField, setTypeComboBox.getSelectedItem().toString()));
addTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
addActionPerformed(null, model, addTextField, setTypeComboBox.getSelectedItem().toString());
}
}
});
pasteButton.addActionListener(e -> {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
try {
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
if (data != null && !data.isEmpty()) {
addDataToTable(data, model);
}
} catch (Exception ignored) {
}
});
removeButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
model.removeRow(selectedRow);
}
});
clearButton.addActionListener(e -> model.setRowCount(0));
JPanel settingMainPanel = new JPanel(new BorderLayout());
settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15));
JScrollPane settingScroller = new JScrollPane(settingPanel);
settingScroller.setBorder(new TitledBorder("Setting"));
settingMainPanel.add(settingScroller, BorderLayout.CENTER);
return settingMainPanel;
}
private String getFirstColumnDataAsString(DefaultTableModel model) {
StringBuilder firstColumnData = new StringBuilder();
int numRows = model.getRowCount();
for (int row = 0; row < numRows; row++) {
firstColumnData.append(model.getValueAt(row, 0));
if (row < numRows - 1) {
firstColumnData.append("|");
}
}
return firstColumnData.toString();
}
private void addDataToTable(String data, DefaultTableModel model) {
if (!data.isBlank()) {
String[] rows = data.split("\\r?\\n");
for (String row : rows) {
model.addRow(new String[]{row});
}
deduplicateTableData(model);
}
}
private void deduplicateTableData(DefaultTableModel model) {
// 使用 Map 存储每一行的数据,用于去重
Set<List<Object>> rowData = new LinkedHashSet<>();
int columnCount = model.getColumnCount();
// 将每一行数据作为一个列表,添加到 Set 中
for (int i = 0; i < model.getRowCount(); i++) {
List<Object> row = new ArrayList<>();
for (int j = 0; j < columnCount; j++) {
row.add(model.getValueAt(i, j));
}
rowData.add(row);
}
// 清除原始数据
model.setRowCount(0);
// 将去重后的数据添加回去
for (List<Object> uniqueRow : rowData) {
model.addRow(uniqueRow.toArray());
}
}
public void updateModeStatus(JCheckBox checkBox) {
boolean selected = checkBox.isSelected();
configLoader.setMode(selected ? "true" : "false");
if (checkBox.isSelected()) {
if (passiveHandler.isRegistered()) {
passiveHandler.deregister();
}
if (!activeHandler.isRegistered()) {
activeHandler = api.http().registerHttpHandler(new HttpMessageActiveHandler(api, configLoader, messageTableModel));
}
} else {
if (!passiveHandler.isRegistered()) {
passiveHandler = api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel));
}
if (activeHandler.isRegistered()) {
activeHandler.deregister();
}
}
}
public void updateScope(JCheckBox checkBox) {
String boxText = checkBox.getText();
boolean selected = checkBox.isSelected();
Set<String> HaEScope = new HashSet<>(Arrays.asList(configLoader.getScope().split("\\|")));
if (selected) {
HaEScope.add(boxText);
} else {
HaEScope.remove(boxText);
}
configLoader.setScope(String.join("|", HaEScope));
}
private void addActionPerformed(ActionEvent e, DefaultTableModel model, JTextField addTextField, String comboBoxSelected) {
String addTextFieldText = addTextField.getText();
if (addTextField.getForeground().equals(Color.BLACK)) {
addDataToTable(addTextFieldText, model);
addTextField.setText("");
addTextField.requestFocusInWindow();
}
}
private void reloadActionPerformed(ActionEvent e) {
rules.reloadRuleGroup();
}
private void reinitActionPerformed(ActionEvent e) {
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to reinitialize rules? This action will overwrite your existing rules.", "Info", JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) {
boolean ret = configLoader.initRules();
if (ret) {
rules.reloadRuleGroup();
}
}
}
}

View File

@@ -0,0 +1,86 @@
package hae.component;
import burp.api.montoya.MontoyaApi;
import hae.component.board.Databoard;
import hae.component.board.message.MessageTableModel;
import hae.component.rule.Rules;
import hae.utils.ConfigLoader;
import javax.swing.*;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
public class Main extends JPanel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final MessageTableModel messageTableModel;
public Main(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
this.messageTableModel = messageTableModel;
initComponents();
}
private void initComponents() {
setLayout(new GridBagLayout());
((GridBagLayout) getLayout()).columnWidths = new int[]{0, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{1.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{1.0, 1.0E-4};
JTabbedPane mainTabbedPane = new JTabbedPane();
// 新增Logo
JTabbedPane HaETabbedPane = new JTabbedPane();
boolean isDarkBg = isDarkBg(HaETabbedPane);
HaETabbedPane.addTab("", getImageIcon(isDarkBg), mainTabbedPane);
// 中文Slogan赋能白帽高效作战
HaETabbedPane.addTab(" Highlighter and Extractor - Empower ethical hacker for efficient operations. ", null);
HaETabbedPane.setEnabledAt(1, false);
HaETabbedPane.addPropertyChangeListener("background", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
boolean isDarkBg = isDarkBg(HaETabbedPane);
HaETabbedPane.setIconAt(0, getImageIcon(isDarkBg));
}
});
add(HaETabbedPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
// 依次添加Rules、Config、Databoard
Rules rules = new Rules(api, configLoader);
mainTabbedPane.addTab("Rules", rules);
mainTabbedPane.addTab("Databoard", new Databoard(api, configLoader, messageTableModel));
mainTabbedPane.addTab("Config", new Config(api, configLoader, messageTableModel, rules));
}
private boolean isDarkBg(JTabbedPane HaETabbedPane) {
Color bg = HaETabbedPane.getBackground();
int r = bg.getRed();
int g = bg.getGreen();
int b = bg.getBlue();
int avg = (r + g + b) / 3;
return avg < 128;
}
private ImageIcon getImageIcon(boolean isDark) {
ClassLoader classLoader = getClass().getClassLoader();
URL imageURL;
if (isDark) {
imageURL = classLoader.getResource("logo/logo.png");
} else {
imageURL = classLoader.getResource("logo/logo_black.png");
}
ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage();
Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST);
return new ImageIcon(scaledImage);
}
}

View File

@@ -0,0 +1,365 @@
package hae.component.board;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import hae.component.board.message.MessageTableModel;
import hae.component.board.message.MessageTableModel.MessageTable;
import hae.component.board.table.Datatable;
import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class Databoard extends JPanel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final MessageTableModel messageTableModel;
private JTextField hostTextField;
private JTabbedPane dataTabbedPane;
private JSplitPane splitPane;
private MessageTable messageTable;
private static Boolean isMatchHost = false;
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
private final JComboBox hostComboBox = new JComboBox(comboBoxModel);
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
private SwingWorker<Void, Void> applyHostFilterWorker;
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
this.messageTableModel = messageTableModel;
initComponents();
}
private void initComponents() {
setLayout(new GridBagLayout());
((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 1.0E-4};
JLabel hostLabel = new JLabel("Host:");
JButton clearButton = new JButton("Clear");
JButton actionButton = new JButton("Action");
JPanel menuPanel = new JPanel(new GridLayout(1, 1, 0, 5));
menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JPopupMenu menu = new JPopupMenu();
menuPanel.add(clearButton);
menu.add(menuPanel);
hostTextField = new JTextField();
String defaultText = "Please enter the host";
UIEnhancer.setTextFieldPlaceholder(hostTextField, defaultText);
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
dataTabbedPane.setPreferredSize(new Dimension(500, 0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
actionButton.addActionListener(e -> {
int x = 0;
int y = actionButton.getHeight();
menu.show(actionButton, x, y);
});
clearButton.addActionListener(this::clearActionPerformed);
splitPane.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
resizePanel();
}
});
splitPane.setVisible(false);
add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 1.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 5, 0, 5), 0, 0));
hostComboBox.setMaximumRowCount(5);
add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
setAutoMatch();
}
private void resizePanel() {
splitPane.setDividerLocation(0.4);
TableColumnModel columnModel = messageTable.getColumnModel();
int totalWidth = (int) (getWidth() * 0.6);
columnModel.getColumn(0).setPreferredWidth((int) (totalWidth * 0.1));
columnModel.getColumn(1).setPreferredWidth((int) (totalWidth * 0.3));
columnModel.getColumn(2).setPreferredWidth((int) (totalWidth * 0.3));
columnModel.getColumn(3).setPreferredWidth((int) (totalWidth * 0.1));
columnModel.getColumn(4).setPreferredWidth((int) (totalWidth * 0.1));
columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1));
}
private void setAutoMatch() {
hostComboBox.setSelectedItem(null);
hostComboBox.addActionListener(this::handleComboBoxAction);
hostTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
handleKeyEvents(e);
}
});
hostTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
filterComboBoxList();
}
@Override
public void removeUpdate(DocumentEvent e) {
filterComboBoxList();
}
@Override
public void changedUpdate(DocumentEvent e) {
filterComboBoxList();
}
});
}
private void handleComboBoxAction(ActionEvent e) {
if (!isMatchHost && hostComboBox.getSelectedItem() != null) {
String selectedHost = hostComboBox.getSelectedItem().toString();
if (getHostByList().contains(selectedHost)) {
hostTextField.setText(selectedHost);
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
handleComboBoxWorker.cancel(true);
}
handleComboBoxWorker = new SwingWorker<Map<String, List<String>>, Void>() {
@Override
protected Map<String, List<String>> doInBackground() {
return getSelectedMapByHost(selectedHost);
}
@Override
protected void done() {
if (!isCancelled()) {
try {
Map<String, List<String>> selectedDataMap = get();
if (!selectedDataMap.isEmpty()) {
dataTabbedPane.removeAll();
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
Datatable datatablePanel = new Datatable(api, configLoader, entry.getKey(), entry.getValue());
datatablePanel.setTableListener(messageTableModel);
dataTabbedPane.addTab(tabTitle, datatablePanel);
}
JSplitPane messageSplitPane = messageTableModel.getSplitPane();
splitPane.setLeftComponent(dataTabbedPane);
splitPane.setRightComponent(messageSplitPane);
messageTable = messageTableModel.getMessageTable();
resizePanel();
splitPane.setVisible(true);
hostTextField.setText(selectedHost);
hostComboBox.setPopupVisible(false);
applyHostFilter(selectedHost);
}
} catch (Exception ignored) {
}
}
}
};
handleComboBoxWorker.execute();
}
}
}
private void handleKeyEvents(KeyEvent e) {
isMatchHost = true;
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE && hostComboBox.isPopupVisible()) {
e.setKeyCode(KeyEvent.VK_ENTER);
}
if (Arrays.asList(KeyEvent.VK_DOWN, KeyEvent.VK_UP).contains(keyCode)) {
hostComboBox.dispatchEvent(e);
}
if (keyCode == KeyEvent.VK_ENTER) {
isMatchHost = false;
handleComboBoxAction(null);
}
if (keyCode == KeyEvent.VK_ESCAPE) {
hostComboBox.setPopupVisible(false);
}
isMatchHost = false;
}
private Map<String, List<String>> getSelectedMapByHost(String selectedHost) {
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
Map<String, List<String>> selectedDataMap;
if (selectedHost.contains("*")) {
selectedDataMap = new HashMap<>();
dataMap.keySet().forEach(key -> {
if ((StringProcessor.matchesHostPattern(key, selectedHost) || selectedHost.equals("*")) && !key.contains("*")) {
Map<String, List<String>> ruleMap = dataMap.get(key);
for (String ruleKey : ruleMap.keySet()) {
List<String> dataList = ruleMap.get(ruleKey);
if (selectedDataMap.containsKey(ruleKey)) {
List<String> mergedList = new ArrayList<>(selectedDataMap.get(ruleKey));
mergedList.addAll(dataList);
HashSet<String> uniqueSet = new HashSet<>(mergedList);
selectedDataMap.put(ruleKey, new ArrayList<>(uniqueSet));
} else {
selectedDataMap.put(ruleKey, dataList);
}
}
}
});
} else {
selectedDataMap = dataMap.get(selectedHost);
}
return selectedDataMap;
}
private void filterComboBoxList() {
isMatchHost = true;
comboBoxModel.removeAllElements();
String input = hostTextField.getText().toLowerCase();
if (!input.isEmpty()) {
for (String host : getHostByList()) {
String lowerCaseHost = host.toLowerCase();
if (lowerCaseHost.contains(input)) {
if (lowerCaseHost.equals(input)) {
comboBoxModel.insertElementAt(lowerCaseHost, 0);
comboBoxModel.setSelectedItem(lowerCaseHost);
} else {
comboBoxModel.addElement(host);
}
}
}
}
hostComboBox.setPopupVisible(comboBoxModel.getSize() > 0);
isMatchHost = false;
}
private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) messageTable.getRowSorter();
String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", "");
if (applyHostFilterWorker != null && !applyHostFilterWorker.isDone()) {
applyHostFilterWorker.cancel(true);
}
applyHostFilterWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
public boolean include(Entry<?, ?> entry) {
if (cleanedText.equals("*")) {
return true;
} else {
String host = StringProcessor.getHostByUrl((String) entry.getValue(1));
return StringProcessor.matchesHostPattern(host, filterText);
}
}
};
sorter.setRowFilter(rowFilter);
messageTableModel.applyHostFilter(filterText);
return null;
}
};
applyHostFilterWorker.execute();
}
private List<String> getHostByList() {
if (!Config.globalDataMap.keySet().isEmpty()) {
return new ArrayList<>(Config.globalDataMap.keySet());
}
return new ArrayList<>();
}
private void clearActionPerformed(ActionEvent e) {
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to clear data?", "Info",
JOptionPane.YES_NO_OPTION);
String host = hostTextField.getText();
if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) {
dataTabbedPane.removeAll();
splitPane.setVisible(false);
Config.globalDataMap.keySet().parallelStream().forEach(key -> {
if (StringProcessor.matchesHostPattern(key, host) || host.equals("*")) {
Config.globalDataMap.remove(key);
}
});
// 删除无用的数据
Set<String> wildcardKeys = Config.globalDataMap.keySet().stream()
.filter(key -> key.startsWith("*."))
.collect(Collectors.toSet());
Set<String> existingSuffixes = Config.globalDataMap.keySet().stream()
.filter(key -> !key.startsWith("*."))
.map(key -> {
int dotIndex = key.indexOf(".");
return dotIndex != -1 ? key.substring(dotIndex) : "";
})
.collect(Collectors.toSet());
Set<String> keysToRemove = wildcardKeys.stream()
.filter(key -> !existingSuffixes.contains(key.substring(1)))
.collect(Collectors.toSet());
keysToRemove.forEach(Config.globalDataMap::remove);
if (Config.globalDataMap.keySet().size() == 1 && Config.globalDataMap.keySet().stream().anyMatch(key -> key.equals("*"))) {
Config.globalDataMap.keySet().remove("*");
}
messageTableModel.deleteByHost(host);
hostTextField.setText("");
}
}
}

View File

@@ -0,0 +1,52 @@
package hae.component.board.message;
import burp.api.montoya.http.message.HttpRequestResponse;
public class MessageEntry {
private final String comment;
private final HttpRequestResponse requestResponse;
private final String url;
private final String length;
private final String status;
private final String color;
private final String method;
MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status) {
this.requestResponse = requestResponse;
this.method = method;
this.url = url;
this.comment = comment;
this.length = length;
this.color = color;
this.status = status;
}
public String getColor() {
return this.color;
}
public String getUrl() {
return this.url;
}
public String getLength() {
return this.length;
}
public String getComment() {
return this.comment;
}
public String getMethod() {
return this.method;
}
public String getStatus() {
return this.status;
}
public HttpRequestResponse getRequestResponse() {
return this.requestResponse;
}
}

View File

@@ -0,0 +1,66 @@
package hae.component.board.message;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
public class MessageRenderer extends DefaultTableCellRenderer {
private final LinkedList<MessageEntry> log;
private final Map<String, Color> colorMap = new HashMap<>();
private final JTable table; // 保存对表格的引用
public MessageRenderer(LinkedList<MessageEntry> log, JTable table) {
this.log = log;
// 与BurpSuite的颜色保持一致
this.colorMap.put("red", new Color(0xFF, 0x64, 0x64));
this.colorMap.put("orange", new Color(0xFF, 0xC8, 0x64));
this.colorMap.put("yellow", new Color(0xFF, 0xFF, 0x64));
this.colorMap.put("green", new Color(0x64, 0xFF, 0x64));
this.colorMap.put("cyan", new Color(0x64, 0xFF, 0xFF));
this.colorMap.put("blue", new Color(0x64, 0x64, 0xFF));
this.colorMap.put("pink", new Color(0xFF, 0xC8, 0xC8));
this.colorMap.put("magenta", new Color(0xFF, 0x64, 0xFF));
this.colorMap.put("gray", new Color(0xB4, 0xB4, 0xB4));
this.table = table;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
MessageEntry messageEntry = log.get(table.convertRowIndexToModel(row)); // 使用convertRowIndexToModel方法转换行索引
// 设置颜色
String colorByLog = messageEntry.getColor();
Color color = colorMap.get(colorByLog);
if (isSelected) {
// 通过更改RGB颜色来达成阴影效果
component.setBackground(new Color(color.getRed() - 0x20, color.getGreen() - 0x20, color.getBlue() - 0x20));
} else {
// 否则使用原始颜色
component.setBackground(color);
}
component.setForeground(Color.BLACK);
return component;
}
@Override
public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
super.firePropertyChange(propertyName, oldValue, newValue);
// 监听表格排序的属性变化
if ("tableCellRenderer".equals(propertyName)) {
// 更新每一行数据的颜色
for (int i = 0; i < table.getRowCount(); i++) {
table.repaint(table.getCellRect(i, 0, true));
}
}
}
}

View File

@@ -0,0 +1,462 @@
package hae.component.board.message;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpHeader;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.persistence.PersistedObject;
import burp.api.montoya.ui.UserInterface;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor;
import hae.Config;
import hae.cache.CachePool;
import hae.utils.ConfigLoader;
import hae.utils.DataManager;
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static burp.api.montoya.ui.editor.EditorOptions.READ_ONLY;
public class MessageTableModel extends AbstractTableModel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final MessageTable messageTable;
private final JSplitPane splitPane;
private final LinkedList<MessageEntry> log = new LinkedList<>();
private final LinkedList<MessageEntry> filteredLog;
private SwingWorker<Void, Void> currentWorker;
public MessageTableModel(MontoyaApi api, ConfigLoader configLoader) {
this.filteredLog = new LinkedList<>();
this.api = api;
this.configLoader = configLoader;
JTabbedPane messageTab = new JTabbedPane();
UserInterface userInterface = api.userInterface();
HttpRequestEditor requestViewer = userInterface.createHttpRequestEditor(READ_ONLY);
HttpResponseEditor responseViewer = userInterface.createHttpResponseEditor(READ_ONLY);
messageTab.addTab("Request", requestViewer.uiComponent());
messageTab.addTab("Response", responseViewer.uiComponent());
// 请求条目表格
messageTable = new MessageTable(MessageTableModel.this, requestViewer, responseViewer);
messageTable.setDefaultRenderer(Object.class, new MessageRenderer(filteredLog, messageTable));
messageTable.setAutoCreateRowSorter(true);
// Length字段根据大小进行排序
TableRowSorter<DefaultTableModel> sorter = (TableRowSorter<DefaultTableModel>) messageTable.getRowSorter();
sorter.setComparator(4, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
Integer age1 = Integer.parseInt(s1);
Integer age2 = Integer.parseInt(s2);
return age1.compareTo(age2);
}
});
// Color字段根据颜色顺序进行排序
sorter.setComparator(5, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
int index1 = getIndex(s1);
int index2 = getIndex(s2);
return Integer.compare(index1, index2);
}
private int getIndex(String color) {
for (int i = 0; i < Config.color.length; i++) {
if (Config.color[i].equals(color)) {
return i;
}
}
return -1;
}
});
messageTable.setRowSorter(sorter);
messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
// 请求/相应文本框
JScrollPane scrollPane = new JScrollPane(messageTable);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
splitPane.setLeftComponent(scrollPane);
splitPane.setRightComponent(messageTab);
}
public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, boolean flag) {
synchronized (log) {
boolean isDuplicate = false;
MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status);
byte[] reqByteA = new byte[0];
byte[] resByteA = new byte[0];
if (messageInfo != null) {
HttpRequest httpRequest = messageInfo.request();
HttpResponse httpResponse = messageInfo.response();
reqByteA = httpRequest.toByteArray().getBytes();
resByteA = httpResponse.toByteArray().getBytes();
}
// 比较Hash如若存在重复的请求或响应则不放入消息内容里
try {
if (!log.isEmpty()) {
for (MessageEntry entry : log) {
HttpRequestResponse reqResMessage = entry.getRequestResponse();
byte[] reqByteB = reqResMessage.request().toByteArray().getBytes();
byte[] resByteB = reqResMessage.response().toByteArray().getBytes();
try {
// 通过URL、请求和响应报文、匹配数据内容多维度进行对比
if ((entry.getUrl().equals(url) || (Arrays.equals(reqByteB, reqByteA) || Arrays.equals(resByteB, resByteA))) && (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA)))) {
isDuplicate = true;
break;
}
} catch (Exception ignored) {
}
}
}
} catch (Exception ignored) {
}
if (!isDuplicate) {
if (flag) {
DataManager dataManager = new DataManager(api);
// 数据存储在BurpSuite空间内
PersistedObject persistedObject = PersistedObject.persistedObject();
persistedObject.setHttpRequestResponse("messageInfo", messageInfo);
persistedObject.setString("comment", comment);
persistedObject.setString("color", color);
String uuidIndex = StringProcessor.getRandomUUID();
dataManager.putData("message", uuidIndex, persistedObject);
}
// 添加进日志
log.add(logEntry);
}
}
}
public void deleteByHost(String filterText) {
filteredLog.clear();
List<Integer> rowsToRemove = new ArrayList<>();
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
}
currentWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
for (int i = 0; i < log.size(); i++) {
MessageEntry entry = log.get(i);
String host = StringProcessor.getHostByUrl(entry.getUrl());
if (!host.isEmpty()) {
if (StringProcessor.matchesHostPattern(host, filterText) || filterText.equals("*")) {
rowsToRemove.add(i);
}
}
}
for (int i = rowsToRemove.size() - 1; i >= 0; i--) {
int row = rowsToRemove.get(i);
log.remove(row);
}
return null;
}
};
currentWorker.execute();
}
public void applyHostFilter(String filterText) {
filteredLog.clear();
log.forEach(entry -> {
String host = StringProcessor.getHostByUrl(entry.getUrl());
if (!host.isEmpty()) {
if (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*")) {
filteredLog.add(entry);
}
}
});
fireTableDataChanged();
}
public void applyMessageFilter(String tableName, String filterText) {
filteredLog.clear();
for (MessageEntry entry : log) {
// 标志变量,表示是否满足过滤条件
AtomicBoolean isMatched = new AtomicBoolean(false);
HttpRequestResponse requestResponse = entry.getRequestResponse();
HttpRequest httpRequest = requestResponse.request();
HttpResponse httpResponse = requestResponse.response();
String requestString = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8);
String requestBody = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8);
String requestHeaders = httpRequest.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
String responseString = new String(httpResponse.toByteArray().getBytes(), StandardCharsets.UTF_8);
String responseBody = new String(httpResponse.body().getBytes(), StandardCharsets.UTF_8);
String responseHeaders = httpResponse.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
Config.globalRules.keySet().forEach(i -> {
for (Object[] objects : Config.globalRules.get(i)) {
String name = objects[1].toString();
String format = objects[4].toString();
String scope = objects[6].toString();
// 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间
if (entry.getComment().contains(name)) {
if (name.equals(tableName)) {
// 标志变量,表示当前规则是否匹配
boolean isMatch = false;
switch (scope) {
case "any":
isMatch = matchingString(format, filterText, requestString) || matchingString(format, filterText, responseString);
break;
case "request":
isMatch = matchingString(format, filterText, requestString);
break;
case "response":
isMatch = matchingString(format, filterText, responseString);
break;
case "any header":
isMatch = matchingString(format, filterText, requestHeaders) || matchingString(format, filterText, responseHeaders);
break;
case "request header":
isMatch = matchingString(format, filterText, requestHeaders);
break;
case "response header":
isMatch = matchingString(format, filterText, responseHeaders);
break;
case "any body":
isMatch = matchingString(format, filterText, requestBody) || matchingString(format, filterText, responseBody);
break;
case "request body":
isMatch = matchingString(format, filterText, requestBody);
break;
case "response body":
isMatch = matchingString(format, filterText, responseBody);
break;
case "request line":
String requestLine = requestString.split("\\r?\\n", 2)[0];
isMatch = matchingString(format, filterText, requestLine);
break;
case "response line":
String responseLine = responseString.split("\\r?\\n", 2)[0];
isMatch = matchingString(format, filterText, responseLine);
break;
default:
break;
}
isMatched.set(isMatch);
break;
}
}
}
});
if (isMatched.get()) {
filteredLog.add(entry);
}
}
fireTableDataChanged();
messageTable.lastSelectedIndex = -1;
}
private boolean matchingString(String format, String filterText, String target) {
boolean isMatch = true;
try {
MessageFormat mf = new MessageFormat(format);
Object[] parsedObjects = mf.parse(filterText);
for (Object parsedObject : parsedObjects) {
if (!target.contains(parsedObject.toString())) {
isMatch = false;
break;
}
}
} catch (Exception e) {
isMatch = false;
}
return isMatch;
}
private Map<String, Map<String, Object>> getCacheData(byte[] content) {
String hashIndex = HashCalculator.calculateHash(content);
return CachePool.get(hashIndex);
}
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
if (map1 == null || map2 == null) {
return false;
}
if (map1.size() != map2.size()) {
return false;
}
for (String key : map1.keySet()) {
if (!map2.containsKey(key)) {
return false;
}
if (areInnerMapsEqual(map1.get(key), map2.get(key))) {
return false;
}
}
return true;
}
private boolean areInnerMapsEqual(Map<String, Object> innerMap1, Map<String, Object> innerMap2) {
if (innerMap1.size() != innerMap2.size()) {
return true;
}
for (String key : innerMap1.keySet()) {
if (!innerMap2.containsKey(key)) {
return true;
}
Object value1 = innerMap1.get(key);
Object value2 = innerMap2.get(key);
// 如果值是Map则递归对比
if (value1 instanceof Map && value2 instanceof Map) {
if (areInnerMapsEqual((Map<String, Object>) value1, (Map<String, Object>) value2)) {
return true;
}
} else if (!value1.equals(value2)) {
return true;
}
}
return false;
}
public JSplitPane getSplitPane() {
return splitPane;
}
public MessageTable getMessageTable() {
return messageTable;
}
public LinkedList<MessageEntry> getLogs() {
return log;
}
@Override
public int getRowCount() {
return filteredLog.size();
}
@Override
public int getColumnCount() {
return 6;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (!filteredLog.isEmpty()) {
try {
MessageEntry messageEntry = filteredLog.get(rowIndex);
if (messageEntry != null) {
return switch (columnIndex) {
case 0 -> messageEntry.getMethod();
case 1 -> messageEntry.getUrl();
case 2 -> messageEntry.getComment();
case 3 -> messageEntry.getStatus();
case 4 -> messageEntry.getLength();
case 5 -> messageEntry.getColor();
default -> "";
};
}
} catch (Exception e) {
api.logging().logToError("getValueAt: " + e.getMessage());
}
}
return "";
}
@Override
public String getColumnName(int columnIndex) {
return switch (columnIndex) {
case 0 -> "Method";
case 1 -> "URL";
case 2 -> "Comment";
case 3 -> "Status";
case 4 -> "Length";
case 5 -> "Color";
default -> "";
};
}
public class MessageTable extends JTable {
private MessageEntry messageEntry;
private final ExecutorService executorService;
private int lastSelectedIndex = -1;
private final HttpRequestEditor requestEditor;
private final HttpResponseEditor responseEditor;
public MessageTable(TableModel messageTableModel, HttpRequestEditor requestEditor, HttpResponseEditor responseEditor) {
super(messageTableModel);
this.requestEditor = requestEditor;
this.responseEditor = responseEditor;
this.executorService = Executors.newSingleThreadExecutor();
}
@Override
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
super.changeSelection(row, col, toggle, extend);
int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex;
executorService.execute(this::getSelectedMessage);
}
}
private void getSelectedMessage() {
messageEntry = filteredLog.get(lastSelectedIndex);
HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), httpRequestResponse.request().toByteArray()));
int responseSizeWithMb = httpRequestResponse.response().toString().length() / 1024 / 1024;
if ((responseSizeWithMb < Integer.parseInt(configLoader.getLimitSize())) || configLoader.getLimitSize().equals("0")) {
responseEditor.setResponse(httpRequestResponse.response());
} else {
responseEditor.setResponse(HttpResponse.httpResponse("Exceeds length limit."));
}
}
}
}

View File

@@ -0,0 +1,289 @@
package hae.component.board.table;
import burp.api.montoya.MontoyaApi;
import hae.component.board.message.MessageTableModel;
import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
public class Datatable extends JPanel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final JTable dataTable;
private final DefaultTableModel dataTableModel;
private final JTextField searchField;
private final JTextField secondSearchField;
private final TableRowSorter<DefaultTableModel> sorter;
private final JCheckBox searchMode = new JCheckBox("Reverse search");
private final String tabName;
private final JPanel footerPanel;
public Datatable(MontoyaApi api, ConfigLoader configLoader, String tabName, List<String> dataList) {
this.api = api;
this.configLoader = configLoader;
this.tabName = tabName;
String[] columnNames = {"#", "Information"};
this.dataTableModel = new DefaultTableModel(columnNames, 0);
this.dataTable = new JTable(dataTableModel);
this.sorter = new TableRowSorter<>(dataTableModel);
this.searchField = new JTextField(10);
this.secondSearchField = new JTextField(10);
this.footerPanel = new JPanel(new BorderLayout(0, 5));
initComponents(dataList);
}
private void initComponents(List<String> dataList) {
// 设置ID排序
sorter.setComparator(0, new Comparator<Integer>() {
@Override
public int compare(Integer s1, Integer s2) {
return s1.compareTo(s2);
}
});
for (String item : dataList) {
if (!item.isEmpty()) {
addRowToTable(new Object[]{item});
}
}
UIEnhancer.setTextFieldPlaceholder(searchField, "Search");
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void removeUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void changedUpdate(DocumentEvent e) {
performSearch();
}
});
UIEnhancer.setTextFieldPlaceholder(secondSearchField, "Second search");
secondSearchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void removeUpdate(DocumentEvent e) {
performSearch();
}
@Override
public void changedUpdate(DocumentEvent e) {
performSearch();
}
});
// 设置布局
JScrollPane scrollPane = new JScrollPane(dataTable);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
dataTable.setRowSorter(sorter);
TableColumn idColumn = dataTable.getColumnModel().getColumn(0);
idColumn.setPreferredWidth(50);
idColumn.setMaxWidth(100);
setLayout(new BorderLayout(0, 5));
JPanel optionsPanel = new JPanel();
optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.X_AXIS));
// Settings按钮
JPanel settingMenuPanel = new JPanel(new GridLayout(1, 1));
settingMenuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JPopupMenu settingMenu = new JPopupMenu();
settingMenuPanel.add(searchMode);
searchMode.addItemListener(e -> performSearch());
settingMenu.add(settingMenuPanel);
JButton settingsButton = new JButton("Settings");
setMenuShow(settingMenu, settingsButton);
optionsPanel.add(settingsButton);
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(searchField);
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(secondSearchField);
footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));
footerPanel.add(optionsPanel, BorderLayout.CENTER);
add(scrollPane, BorderLayout.CENTER);
add(footerPanel, BorderLayout.SOUTH);
}
private void setMenuShow(JPopupMenu menu, JButton button) {
button.addActionListener(e -> {
Point buttonLocation = button.getLocationOnScreen();
Dimension menuSize = menu.getPreferredSize();
int x = buttonLocation.x + (button.getWidth() - menuSize.width) / 2;
int y = buttonLocation.y - menuSize.height;
menu.show(button, x - buttonLocation.x, y - buttonLocation.y);
});
}
private void addRowToTable(Object[] data) {
int rowCount = dataTableModel.getRowCount();
int id = rowCount > 0 ? (Integer) dataTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
Object[] rowData = new Object[data.length + 1];
rowData[0] = id;
System.arraycopy(data, 0, rowData, 1, data.length);
dataTableModel.addRow(rowData);
}
private void performSearch() {
RowFilter<Object, Object> firstRowFilter = applyFirstSearchFilter();
RowFilter<Object, Object> secondRowFilter = applySecondFilter();
if (searchField.getForeground().equals(Color.BLACK)) {
sorter.setRowFilter(firstRowFilter);
if (secondSearchField.getForeground().equals(Color.BLACK)) {
List<RowFilter<Object, Object>> filters = new ArrayList<>();
filters.add(firstRowFilter);
filters.add(secondRowFilter);
sorter.setRowFilter(RowFilter.andFilter(filters));
}
}
}
private RowFilter<Object, Object> applyFirstSearchFilter() {
return new RowFilter<Object, Object>() {
public boolean include(Entry<?, ?> entry) {
String searchFieldTextText = searchField.getText();
Pattern pattern = null;
try {
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
} catch (Exception ignored) {
}
String entryValue = ((String) entry.getValue(1)).toLowerCase();
searchFieldTextText = searchFieldTextText.toLowerCase();
if (pattern != null) {
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find() != searchMode.isSelected();
} else {
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText) != searchMode.isSelected();
}
}
};
}
private RowFilter<Object, Object> applySecondFilter() {
return new RowFilter<Object, Object>() {
public boolean include(Entry<?, ?> entry) {
String searchFieldTextText = secondSearchField.getText();
Pattern pattern = null;
try {
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
} catch (Exception ignored) {
}
String entryValue = ((String) entry.getValue(1)).toLowerCase();
searchFieldTextText = searchFieldTextText.toLowerCase();
if (pattern != null) {
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find();
} else {
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText);
}
}
};
}
public void setTableListener(MessageTableModel messagePanel) {
// 表格复制功能
dataTable.setTransferHandler(new TransferHandler() {
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
if (comp instanceof JTable) {
StringSelection stringSelection = new StringSelection(getSelectedDataAtTable((JTable) comp).replace("\0", "").replaceAll("[\\p{Cntrl}&&[^\r\n\t]]", ""));
clip.setContents(stringSelection, null);
} else {
super.exportToClipboard(comp, clip, action);
}
}
});
dataTable.setDefaultEditor(Object.class, null);
// 表格内容双击事件
dataTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = dataTable.getSelectedRow();
if (selectedRow != -1) {
String rowData = dataTable.getValueAt(selectedRow, 1).toString();
messagePanel.applyMessageFilter(tabName, rowData);
}
}
}
});
}
private String getTableData(JTable table) {
StringBuilder selectData = new StringBuilder();
int rowCount = table.getRowCount();
for (int i = 0; i < rowCount; i++) {
selectData.append(table.getValueAt(i, 1).toString()).append("\r\n");
}
if (!selectData.isEmpty()) {
selectData.delete(selectData.length() - 2, selectData.length());
} else {
return "";
}
return selectData.toString();
}
public String getSelectedDataAtTable(JTable table) {
int[] selectRows = table.getSelectedRows();
StringBuilder selectData = new StringBuilder();
for (int row : selectRows) {
selectData.append(table.getValueAt(row, 1).toString()).append("\r\n");
}
if (!selectData.isEmpty()) {
selectData.delete(selectData.length() - 2, selectData.length());
} else {
return "";
}
return selectData.toString();
}
public JTable getDataTable() {
return this.dataTable;
}
}

View File

@@ -0,0 +1,79 @@
package hae.component.rule;
import hae.Config;
import javax.swing.*;
import java.awt.*;
public class Display extends JPanel {
public JTextField firstRegexTextField;
public JTextField secondRegexTextField;
public JTextField formatTextField;
public JTextField ruleNameTextField;
public JComboBox<String> scopeComboBox;
public JComboBox<String> engineComboBox;
public JComboBox<String> colorComboBox;
public JComboBox<Boolean> sensitiveComboBox;
public Display() {
initComponents();
}
private void initComponents() {
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
addLabel("Name:", 0, c);
ruleNameTextField = addTextField(0, c);
addLabel("F-Regex:", 1, c);
firstRegexTextField = addTextField(1, c);
addLabel("S-Regex:", 2, c);
secondRegexTextField = addTextField(2, c);
addLabel("Format:", 3, c);
formatTextField = addTextField(3, c);
addLabel("Scope:", 4, c);
scopeComboBox = addComboBox(Config.scope, 4, c);
addLabel("Engine:", 5, c);
engineComboBox = addComboBox(Config.engine, 5, c);
engineComboBox.addActionListener(e -> {
boolean isNfa = "nfa".equals(engineComboBox.getSelectedItem().toString());
formatTextField.setEnabled(isNfa);
formatTextField.setText(isNfa ? formatTextField.getText() : "{0}");
});
addLabel("Color:", 6, c);
colorComboBox = addComboBox(Config.color, 6, c);
addLabel("Sensitive:", 7, c);
sensitiveComboBox = addComboBox(new Boolean[]{true, false}, 7, c);
}
private void addLabel(String text, int y, GridBagConstraints c) {
JLabel label = new JLabel(text);
c.gridx = 0;
c.gridy = y;
add(label, c);
}
private JTextField addTextField(int y, GridBagConstraints c) {
JTextField textField = new JTextField(35);
c.gridx = 1;
c.gridy = y;
add(textField, c);
return textField;
}
private <T> JComboBox<T> addComboBox(T[] items, int y, GridBagConstraints c) {
JComboBox<T> comboBox = new JComboBox<>(items);
c.gridx = 1;
c.gridy = y;
add(comboBox, c);
return comboBox;
}
}

View File

@@ -0,0 +1,163 @@
package hae.component.rule;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import hae.utils.ConfigLoader;
import hae.utils.rule.RuleProcessor;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Vector;
import static javax.swing.JOptionPane.YES_OPTION;
public class Rule extends JPanel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final RuleProcessor ruleProcessor;
private final JTabbedPane tabbedPane;
public Rule(MontoyaApi api, ConfigLoader configLoader, Object[][] data, JTabbedPane tabbedPane) {
this.api = api;
this.configLoader = configLoader;
this.ruleProcessor = new RuleProcessor(api, configLoader);
this.tabbedPane = tabbedPane;
initComponents(data);
}
private void initComponents(Object[][] data) {
setLayout(new GridBagLayout());
((GridBagLayout) getLayout()).columnWidths = new int[]{0, 0, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 0, 0, 0, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 1.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 0.0, 0.0, 1.0, 1.0E-4};
JButton addButton = new JButton("Add");
JButton editButton = new JButton("Edit");
JButton removeButton = new JButton("Remove");
JTable ruleTable = new JTable();
JScrollPane scrollPane = new JScrollPane();
ruleTable.setShowVerticalLines(false);
ruleTable.setShowHorizontalLines(false);
ruleTable.setVerifyInputWhenFocusTarget(false);
ruleTable.setUpdateSelectionOnSort(false);
ruleTable.setSurrendersFocusOnKeystroke(true);
scrollPane.setViewportView(ruleTable);
// 按钮监听事件
addButton.addActionListener(e -> ruleAddActionPerformed(e, ruleTable, tabbedPane));
editButton.addActionListener(e -> ruleEditActionPerformed(e, ruleTable, tabbedPane));
removeButton.addActionListener(e -> ruleRemoveActionPerformed(e, ruleTable, tabbedPane));
// 表格
DefaultTableModel model = new DefaultTableModel() {
@Override
public Class<?> getColumnClass(int column) {
return (column == 0) ? Boolean.class : String.class;
}
@Override
public boolean isCellEditable(int row, int column) {
return column == 0;
}
};
ruleTable.setModel(model);
ruleTable.setRowSorter(new TableRowSorter<>(model));
model.setDataVector(data, Config.ruleFields);
model.addTableModelListener(e -> {
if (e.getColumn() == 0 && ruleTable.getSelectedRow() != -1) {
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
ruleProcessor.changeRule(model.getDataVector().get(select), select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
}
});
add(addButton, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(15, 5, 3, 2), 0, 0));
add(editButton, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 5, 3, 2), 0, 0));
add(removeButton, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 5, 3, 2), 0, 0));
add(scrollPane, new GridBagConstraints(1, 0, 1, 4, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(15, 5, 5, 5), 0, 0));
}
private void ruleAddActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
Display ruleDisplay = new Display();
ruleDisplay.formatTextField.setText("{0}");
int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Add Rule", JOptionPane.OK_OPTION);
if (showState == YES_OPTION) {
Vector<Object> ruleData = new Vector<>();
ruleData.add(false);
ruleData.add(ruleDisplay.ruleNameTextField.getText());
ruleData.add(ruleDisplay.firstRegexTextField.getText());
ruleData.add(ruleDisplay.secondRegexTextField.getText());
ruleData.add(ruleDisplay.formatTextField.getText());
ruleData.add(ruleDisplay.colorComboBox.getSelectedItem().toString());
ruleData.add(ruleDisplay.scopeComboBox.getSelectedItem().toString());
ruleData.add(ruleDisplay.engineComboBox.getSelectedItem().toString());
ruleData.add(ruleDisplay.sensitiveComboBox.getSelectedItem());
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
model.insertRow(model.getRowCount(), ruleData);
ruleProcessor.addRule(ruleData, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
}
}
private void ruleEditActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
if (ruleTable.getSelectedRowCount() >= 1) {
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
Display ruleDisplay = new Display();
ruleDisplay.ruleNameTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 1).toString());
ruleDisplay.firstRegexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 2).toString());
ruleDisplay.secondRegexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 3).toString());
ruleDisplay.formatTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 4).toString());
ruleDisplay.colorComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 5).toString());
ruleDisplay.scopeComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 6).toString());
ruleDisplay.engineComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 7).toString());
ruleDisplay.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 8));
ruleDisplay.formatTextField.setEnabled(ruleDisplay.engineComboBox.getSelectedItem().toString().equals("nfa"));
int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Edit Rule", JOptionPane.OK_OPTION);
if (showState == 0) {
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
model.setValueAt(ruleDisplay.ruleNameTextField.getText(), select, 1);
model.setValueAt(ruleDisplay.firstRegexTextField.getText(), select, 2);
model.setValueAt(ruleDisplay.secondRegexTextField.getText(), select, 3);
model.setValueAt(ruleDisplay.formatTextField.getText(), select, 4);
model.setValueAt(ruleDisplay.colorComboBox.getSelectedItem().toString(), select, 5);
model.setValueAt(ruleDisplay.scopeComboBox.getSelectedItem().toString(), select, 6);
model.setValueAt(ruleDisplay.engineComboBox.getSelectedItem().toString(), select, 7);
model.setValueAt(ruleDisplay.sensitiveComboBox.getSelectedItem(), select, 8);
model = (DefaultTableModel) ruleTable.getModel();
ruleProcessor.changeRule(model.getDataVector().get(select), select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
}
}
}
private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
if (ruleTable.getSelectedRowCount() >= 1) {
if (JOptionPane.showConfirmDialog(this, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) {
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
model.removeRow(select);
ruleProcessor.removeRule(select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
}
}
}
}

View File

@@ -0,0 +1,158 @@
package hae.component.rule;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import hae.utils.ConfigLoader;
import hae.utils.rule.RuleProcessor;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Rules extends JTabbedPane {
private final MontoyaApi api;
private ConfigLoader configLoader;
private final RuleProcessor ruleProcessor;
private final JTextField ruleGroupNameTextField;
private Component tabComponent;
private int selectedIndex;
public Rules(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
this.ruleProcessor = new RuleProcessor(api, configLoader);
this.ruleGroupNameTextField = new JTextField();
initComponents();
}
private void initComponents() {
reloadRuleGroup();
JTabbedPane tabbedPane = this;
JMenuItem deleteMenuItem = new JMenuItem("Delete");
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(deleteMenuItem);
deleteMenuItem.addActionListener(this::deleteRuleGroupActionPerformed);
ruleGroupNameTextField.setBorder(BorderFactory.createEmptyBorder());
ruleGroupNameTextField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
renameTitleActionPerformed.actionPerformed(null);
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int index = getSelectedIndex();
Rectangle r = getBoundsAt(index);
if (r.contains(e.getPoint()) && index >= 0) {
switch (e.getButton()) {
case MouseEvent.BUTTON1:
if (e.getClickCount() == 2) {
selectedIndex = index;
tabComponent = getTabComponentAt(selectedIndex);
String ruleGroupName = getTitleAt(selectedIndex);
if (!"...".equals(ruleGroupName)) {
setTabComponentAt(selectedIndex, ruleGroupNameTextField);
ruleGroupNameTextField.setVisible(true);
ruleGroupNameTextField.setText(ruleGroupName);
ruleGroupNameTextField.selectAll();
ruleGroupNameTextField.requestFocusInWindow();
ruleGroupNameTextField.setMinimumSize(ruleGroupNameTextField.getPreferredSize());
}
} else if (e.getClickCount() == 1) {
if ("...".equals(getTitleAt(getSelectedIndex()))) {
String title = ruleProcessor.newRule();
Rule newRule = new Rule(api, configLoader, Config.ruleTemplate, tabbedPane);
insertTab(title, null, newRule, null, getTabCount() - 1);
setSelectedIndex(getTabCount() - 2);
} else {
renameTitleActionPerformed.actionPerformed(null);
}
}
break;
case MouseEvent.BUTTON3:
if (!"...".equals(getTitleAt(getSelectedIndex()))) {
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
break;
default:
break;
}
}
}
});
InputMap im = ruleGroupNameTextField.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap am = ruleGroupNameTextField.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");
am.put("cancel", cancelActionPerformed);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "rename");
am.put("rename", renameTitleActionPerformed);
}
public void reloadRuleGroup() {
removeAll();
this.configLoader = new ConfigLoader(api);
Config.globalRules.keySet().forEach(i -> addTab(i, new Rule(api, configLoader, hae.Config.globalRules.get(i), this)));
addTab("...", null);
}
private void deleteRuleGroupActionPerformed(ActionEvent e) {
if (getTabCount() > 2) {
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to delete this rule group?", "Info",
JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) {
String title = getTitleAt(getSelectedIndex());
ruleProcessor.deleteRuleGroup(title);
remove(getSelectedIndex());
setSelectedIndex(getSelectedIndex() - 1);
}
}
}
private final Action renameTitleActionPerformed = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
String title = ruleGroupNameTextField.getText();
if (!title.isEmpty() && selectedIndex >= 0) {
String oldName = getTitleAt(selectedIndex);
setTitleAt(selectedIndex, title);
if (!oldName.equals(title)) {
ruleProcessor.renameRuleGroup(oldName, title);
}
}
cancelActionPerformed.actionPerformed(null);
}
};
private final Action cancelActionPerformed = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (selectedIndex >= 0) {
setTabComponentAt(selectedIndex, tabComponent);
ruleGroupNameTextField.setVisible(false);
ruleGroupNameTextField.setPreferredSize(null);
selectedIndex = -1;
tabComponent = null;
requestFocusInWindow();
}
}
};
}

View File

@@ -0,0 +1,143 @@
package hae.instances.editor;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
import hae.Config;
import hae.component.board.table.Datatable;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.ConfigLoader;
import hae.utils.http.HttpUtils;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class RequestEditor implements HttpRequestEditorProvider {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public RequestEditor(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
@Override
public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext editorCreationContext) {
return new Editor(api, configLoader, editorCreationContext);
}
private static class Editor implements ExtensionProvidedHttpRequestEditor {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final HttpUtils httpUtils;
private final EditorCreationContext creationContext;
private final MessageProcessor messageProcessor;
private HttpRequestResponse requestResponse;
private List<Map<String, String>> dataList;
private final JTabbedPane jTabbedPane = new JTabbedPane();
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
this.api = api;
this.configLoader = configLoader;
this.httpUtils = new HttpUtils(api, configLoader);
this.creationContext = creationContext;
this.messageProcessor = new MessageProcessor(api);
}
@Override
public HttpRequest getRequest() {
return requestResponse.request();
}
@Override
public void setRequestResponse(HttpRequestResponse requestResponse) {
this.requestResponse = requestResponse;
generateTabbedPaneFromResultMap(api, configLoader, jTabbedPane, this.dataList);
}
@Override
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
HttpRequest request = requestResponse.request();
if (request != null) {
try {
String host = StringProcessor.getHostByUrl(request.url());
if (!host.isEmpty()) {
String toolType = creationContext.toolSource().toolType().toolName();
boolean matches = httpUtils.verifyHttpRequestResponse(requestResponse, toolType);
if (!matches) {
this.dataList = messageProcessor.processRequest("", request, false);
return isListHasData(this.dataList);
}
}
} catch (Exception ignored) {
}
}
return false;
}
@Override
public String caption() {
return "MarkInfo";
}
@Override
public Component uiComponent() {
return jTabbedPane;
}
@Override
public Selection selectedData() {
return new Selection() {
@Override
public ByteArray contents() {
Datatable dataTable = (Datatable) jTabbedPane.getSelectedComponent();
return ByteArray.byteArray(dataTable.getSelectedDataAtTable(dataTable.getDataTable()));
}
@Override
public Range offsets() {
return null;
}
};
}
@Override
public boolean isModified() {
return false;
}
}
public static boolean isListHasData(List<Map<String, String>> dataList) {
if (dataList != null && !dataList.isEmpty()) {
Map<String, String> dataMap = dataList.get(0);
return dataMap != null && !dataMap.isEmpty();
}
return false;
}
public static void generateTabbedPaneFromResultMap(MontoyaApi api, ConfigLoader configLoader, JTabbedPane tabbedPane, List<Map<String, String>> result) {
tabbedPane.removeAll();
if (result != null && !result.isEmpty()) {
Map<String, String> dataMap = result.get(0);
if (dataMap != null && !dataMap.isEmpty()) {
dataMap.keySet().forEach(i -> {
String[] extractData = dataMap.get(i).split(Config.boundary);
Datatable dataPanel = new Datatable(api, configLoader, i, Arrays.asList(extractData));
tabbedPane.addTab(i, dataPanel);
});
}
}
}
}

View File

@@ -0,0 +1,127 @@
package hae.instances.editor;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor;
import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider;
import hae.component.board.table.Datatable;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.ConfigLoader;
import hae.utils.http.HttpUtils;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import java.util.Map;
public class ResponseEditor implements HttpResponseEditorProvider {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public ResponseEditor(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
@Override
public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext editorCreationContext) {
return new Editor(api, configLoader, editorCreationContext);
}
private static class Editor implements ExtensionProvidedHttpResponseEditor {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final HttpUtils httpUtils;
private final EditorCreationContext creationContext;
private final MessageProcessor messageProcessor;
private HttpRequestResponse requestResponse;
private List<Map<String, String>> dataList;
private final JTabbedPane jTabbedPane = new JTabbedPane();
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
this.api = api;
this.configLoader = configLoader;
this.httpUtils = new HttpUtils(api, configLoader);
this.creationContext = creationContext;
this.messageProcessor = new MessageProcessor(api);
}
@Override
public HttpResponse getResponse() {
return requestResponse.response();
}
@Override
public void setRequestResponse(HttpRequestResponse requestResponse) {
this.requestResponse = requestResponse;
RequestEditor.generateTabbedPaneFromResultMap(api, configLoader, jTabbedPane, this.dataList);
}
@Override
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
HttpResponse response = requestResponse.response();
if (response != null) {
HttpRequest request = requestResponse.request();
boolean matches = false;
if (request != null) {
try {
String host = StringProcessor.getHostByUrl(request.url());
if (!host.isEmpty()) {
String toolType = creationContext.toolSource().toolType().toolName();
matches = httpUtils.verifyHttpRequestResponse(requestResponse, toolType);
}
} catch (Exception ignored) {
}
}
if (!matches) {
this.dataList = messageProcessor.processResponse("", response, false);
return RequestEditor.isListHasData(this.dataList);
}
}
return false;
}
@Override
public String caption() {
return "MarkInfo";
}
@Override
public Component uiComponent() {
return jTabbedPane;
}
@Override
public Selection selectedData() {
return new Selection() {
@Override
public ByteArray contents() {
Datatable dataTable = (Datatable) jTabbedPane.getSelectedComponent();
return ByteArray.byteArray(dataTable.getSelectedDataAtTable(dataTable.getDataTable()));
}
@Override
public Range offsets() {
return null;
}
};
}
@Override
public boolean isModified() {
return false;
}
}
}

View File

@@ -0,0 +1,103 @@
package hae.instances.editor;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.contextmenu.WebSocketMessage;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedWebSocketMessageEditor;
import burp.api.montoya.ui.editor.extension.WebSocketMessageEditorProvider;
import hae.component.board.table.Datatable;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.ConfigLoader;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import java.util.Map;
public class WebSocketEditor implements WebSocketMessageEditorProvider {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public WebSocketEditor(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
@Override
public ExtensionProvidedWebSocketMessageEditor provideMessageEditor(EditorCreationContext editorCreationContext) {
return new Editor(api, configLoader, editorCreationContext);
}
private static class Editor implements ExtensionProvidedWebSocketMessageEditor {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final EditorCreationContext creationContext;
private final MessageProcessor messageProcessor;
private ByteArray message;
private List<Map<String, String>> dataList;
private final JTabbedPane jTabbedPane = new JTabbedPane();
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
this.api = api;
this.configLoader = configLoader;
this.creationContext = creationContext;
this.messageProcessor = new MessageProcessor(api);
}
@Override
public ByteArray getMessage() {
return message;
}
@Override
public void setMessage(WebSocketMessage webSocketMessage) {
this.message = webSocketMessage.payload();
RequestEditor.generateTabbedPaneFromResultMap(api, configLoader, jTabbedPane, this.dataList);
}
@Override
public boolean isEnabledFor(WebSocketMessage webSocketMessage) {
String websocketMessage = webSocketMessage.payload().toString();
if (!websocketMessage.isEmpty()) {
this.dataList = messageProcessor.processMessage("", websocketMessage, false);
return RequestEditor.isListHasData(this.dataList);
}
return false;
}
@Override
public String caption() {
return "MarkInfo";
}
@Override
public Component uiComponent() {
return jTabbedPane;
}
@Override
public Selection selectedData() {
return new Selection() {
@Override
public ByteArray contents() {
Datatable dataTable = (Datatable) jTabbedPane.getSelectedComponent();
return ByteArray.byteArray(dataTable.getSelectedDataAtTable(dataTable.getDataTable()));
}
@Override
public Range offsets() {
return null;
}
};
}
@Override
public boolean isModified() {
return false;
}
}
}

View File

@@ -0,0 +1,106 @@
package hae.instances.http;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Annotations;
import burp.api.montoya.core.HighlightColor;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import hae.component.board.message.MessageTableModel;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.ConfigLoader;
import hae.utils.http.HttpUtils;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpMessageActiveHandler implements HttpHandler {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final HttpUtils httpUtils;
private final MessageTableModel messageTableModel;
private final MessageProcessor messageProcessor;
// Montoya API对HTTP消息的处理分为了请求和响应因此此处设置高亮和标记需要使用全局变量的方式以此兼顾请求和响应
// 同时采用 ThreadLocal 来保证多线程并发的情况下全局变量的安全性
private final ThreadLocal<String> host = ThreadLocal.withInitial(() -> "");
private final ThreadLocal<List<String>> colorList = ThreadLocal.withInitial(ArrayList::new);
private final ThreadLocal<List<String>> commentList = ThreadLocal.withInitial(ArrayList::new);
public HttpMessageActiveHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
this.httpUtils = new HttpUtils(api, configLoader);
this.messageTableModel = messageTableModel;
this.messageProcessor = new MessageProcessor(api);
}
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent httpRequestToBeSent) {
colorList.get().clear();
commentList.get().clear();
Annotations annotations = httpRequestToBeSent.annotations();
try {
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
} catch (Exception e) {
api.logging().logToError("handleHttpRequestToBeSent: " + e.getMessage());
}
return RequestToBeSentAction.continueWith(httpRequestToBeSent, annotations);
}
@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived httpResponseReceived) {
Annotations annotations = httpResponseReceived.annotations();
HttpRequest request = httpResponseReceived.initiatingRequest();
HttpRequestResponse requestResponse = HttpRequestResponse.httpRequestResponse(request, httpResponseReceived);
String toolType = httpResponseReceived.toolSource().toolType().toolName();
boolean matches = httpUtils.verifyHttpRequestResponse(requestResponse, toolType);
if (!matches) {
try {
setColorAndCommentList(messageProcessor.processRequest(host.get(), request, true));
setColorAndCommentList(messageProcessor.processResponse(host.get(), httpResponseReceived, true));
if (!colorList.get().isEmpty() && !commentList.get().isEmpty()) {
HttpRequestResponse httpRequestResponse = HttpRequestResponse.httpRequestResponse(request, httpResponseReceived);
String color = messageProcessor.retrieveFinalColor(messageProcessor.retrieveColorIndices(colorList.get()));
annotations.setHighlightColor(HighlightColor.highlightColor(color));
String comment = StringProcessor.mergeComment(String.join(", ", commentList.get()));
annotations.setNotes(comment);
String method = request.method();
String url = request.url();
String status = String.valueOf(httpResponseReceived.statusCode());
String length = String.valueOf(httpResponseReceived.toByteArray().length());
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, true);
return null;
}
}.execute();
}
} catch (Exception e) {
api.logging().logToError("handleHttpResponseReceived: " + e.getMessage());
}
}
return ResponseReceivedAction.continueWith(httpResponseReceived, annotations);
}
private void setColorAndCommentList(List<Map<String, String>> result) {
if (result != null && !result.isEmpty()) {
colorList.get().add(result.get(0).get("color"));
commentList.get().add(result.get(1).get("comment"));
}
}
}

View File

@@ -0,0 +1,97 @@
package hae.instances.http;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.scanner.AuditResult;
import burp.api.montoya.scanner.ConsolidationAction;
import burp.api.montoya.scanner.ScanCheck;
import burp.api.montoya.scanner.audit.insertionpoint.AuditInsertionPoint;
import burp.api.montoya.scanner.audit.issues.AuditIssue;
import hae.component.board.message.MessageTableModel;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.ConfigLoader;
import hae.utils.http.HttpUtils;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static burp.api.montoya.scanner.AuditResult.auditResult;
import static burp.api.montoya.scanner.ConsolidationAction.KEEP_BOTH;
import static burp.api.montoya.scanner.ConsolidationAction.KEEP_EXISTING;
import static java.util.Collections.emptyList;
public class HttpMessagePassiveHandler implements ScanCheck {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final HttpUtils httpUtils;
private final MessageTableModel messageTableModel;
private final MessageProcessor messageProcessor;
public HttpMessagePassiveHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
this.httpUtils = new HttpUtils(api, configLoader);
this.messageTableModel = messageTableModel;
this.messageProcessor = new MessageProcessor(api);
}
@Override
public AuditResult activeAudit(HttpRequestResponse httpRequestResponse, AuditInsertionPoint auditInsertionPoint) {
return auditResult(emptyList());
}
@Override
public AuditResult passiveAudit(HttpRequestResponse httpRequestResponse) {
List<String> colorList = new ArrayList<>();
List<String> commentList = new ArrayList<>();
HttpRequest request = httpRequestResponse.request();
HttpResponse response = httpRequestResponse.response();
boolean matches = httpUtils.verifyHttpRequestResponse(httpRequestResponse, "Proxy");
if (!matches) {
try {
String host = StringProcessor.getHostByUrl(request.url());
setColorAndCommentList(messageProcessor.processRequest(host, request, true), colorList, commentList);
setColorAndCommentList(messageProcessor.processResponse(host, response, true), colorList, commentList);
String url = request.url();
String method = request.method();
String status = String.valueOf(response.statusCode());
String color = messageProcessor.retrieveFinalColor(messageProcessor.retrieveColorIndices(colorList));
String comment = StringProcessor.mergeComment(String.join(", ", commentList));
String length = String.valueOf(response.toByteArray().length());
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, true);
return null;
}
}.execute();
} catch (Exception e) {
api.logging().logToError("passiveAudit: " + e.getMessage());
}
}
return auditResult(emptyList());
}
private void setColorAndCommentList(List<Map<String, String>> result, List<String> colorList, List<String> commentList) {
if (result != null && !result.isEmpty()) {
colorList.add(result.get(0).get("color"));
commentList.add(result.get(1).get("comment"));
}
}
@Override
public ConsolidationAction consolidateIssues(AuditIssue newIssue, AuditIssue existingIssue) {
return existingIssue.name().equals(newIssue.name()) ? KEEP_EXISTING : KEEP_BOTH;
}
}

View File

@@ -0,0 +1,178 @@
package hae.instances.http.utils;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpHeader;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import hae.Config;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
public class MessageProcessor {
private final MontoyaApi api;
private final RegularMatcher regularMatcher;
private String finalColor = "";
public MessageProcessor(MontoyaApi api) {
this.api = api;
this.regularMatcher = new RegularMatcher(api);
}
public List<Map<String, String>> processMessage(String host, String message, boolean flag) {
Map<String, Map<String, Object>> obj = null;
try {
obj = regularMatcher.match(host, "any", message, message, message);
} catch (Exception ignored) {
}
return getDataList(obj, flag);
}
public List<Map<String, String>> processResponse(String host, HttpResponse httpResponse, boolean flag) {
Map<String, Map<String, Object>> obj = null;
try {
String response = new String(httpResponse.toByteArray().getBytes(), StandardCharsets.UTF_8);
String body = new String(httpResponse.body().getBytes(), StandardCharsets.UTF_8);
String header = httpResponse.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
obj = regularMatcher.match(host, "response", response, header, body);
} catch (Exception ignored) {
}
return getDataList(obj, flag);
}
public List<Map<String, String>> processRequest(String host, HttpRequest httpRequest, boolean flag) {
Map<String, Map<String, Object>> obj = null;
try {
String request = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8);
String body = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8);
String header = httpRequest.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
obj = regularMatcher.match(host, "request", request, header, body);
} catch (Exception ignored) {
}
return getDataList(obj, flag);
}
private List<Map<String, String>> getDataList(Map<String, Map<String, Object>> obj, boolean actionFlag) {
List<Map<String, String>> highlightList = new ArrayList<>();
List<Map<String, String>> extractList = new ArrayList<>();
if (obj != null && !obj.isEmpty()) {
if (actionFlag) {
List<List<String>> resultList = extractColorsAndComments(obj);
List<String> colorList = resultList.get(0);
List<String> commentList = resultList.get(1);
if (!colorList.isEmpty() && !commentList.isEmpty()) {
String color = retrieveFinalColor(retrieveColorIndices(colorList));
Map<String, String> colorMap = new HashMap<String, String>() {{
put("color", color);
}};
Map<String, String> commentMap = new HashMap<String, String>() {{
put("comment", String.join(", ", commentList));
}};
highlightList.add(colorMap);
highlightList.add(commentMap);
}
} else {
extractList.add(extractDataFromMap(obj));
}
}
return actionFlag ? highlightList : extractList;
}
private Map<String, String> extractDataFromMap(Map<String, Map<String, Object>> inputData) {
Map<String, String> extractedData = new HashMap<>();
inputData.keySet().forEach(key -> {
Map<String, Object> tempMap = inputData.get(key);
String data = tempMap.get("data").toString();
extractedData.put(key, data);
});
return extractedData;
}
private List<List<String>> extractColorsAndComments(Map<String, Map<String, Object>> inputData) {
List<String> colorList = new ArrayList<>();
List<String> commentList = new ArrayList<>();
inputData.keySet().forEach(key -> {
Map<String, Object> tempMap = inputData.get(key);
String color = tempMap.get("color").toString();
colorList.add(color);
commentList.add(key);
});
List<List<String>> result = new ArrayList<>();
result.add(colorList);
result.add(commentList);
return result;
}
public List<Integer> retrieveColorIndices(List<String> colors) {
List<Integer> indices = new ArrayList<>();
String[] colorArray = Config.color;
int size = colorArray.length;
for (String color : colors) {
for (int i = 0; i < size; i++) {
if (colorArray[i].equals(color)) {
indices.add(i);
}
}
}
return indices;
}
private void upgradeColors(List<Integer> colorList) {
int colorSize = colorList.size();
String[] colorArray = Config.color;
colorList.sort(Comparator.comparingInt(Integer::intValue));
int i = 0;
List<Integer> stack = new ArrayList<>();
while (i < colorSize) {
if (stack.isEmpty()) {
stack.add(colorList.get(i));
} else {
if (!Objects.equals(colorList.get(i), stack.stream().reduce((first, second) -> second).orElse(99999999))) {
stack.add(colorList.get(i));
} else {
stack.set(stack.size() - 1, stack.get(stack.size() - 1) - 1);
}
}
i++;
}
// 利用HashSet删除重复元素
HashSet tmpList = new HashSet(stack);
if (stack.size() == tmpList.size()) {
stack.sort(Comparator.comparingInt(Integer::intValue));
if (stack.get(0) < 0) {
finalColor = colorArray[0];
} else {
finalColor = colorArray[stack.get(0)];
}
} else {
upgradeColors(stack);
}
}
public String retrieveFinalColor(List<Integer> colorList) {
upgradeColors(colorList);
return finalColor;
}
}

View File

@@ -0,0 +1,289 @@
package hae.instances.http.utils;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.persistence.PersistedList;
import burp.api.montoya.persistence.PersistedObject;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.AutomatonMatcher;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
import hae.Config;
import hae.cache.CachePool;
import hae.utils.DataManager;
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularMatcher {
private final MontoyaApi api;
public RegularMatcher(MontoyaApi api) {
this.api = api;
}
public Map<String, Map<String, Object>> match(String host, String type, String message, String header, String body) {
// 先从缓存池里判断是否有已经匹配好的结果
String messageIndex = HashCalculator.calculateHash(message.getBytes());
Map<String, Map<String, Object>> map = CachePool.get(messageIndex);
if (map != null) {
return map;
} else {
// 最终返回的结果
Map<String, Map<String, Object>> finalMap = new HashMap<>();
Config.globalRules.keySet().parallelStream().forEach(i -> {
for (Object[] objects : Config.globalRules.get(i)) {
// 多线程执行,一定程度上减少阻塞现象
String matchContent = "";
// 遍历获取规则
List<String> result;
Map<String, Object> tmpMap = new HashMap<>();
boolean loaded = (Boolean) objects[0];
String name = objects[1].toString();
String f_regex = objects[2].toString();
String s_regex = objects[3].toString();
String format = objects[4].toString();
String color = objects[5].toString();
String scope = objects[6].toString();
String engine = objects[7].toString();
boolean sensitive = (Boolean) objects[8];
// 判断规则是否开启与作用域
if (loaded && (scope.contains(type) || scope.contains("any") || type.equals("any"))) {
switch (scope) {
case "any":
case "request":
case "response":
matchContent = message;
break;
case "any header":
case "request header":
case "response header":
matchContent = header;
break;
case "any body":
case "request body":
case "response body":
matchContent = body;
break;
case "request line":
case "response line":
matchContent = message.split("\\r?\\n", 2)[0];
break;
default:
break;
}
try {
result = new ArrayList<>(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
} catch (Exception e) {
api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
api.logging().logToError(e.getMessage());
continue;
}
// 去除重复内容
HashSet tmpList = new HashSet(result);
result.clear();
result.addAll(tmpList);
if (!result.isEmpty()) {
tmpMap.put("color", color);
String dataStr = String.join(Config.boundary, result);
tmpMap.put("data", dataStr);
String nameAndSize = String.format("%s (%s)", name, result.size());
finalMap.put(nameAndSize, tmpMap);
putDataToGlobalMap(api, host, name, result, true);
}
}
}
});
CachePool.put(messageIndex, finalMap);
return finalMap;
}
}
public static void putDataToGlobalMap(MontoyaApi api, String host, String name, List<String> dataList, boolean flag) {
// 添加到全局变量中便于Databoard检索
if (!Objects.equals(host, "") && host != null) {
Config.globalDataMap.compute(host, (existingHost, existingMap) -> {
Map<String, List<String>> gRuleMap = Optional.ofNullable(existingMap).orElse(new ConcurrentHashMap<>());
gRuleMap.merge(name, new ArrayList<>(dataList), (existingList, newList) -> {
Set<String> combinedSet = new LinkedHashSet<>(existingList);
combinedSet.addAll(newList);
return new ArrayList<>(combinedSet);
});
if (flag) {
// 数据存储在BurpSuite空间内
DataManager dataManager = new DataManager(api);
PersistedObject persistedObject = PersistedObject.persistedObject();
gRuleMap.forEach((kName, vList) -> {
PersistedList<String> persistedList = PersistedList.persistedStringList();
persistedList.addAll(vList);
persistedObject.setStringList(kName, persistedList);
});
dataManager.putData("data", host, persistedObject);
}
return gRuleMap;
});
String[] splitHost = host.split("\\.");
String onlyHost = host.split(":")[0];
String anyHost = (splitHost.length > 2 && !StringProcessor.matchHostIsIp(onlyHost)) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : "";
if (!Config.globalDataMap.containsKey(anyHost) && !anyHost.isEmpty()) {
// 添加通配符Host实际数据从查询哪里将所有数据提取
Config.globalDataMap.put(anyHost, new HashMap<>());
}
if (!Config.globalDataMap.containsKey("*")) {
// 添加通配符全匹配,同上
Config.globalDataMap.put("*", new HashMap<>());
}
}
}
private List<String> matchByRegex(String f_regex, String s_regex, String content, String format, String engine, boolean sensitive) {
List<String> retList = new ArrayList<>();
if ("nfa".equals(engine)) {
Matcher matcher = createPatternMatcher(f_regex, content, sensitive);
retList.addAll(extractMatches(s_regex, format, sensitive, matcher));
} else {
// DFA不支持格式化输出因此不关注format
String newContent = content;
String newFirstRegex = f_regex;
if (!sensitive) {
newContent = content.toLowerCase();
newFirstRegex = f_regex.toLowerCase();
}
AutomatonMatcher autoMatcher = createAutomatonMatcher(newFirstRegex, newContent);
retList.addAll(extractMatches(s_regex, autoMatcher, content));
}
return retList;
}
private List<String> extractMatches(String s_regex, String format, boolean sensitive, Matcher matcher) {
List<String> matches = new ArrayList<>();
if (s_regex.isEmpty()) {
matches.addAll(getFormatString(matcher, format));
} else {
while (matcher.find()) {
String matchContent = matcher.group(1);
if (!matchContent.isEmpty()) {
matcher = createPatternMatcher(s_regex, matchContent, sensitive);
matches.addAll(getFormatString(matcher, format));
}
}
}
return matches;
}
private List<String> extractMatches(String s_regex, AutomatonMatcher autoMatcher, String content) {
List<String> matches = new ArrayList<>();
if (s_regex.isEmpty()) {
matches.addAll(getFormatString(autoMatcher, content));
} else {
while (autoMatcher.find()) {
String s = autoMatcher.group();
if (!s.isEmpty()) {
autoMatcher = createAutomatonMatcher(s_regex, getSubString(content, s));
matches.addAll(getFormatString(autoMatcher, content));
}
}
}
return matches;
}
private List<String> getFormatString(Matcher matcher, String format) {
List<Integer> indexList = parseIndexesFromString(format);
List<String> stringList = new ArrayList<>();
while (matcher.find()) {
if (!matcher.group(1).isEmpty()) {
Object[] params = indexList.stream().map(i -> {
if (!matcher.group(i + 1).isEmpty()) {
return matcher.group(i + 1);
}
return "";
}).toArray();
stringList.add(MessageFormat.format(reorderIndex(format), params));
}
}
return stringList;
}
private List<String> getFormatString(AutomatonMatcher matcher, String content) {
List<String> stringList = new ArrayList<>();
while (matcher.find()) {
String s = matcher.group(0);
if (!s.isEmpty()) {
stringList.add(getSubString(content, s));
}
}
return stringList;
}
private Matcher createPatternMatcher(String regex, String content, boolean sensitive) {
Pattern pattern = sensitive ? Pattern.compile(regex) : Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
return pattern.matcher(content);
}
private AutomatonMatcher createAutomatonMatcher(String regex, String content) {
RegExp regexp = new RegExp(regex);
Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
return runAuto.newMatcher(content);
}
private LinkedList<Integer> parseIndexesFromString(String input) {
LinkedList<Integer> indexes = new LinkedList<>();
Pattern pattern = Pattern.compile("\\{(\\d+)}");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
String index = matcher.group(1);
if (!index.isEmpty()) {
indexes.add(Integer.valueOf(index));
}
}
return indexes;
}
private String getSubString(String content, String s) {
byte[] contentByte = api.utilities().byteUtils().convertFromString(content);
byte[] sByte = api.utilities().byteUtils().convertFromString(s);
int startIndex = api.utilities().byteUtils().indexOf(contentByte, sByte, false, 1, contentByte.length);
int endIndex = startIndex + s.length();
return content.substring(startIndex, endIndex);
}
private String reorderIndex(String format) {
Pattern pattern = Pattern.compile("\\{(\\d+)}");
Matcher matcher = pattern.matcher(format);
int count = 0;
while (matcher.find()) {
String newStr = String.format("{%s}", count);
String matchStr = matcher.group(0);
format = format.replace(matchStr, newStr);
count++;
}
return format;
}
}

View File

@@ -0,0 +1,47 @@
package hae.instances.websocket;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.HighlightColor;
import burp.api.montoya.proxy.websocket.*;
import hae.instances.http.utils.MessageProcessor;
import java.util.List;
import java.util.Map;
public class WebSocketMessageHandler implements ProxyMessageHandler {
private final MontoyaApi api;
private final MessageProcessor messageProcessor;
public WebSocketMessageHandler(MontoyaApi api) {
this.api = api;
this.messageProcessor = new MessageProcessor(api);
}
@Override
public TextMessageReceivedAction handleTextMessageReceived(InterceptedTextMessage interceptedTextMessage) {
String message = interceptedTextMessage.payload();
List<Map<String, String>> result = messageProcessor.processMessage("", message, true);
if (result != null && !result.isEmpty()) {
interceptedTextMessage.annotations().setHighlightColor(HighlightColor.highlightColor(result.get(0).get("color")));
interceptedTextMessage.annotations().setNotes(result.get(1).get("comment"));
}
return TextMessageReceivedAction.continueWith(interceptedTextMessage);
}
@Override
public TextMessageToBeSentAction handleTextMessageToBeSent(InterceptedTextMessage interceptedTextMessage) {
return TextMessageToBeSentAction.continueWith(interceptedTextMessage);
}
@Override
public BinaryMessageReceivedAction handleBinaryMessageReceived(InterceptedBinaryMessage interceptedBinaryMessage) {
return BinaryMessageReceivedAction.continueWith(interceptedBinaryMessage);
}
@Override
public BinaryMessageToBeSentAction handleBinaryMessageToBeSent(InterceptedBinaryMessage interceptedBinaryMessage) {
return BinaryMessageToBeSentAction.continueWith(interceptedBinaryMessage);
}
}

View File

@@ -0,0 +1,254 @@
package hae.utils;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
public class ConfigLoader {
private final MontoyaApi api;
private final Yaml yaml;
private final String configFilePath;
private final String rulesFilePath;
public ConfigLoader(MontoyaApi api) {
this.api = api;
DumperOptions dop = new DumperOptions();
dop.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Representer representer = new Representer(dop);
this.yaml = new Yaml(representer, dop);
String configPath = determineConfigPath();
this.configFilePath = String.format("%s/%s", configPath, "Config.yml");
this.rulesFilePath = String.format("%s/%s", configPath, "Rules.yml");
// 构造函数,初始化配置
File HaEConfigPathFile = new File(configPath);
if (!(HaEConfigPathFile.exists() && HaEConfigPathFile.isDirectory())) {
HaEConfigPathFile.mkdirs();
}
File configFilePath = new File(this.configFilePath);
if (!(configFilePath.exists() && configFilePath.isFile())) {
initConfig();
}
File rulesFilePath = new File(this.rulesFilePath);
if (!(rulesFilePath.exists() && rulesFilePath.isFile())) {
initRules();
}
Config.globalRules = getRules();
}
private String determineConfigPath() {
// 优先级1用户根目录
String userConfigPath = String.format("%s/.config/HaE", System.getProperty("user.home"));
if (isValidConfigPath(userConfigPath)) {
return userConfigPath;
}
// 优先级2Jar包所在目录
String jarPath = api.extension().filename();
String jarDirectory = new File(jarPath).getParent();
String jarConfigPath = String.format("%s/.config/HaE", jarDirectory);
if (isValidConfigPath(jarConfigPath)) {
return jarConfigPath;
}
return userConfigPath;
}
private static boolean isValidConfigPath(String configPath) {
File configPathFile = new File(configPath);
return configPathFile.exists() && configPathFile.isDirectory();
}
public void initConfig() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("ExcludeSuffix", getExcludeSuffix());
r.put("BlockHost", getBlockHost());
r.put("ExcludeStatus", getExcludeStatus());
r.put("LimitSize", getLimitSize());
r.put("HaEScope", getScope());
try {
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws);
ws.close();
} catch (Exception ignored) {
}
}
public String getRulesFilePath() {
return rulesFilePath;
}
// 获取规则配置
public Map<String, Object[][]> getRules() {
Map<String, Object[][]> rules = new HashMap<>();
try {
InputStream inputStream = Files.newInputStream(Paths.get(getRulesFilePath()));
DumperOptions dop = new DumperOptions();
dop.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Representer representer = new Representer(dop);
Map<String, Object> rulesMap = new Yaml(representer, dop).load(inputStream);
String[] fieldKeys = {"loaded", "name", "f_regex", "s_regex", "format", "color", "scope", "engine", "sensitive"};
Object rulesObj = rulesMap.get("rules");
if (rulesObj instanceof List) {
List<Map<String, Object>> groupData = (List<Map<String, Object>>) rulesObj;
for (Map<String, Object> groupFields : groupData) {
ArrayList<Object[]> data = new ArrayList<>();
Object ruleObj = groupFields.get("rule");
if (ruleObj instanceof List) {
List<Map<String, Object>> ruleData = (List<Map<String, Object>>) ruleObj;
for (Map<String, Object> ruleFields : ruleData) {
Object[] valuesArray = new Object[fieldKeys.length];
for (int i = 0; i < fieldKeys.length; i++) {
valuesArray[i] = ruleFields.get(fieldKeys[i]);
}
data.add(valuesArray);
}
}
Object[][] dataArray = data.toArray(new Object[data.size()][]);
rules.put(groupFields.get("group").toString(), dataArray);
}
}
return rules;
} catch (Exception ignored) {
}
return rules;
}
public String getBlockHost() {
return getValueFromConfig("BlockHost", Config.host);
}
public String getExcludeSuffix() {
return getValueFromConfig("ExcludeSuffix", Config.suffix);
}
public String getExcludeStatus() {
return getValueFromConfig("ExcludeStatus", Config.status);
}
public String getLimitSize() {
return getValueFromConfig("LimitSize", Config.size);
}
public String getScope() {
return getValueFromConfig("HaEScope", Config.scopeOptions);
}
public boolean getMode() {
return getValueFromConfig("HaEModeStatus", Config.modeStatus).equals("true");
}
private String getValueFromConfig(String name, String defaultValue) {
File yamlSetting = new File(configFilePath);
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
return defaultValue;
}
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
Map<String, Object> r = new Yaml().load(inorder);
if (r.containsKey(name)) {
return r.get(name).toString();
}
} catch (Exception ignored) {
}
return defaultValue;
}
public void setExcludeSuffix(String excludeSuffix) {
setValueToConfig("ExcludeSuffix", excludeSuffix);
}
public void setBlockHost(String blockHost) {
setValueToConfig("BlockHost", blockHost);
}
public void setExcludeStatus(String status) {
setValueToConfig("ExcludeStatus", status);
}
public void setLimitSize(String size) {
setValueToConfig("LimitSize", size);
}
public void setScope(String scope) {
setValueToConfig("HaEScope", scope);
}
public void setMode(String mode) {
setValueToConfig("HaEModeStatus", mode);
}
private void setValueToConfig(String name, String value) {
Map<String, Object> currentConfig = loadCurrentConfig();
currentConfig.put(name, value);
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8)) {
yaml.dump(currentConfig, ws);
} catch (Exception ignored) {
}
}
private Map<String, Object> loadCurrentConfig() {
Path path = Paths.get(configFilePath);
if (!Files.exists(path)) {
return new LinkedHashMap<>(); // 返回空的Map表示没有当前配置
}
try (InputStream in = Files.newInputStream(path)) {
return yaml.load(in);
} catch (Exception e) {
return new LinkedHashMap<>(); // 读取失败时也返回空的Map
}
}
public boolean initRules() {
boolean ret = copyRulesToFile(this.rulesFilePath);
if (!ret) {
api.extension().unload();
}
return ret;
}
private boolean copyRulesToFile(String targetFilePath) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("rules/Rules.yml");
File targetFile = new File(targetFilePath);
try (inputStream; OutputStream outputStream = new FileOutputStream(targetFile)) {
if (inputStream != null) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
return true;
}
} catch (Exception ignored) {
}
return false;
}
}

View File

@@ -0,0 +1,85 @@
package hae.utils;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.persistence.PersistedList;
import burp.api.montoya.persistence.PersistedObject;
import burp.api.montoya.persistence.Persistence;
import hae.component.board.message.MessageTableModel;
import hae.instances.http.utils.RegularMatcher;
public class DataManager {
private final MontoyaApi api;
private final Persistence persistence;
public DataManager(MontoyaApi api) {
this.api = api;
this.persistence = api.persistence();
}
private void saveIndex(String indexName, String indexValue) {
PersistedList<String> indexList = persistence.extensionData().getStringList(indexName);
if (indexList != null && !indexList.isEmpty()) {
persistence.extensionData().deleteStringList(indexName);
} else {
indexList = PersistedList.persistedStringList();
}
if (!indexList.contains(indexValue)) {
indexList.add(indexValue);
}
persistence.extensionData().setStringList(indexName, indexList);
}
public void putData(String dataType, String dataName, PersistedObject persistedObject) {
if (persistence.extensionData().getChildObject(dataName) != null) {
persistence.extensionData().deleteChildObject(dataName);
}
persistence.extensionData().setChildObject(dataName, persistedObject);
saveIndex(dataType, dataName);
}
public void loadData(MessageTableModel messageTableModel) {
// 1. 获取索引
PersistedList<String> dataIndex = persistence.extensionData().getStringList("data"); // 数据索引
PersistedList<String> messageIndex = persistence.extensionData().getStringList("message"); // 消息索引
// 2. 从索引获取数据
loadHaEData(dataIndex);
loadMessageData(messageIndex, messageTableModel);
}
private void loadHaEData(PersistedList<String> dataIndex) {
if (dataIndex != null && !dataIndex.isEmpty()) {
dataIndex.parallelStream().forEach(index -> {
PersistedObject dataObj = persistence.extensionData().getChildObject(index);
dataObj.stringListKeys().forEach(dataKey -> {
RegularMatcher.putDataToGlobalMap(api, index, dataKey, dataObj.getStringList(dataKey).stream().toList(), false);
});
});
}
}
private void loadMessageData(PersistedList<String> messageIndex, MessageTableModel messageTableModel) {
if (messageIndex != null && !messageIndex.isEmpty()) {
messageIndex.parallelStream().forEach(index -> {
PersistedObject dataObj = persistence.extensionData().getChildObject(index);
HttpRequestResponse messageInfo = dataObj.getHttpRequestResponse("messageInfo");
String comment = dataObj.getString("comment");
String color = dataObj.getString("color");
HttpRequest request = messageInfo.request();
HttpResponse response = messageInfo.response();
String method = request.method();
String url = request.url();
String status = String.valueOf(response.statusCode());
String length = String.valueOf(response.toByteArray().length());
messageTableModel.add(messageInfo, url, method, status, length, comment, color, false);
});
}
}
}

View File

@@ -0,0 +1,44 @@
package hae.utils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
public class UIEnhancer {
public static void setTextFieldPlaceholder(JTextField textField, String placeholderText) {
// 使用客户端属性来存储占位符文本和占位符状态
textField.putClientProperty("placeholderText", placeholderText);
textField.putClientProperty("isPlaceholder", true);
// 设置占位符文本和颜色
setPlaceholderText(textField);
textField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
// 当获得焦点且文本是占位符时,清除文本并更改颜色
if ((boolean) textField.getClientProperty("isPlaceholder")) {
textField.setText("");
textField.setForeground(Color.BLACK);
textField.putClientProperty("isPlaceholder", false);
}
}
@Override
public void focusLost(FocusEvent e) {
// 当失去焦点且文本为空时,设置占位符文本和颜色
if (textField.getText().isEmpty()) {
setPlaceholderText(textField);
}
}
});
}
private static void setPlaceholderText(JTextField textField) {
String placeholderText = (String) textField.getClientProperty("placeholderText");
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
textField.putClientProperty("isPlaceholder", true);
}
}

View File

@@ -0,0 +1,58 @@
package hae.utils.http;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import hae.utils.ConfigLoader;
import hae.utils.string.StringProcessor;
import java.util.Arrays;
import java.util.List;
public class HttpUtils {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public HttpUtils(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
public boolean verifyHttpRequestResponse(HttpRequestResponse requestResponse, String toolType) {
HttpRequest request = requestResponse.request();
HttpResponse response = requestResponse.response();
boolean retStatus = false;
try {
String host = StringProcessor.getHostByUrl(request.url());
String[] hostList = configLoader.getBlockHost().split("\\|");
boolean isBlockHost = isBlockHost(hostList, host);
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
boolean isExcludeSuffix = suffixList.contains(request.fileExtension().toLowerCase());
boolean isToolScope = !configLoader.getScope().contains(toolType);
List<String> statusList = Arrays.asList(configLoader.getExcludeStatus().split("\\|"));
boolean isExcludeStatus = statusList.contains(String.valueOf(response.statusCode()));
retStatus = isExcludeSuffix || isBlockHost || isToolScope || isExcludeStatus;
} catch (Exception ignored) {
}
return retStatus;
}
private boolean isBlockHost(String[] hostList, String host) {
boolean isBlockHost = false;
for (String hostName : hostList) {
String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", "");
if (hostName.contains("*.") && StringProcessor.matchFromEnd(host, cleanedHost)) {
isBlockHost = true;
} else if (host.equals(hostName) || hostName.equals("*")) {
isBlockHost = true;
}
}
return isBlockHost;
}
}

View File

@@ -0,0 +1,110 @@
package hae.utils.rule;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import hae.utils.ConfigLoader;
import hae.utils.rule.model.Group;
import hae.utils.rule.model.Info;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;
public class RuleProcessor {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public RuleProcessor(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
public void rulesFormatAndSave() {
DumperOptions dop = new DumperOptions();
dop.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Representer representer = new Representer(dop);
Yaml yaml = new Yaml(representer, dop);
List<Group> ruleGroupList = new ArrayList<>();
Config.globalRules.forEach((k, v) -> {
List<Info> ruleList = Arrays.stream(v)
.map(objects -> new Info(
(boolean) objects[0],
(String) objects[1],
(String) objects[2],
(String) objects[3],
(String) objects[4],
(String) objects[5],
(String) objects[6],
(String) objects[7],
(boolean) objects[8]))
.collect(Collectors.toList());
ruleGroupList.add(new Group(k, ruleList));
});
List<Map<String, Object>> outputGroupsMap = ruleGroupList.stream()
.map(Group::getFields)
.collect(Collectors.toList());
Map<String, Object> outputMap = new LinkedHashMap<>();
outputMap.put("rules", outputGroupsMap);
File f = new File(configLoader.getRulesFilePath());
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(f.toPath()), StandardCharsets.UTF_8)) {
yaml.dump(outputMap, ws);
} catch (Exception ignored) {
}
}
public void changeRule(Vector data, int select, String type) {
Config.globalRules.get(type)[select] = data.toArray();
this.rulesFormatAndSave();
}
public void addRule(Vector data, String type) {
ArrayList<Object[]> x = new ArrayList<>(Arrays.asList(Config.globalRules.get(type)));
x.add(data.toArray());
Config.globalRules.put(type, x.toArray(new Object[x.size()][]));
this.rulesFormatAndSave();
}
public void removeRule(int select, String type) {
ArrayList<Object[]> x = new ArrayList<>(Arrays.asList(Config.globalRules.get(type)));
x.remove(select);
Config.globalRules.put(type, x.toArray(new Object[x.size()][]));
this.rulesFormatAndSave();
}
public void renameRuleGroup(String oldName, String newName) {
Config.globalRules.put(newName, Config.globalRules.remove(oldName));
this.rulesFormatAndSave();
}
public void deleteRuleGroup(String Rules) {
Config.globalRules.remove(Rules);
this.rulesFormatAndSave();
}
public String newRule() {
int i = 0;
String name = "New ";
while (Config.globalRules.containsKey(name + i)) {
i++;
}
Config.globalRules.put(name + i, Config.ruleTemplate);
this.rulesFormatAndSave();
return name + i;
}
}

View File

@@ -0,0 +1,29 @@
package hae.utils.rule.model;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Group {
private Map<String, Object> fields;
public Group(String groupName, List<Info> rules) {
List<Map<String, Object>> ruleList = new ArrayList<>();
for (Info rule : rules) {
ruleList.add(rule.getFields());
}
fields = new LinkedHashMap<>();
fields.put("group", groupName);
fields.put("rule", ruleList);
}
public Map<String, Object> getFields() {
return fields;
}
public void loadFields(Map<String, Object> fields) {
this.fields = fields;
}
}

View File

@@ -0,0 +1,29 @@
package hae.utils.rule.model;
import java.util.LinkedHashMap;
import java.util.Map;
public class Info {
private Map<String, Object> fields;
public Info(boolean loaded, String name, String f_regex, String s_regex, String format, String color, String scope, String engine, boolean sensitive) {
fields = new LinkedHashMap<>();
fields.put("name", name);
fields.put("loaded", loaded);
fields.put("f_regex", f_regex);
fields.put("s_regex", s_regex);
fields.put("format", format);
fields.put("color", color);
fields.put("scope", scope);
fields.put("engine", engine);
fields.put("sensitive", sensitive);
}
public Map<String, Object> getFields() {
return fields;
}
public void loadFields(Map<String, Object> fields) {
this.fields = fields;
}
}

View File

@@ -0,0 +1,28 @@
package hae.utils.string;
import java.security.MessageDigest;
public class HashCalculator {
public static String calculateHash(byte[] bytes) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
byte[] hashBytes = digest.digest(bytes);
return bytesToHex(hashBytes);
} catch (Exception ignored) {
return "";
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}

View File

@@ -0,0 +1,145 @@
package hae.utils.string;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.http.HttpService;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class StringProcessor {
public static String replaceFirstOccurrence(String original, String find, String replace) {
int index = original.indexOf(find);
if (index != -1) {
return original.substring(0, index) + replace + original.substring(index + find.length());
}
return original;
}
public static boolean matchFromEnd(String input, String pattern) {
int inputLength = input.length();
int patternLength = pattern.length();
int inputIndex = inputLength - 1;
int patternIndex = patternLength - 1;
while (inputIndex >= 0 && patternIndex >= 0) {
if (input.charAt(inputIndex) != pattern.charAt(patternIndex)) {
return false;
}
inputIndex--;
patternIndex--;
}
// 如果patternIndex为-1表示pattern字符串已经完全匹配
return patternIndex == -1;
}
public static String extractHostname(String hostWithPort) {
if (hostWithPort == null || hostWithPort.isEmpty()) {
return "";
}
int colonIndex = hostWithPort.indexOf(":");
if (colonIndex != -1) {
return hostWithPort.substring(0, colonIndex);
} else {
return hostWithPort;
}
}
public static boolean matchesHostPattern(String host, String selectedHost) {
String hostname = StringProcessor.extractHostname(host);
String hostPattern = selectedHost.replace("*.", "");
boolean matchesDirectly = selectedHost.equals("*") || host.equals(selectedHost);
boolean matchesPattern = !host.contains("*") &&
(hostPattern.equals(selectedHost) ?
StringProcessor.matchFromEnd(host, hostPattern) :
StringProcessor.matchFromEnd(hostname, hostPattern));
return matchesDirectly || matchesPattern;
}
public static HttpRequestResponse createHttpRequestResponse(String url, byte[] request, byte[] response) {
HttpService httpService = HttpService.httpService(url);
HttpRequest httpRequest = HttpRequest.httpRequest(httpService, ByteArray.byteArray(request));
HttpResponse httpResponse = HttpResponse.httpResponse(ByteArray.byteArray(response));
return HttpRequestResponse.httpRequestResponse(httpRequest, httpResponse);
}
public static String getCurrentTime() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
LocalDateTime now = LocalDateTime.now();
return now.format(formatter);
}
public static String getRandomUUID() {
UUID uuid = UUID.randomUUID();
return uuid.toString();
}
public static String mergeComment(String comment) {
if (!comment.contains(",")) {
return comment;
}
Map<String, Integer> itemCounts = getStringIntegerMap(comment);
StringBuilder mergedItems = new StringBuilder();
for (Map.Entry<String, Integer> entry : itemCounts.entrySet()) {
String itemName = entry.getKey();
int count = entry.getValue();
if (count != 0) {
mergedItems.append(itemName).append(" (").append(count).append("), ");
}
}
return mergedItems.substring(0, mergedItems.length() - 2);
}
public static String getHostByUrl(String url) {
String host = "";
try {
URL u = new URL(url);
int port = u.getPort();
if (port == -1) {
host = u.getHost();
} else {
host = String.format("%s:%s", u.getHost(), port);
}
} catch (Exception ignored) {
}
return host;
}
public static boolean matchHostIsIp(String host) {
return host.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b");
}
private static Map<String, Integer> getStringIntegerMap(String comment) {
Map<String, Integer> itemCounts = new HashMap<>();
String[] items = comment.split(", ");
for (String item : items) {
if (item.contains("(") && item.contains(")")) {
int openParenIndex = item.lastIndexOf("(");
int closeParenIndex = item.lastIndexOf(")");
String itemName = item.substring(0, openParenIndex).trim();
int count = Integer.parseInt(item.substring(openParenIndex + 1, closeParenIndex).trim());
itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count);
} else {
itemCounts.put(item, 0);
}
}
return itemCounts;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,303 @@
rules:
- group: Fingerprint
rule:
- name: Shiro
loaded: true
f_regex: (=deleteMe|rememberMe=)
s_regex: ''
format: '{0}'
color: green
scope: any header
engine: dfa
sensitive: true
- name: JSON Web Token
loaded: true
f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
s_regex: ''
format: '{0}'
color: green
scope: any
engine: nfa
sensitive: true
- name: Swagger UI
loaded: true
f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
s_regex: ''
format: '{0}'
color: red
scope: response body
engine: dfa
sensitive: false
- name: Ueditor
loaded: true
f_regex: (ueditor\.(config|all)\.js)
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: dfa
sensitive: false
- name: Druid
loaded: true
f_regex: (Druid Stat Index)
s_regex: ''
format: '{0}'
color: orange
scope: response body
engine: dfa
sensitive: false
- group: Maybe Vulnerability
rule:
- name: Java Deserialization
loaded: true
f_regex: (javax\.faces\.ViewState)
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: dfa
sensitive: false
- name: Debug Logic Parameters
loaded: true
f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=))
s_regex: ''
format: '{0}'
color: cyan
scope: request
engine: dfa
sensitive: false
- name: URL As A Value
loaded: true
f_regex: (=(https?)(://|%3a%2f%2f))
s_regex: ''
format: '{0}'
color: cyan
scope: any
engine: nfa
sensitive: false
- name: Upload Form
loaded: true
f_regex: (type\=\"file\")
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: dfa
sensitive: false
- name: DoS Paramters
loaded: true
f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
s_regex: ''
format: '{0}'
color: cyan
scope: request
engine: dfa
sensitive: false
- group: Basic Information
rule:
- name: Email
loaded: true
f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5}))
s_regex: ''
format: '{0}'
color: yellow
scope: response
engine: nfa
sensitive: false
- name: Chinese IDCard
loaded: true
f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]'
s_regex: ''
format: '{0}'
color: orange
scope: response body
engine: nfa
sensitive: true
- name: Chinese Mobile Number
loaded: true
f_regex: '[^\w]((?:(?:\+|0{0,2})86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]'
s_regex: ''
format: '{0}'
color: orange
scope: response body
engine: nfa
sensitive: false
- name: Internal IP Address
loaded: true
f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))'
s_regex: ''
format: '{0}'
color: cyan
scope: response
engine: nfa
sensitive: true
- name: MAC Address
loaded: true
f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}))
s_regex: ''
format: '{0}'
color: green
scope: response
engine: nfa
sensitive: true
- group: Sensitive Information
rule:
- name: Cloud Key
loaded: true
f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20}))
s_regex: ''
format: '{0}'
color: yellow
scope: any
engine: nfa
sensitive: false
- name: Windows File/Dir Path
loaded: true
f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)'
s_regex: ''
format: '{0}'
color: green
scope: response
engine: nfa
sensitive: true
- name: Password Field
loaded: true
f_regex: ((|\\)(|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()(
|)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: nfa
sensitive: false
- name: Username Field
loaded: true
f_regex: ((|\\)(|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()(
|)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: nfa
sensitive: false
- name: WeCom Key
loaded: true
f_regex: ((corp)(id|secret))
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: dfa
sensitive: false
- name: JDBC Connection
loaded: true
f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+)
s_regex: ''
format: '{0}'
color: yellow
scope: any
engine: nfa
sensitive: false
- name: Authorization Header
loaded: true
f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: nfa
sensitive: false
- name: Sensitive Field
loaded: true
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin)|(ticket))([\w]{0,10})('|")?(\])?(
|)(:|=|\)\.val\()( |)('|")([^'"]+?)('|")(|,|\)))
s_regex: ''
format: '{0}'
color: yellow
scope: response
engine: nfa
sensitive: false
- name: Mobile Number Field
loaded: true
f_regex: ((|\\)(|'|")(|[\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()(
|)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: nfa
sensitive: false
- group: Other
rule:
- name: Linkfinder
loaded: true
f_regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:\w)(?:[\?|#][^"|']{0,}|)))(?:"|')
s_regex: ''
format: '{0}'
color: gray
scope: response body
engine: nfa
sensitive: true
- name: Source Map
loaded: true
f_regex: (\.js\.map)
s_regex: ''
format: '{0}'
color: pink
scope: response body
engine: dfa
sensitive: false
- name: Create Script
loaded: true
f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js")
s_regex: '"?([\w].*?)"?:"(.*?)"'
format: '{0}.{1}'
color: green
scope: response body
engine: nfa
sensitive: false
- name: URL Schemes
loaded: true
f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]))
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: nfa
sensitive: false
- name: Router Push
loaded: true
f_regex: (\$router\.push)
s_regex: ''
format: '{0}'
color: magenta
scope: response body
engine: dfa
sensitive: false
- name: All URL
loaded: true
f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|])
s_regex: ''
format: '{0}'
color: gray
scope: response body
engine: nfa
sensitive: true
- name: Request URI
loaded: true
f_regex: ' ((?!.*\.js(\?.*)?$)(.*?[^.js$])) '
s_regex: ''
format: '{0}'
color: gray
scope: request line
engine: nfa
sensitive: false
- name: 302 Location
loaded: true
f_regex: 'Location: (.*?)\n'
s_regex: ''
format: '{0}'
color: gray
scope: response header
engine: nfa
sensitive: false